【技巧】动态执行js脚本

【技巧】动态执行js脚本

我们写的 js 代码,主要执行在浏览器环境和 node 环境,也叫宿主环境。宿主环境通过加载机制获取到我们的代码,然后使用 js 引擎解释执行。这是正常的 js 代码执行流程。有些场景下,js 代码是通过程序动态生成的,此时我们已经运行在 js 引擎内部,没有宿主环境帮我们执行代码,就需要 js 引擎提供的动态执行代码的能力。下面我总结了几种动态执行 js 代码的方法。

js 动态执行代码的几种方法

new Function

Function 构造函数创建一个函数对象,这个函数对象和使用函数声明和函数表达式创建的一样,区别是函数的解析时机不同。Function 构造函数是在执行时解析,后者是在脚本加载时解析。

Function 创建的函数有自己的作用域,其父作用域是全局作用域,只能访问全局变量和自己的局部变量,不能访问函数被创建时所在的作用域。需要注意在 node 环境和 esm 环境存在模块作用域,模块作用域不是全局作用域。Function 创建的函数也不能访问模块作用域。

var a = -100;(function() {var a = 1;// 函数执行时的父作用域时全局作用域(new Function('console.log(a)'))(); // -100// 内部的 this 是 windowvar nfunc = new Function('return this')console.log(nfunc()) // Window// 作为对象的方法时, this 是当前对象var obj = { nfunc: nfunc }console.log(obj.nfunc()) // {nfunc: ƒ}
})();

eval

eval 没有自己的作用域,而是使用执行时所在的作用域,在 eval 中初始化语会将变量加入到当前作用域。由于变量是在运行时动态添加的,导致 v8 引擎不能做出正确的判断,只能放弃优化策略。在严格模式下,eval 有自己的作用域,这样就不会污染当前作用域。

var a = 0;
var b = 1;(function() {// eval 没有自己的作用域,使用当前作用域。var a = 100;eval('console.log(a)'); // 100// 初始化语句会添加变量到当前作用域上,也就是会污染当前作用域。这是 v8 引擎没法优化这段代码的原因,也是性能差的原因。eval('var b = 20');console.log(b); // 20})();(function() {'use strict'// 严格模式下,eval 有自己的作用域,父作用域是当前作用域。var a = 100;eval('console.log(a)'); // 100 当前作用域上的 a// 严格模式下,eval 有自己的作用域,初始化语句将变量添加到自己的作用域内。执行完后当前作用域被销毁eval('var b = 20');console.log(b); // 1 全局作用域上的 b// 返回 eval 代码段产生的闭包var innerb = eval('var b = 20; (function () { return b })')();console.log('innerb', innerb); // innerb 20})();

值得注意的是,eval 如果不使用 direct call 的方式调用,其使用的作用域将会变为全局作用域。

var a = 0;(function() {var a = 100;var fn = eval;// 非 direct call 的调用方式fn('console.log(a)'); // 0
})();

setTimeout

setTimeout 用来设置定时器,其第一个参数可以传入函数,也可以传入代码片段。传入函数时,函数的作用域是正常的函数作用域。传入代码片段时,没有自己的作用域,其执行时作用域是全局作用域。

var a = -100;(function () {var a = 0;// setTimeout 执行的代码段,没有自己的作用域,运行在全局作用域中var dynameicCode = "console.log(a)";setTimeout(dynameicCode, 10); // -100// setTimeout 执行的代码段,初始化语句会添加变量到 window 上var dynameicCode = "var b = 200;";setTimeout(dynameicCode, 10);setTimeout(function() {console.log('window.b', window.b); // window.b 200}, 20);})();

script.textContent

动态创建 script 节点,也是一种动态执行语句的方式。其创建的 script 和普通 script 没有区别,代码的作用域是全局作用域。需要注意 script 应该使用 document.createElement('script') 创建并插入到文档中。使用 innerHTML 插入 script 的方式,脚本不会执行。

(function() {var a = 1;var s = document.createElement('script');s.textContent = "console.log(a)";document.documentElement.append(s);
})();

οnclick="xxx"

html 元素的 onclick 属性也支持设置 js 代码,这种特性被称为 Inline event handlers。这种方式执行的代码存在自己的作用域,父作用域是全局作用域。也就是说初始化语句不会污染全局作用域。

<script>var a = -100;var b = 2;
</script><!-- 点击按钮输出 -100 200 -->
<button onclick="var b = 200; console.log(a, b);">click me</button>
<!-- 执行成功后,在控制台检查,全局作用域内并没有变量 b -->

总结

动态执行代码普遍存在两个缺点,一是安全性问题,传递的函数体字符串如果包含非法代码也会被执行。二是执行性能,动态执行时会解析代码,存在一定的时间消耗。eval 还会影响到 js 引擎的优化过程,导致效率降低非常多。eval is evil 说的就是使用 eval 可能导致很严重的问题。从上面几种方法可以看到,理解 js 动态执行代码时的作用域是关键。一是是否存在自己的作用域,二是其父作用域是当前作用域还是全局作用域。只要记住这两个问题的答案,在使用时就不会出现大问题。

在我的工作经历中,使用动态执行代码的次数屈指可数,倒是前端框架使用这个比较普遍,比如 webpack 和 vue。系统的了解一下这些知识,可以方便自己看框架的源码,也能加深对语言和其执行环境的认知。以我的能力,想要说清楚动态执行代码的细节比较困难。这里涉及到 js 引擎的底层知识,比如词法语法分析、语法树构建、作用域和作用域链。我希望自己能正确使用这些知识技能,能说清楚它们的特性和用途也就足够了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

供应链管理系统有哪些?

1万字干货分享&#xff0c;国内外 20款 供应链管理软件都给你讲的明明白白。如果你还不知道怎么选择&#xff0c;一定要翻到第三大段&#xff0c;这里我将会通过8年的软件产品选型经验告诉你&#xff0c;怎么样才能快速选到适合自己的软件工具。 &#xff08;为防后续找不到&a…

IOS与Android APP开发的差异性

iPhone和 Android是全球最流行的两种移动平台&#xff0c;有许多不同的开发者开发了应用程序&#xff0c;并将它们发布到市场上。虽然大多数开发者都使用了这两个平台&#xff0c;但您仍然需要了解它们的差异。 虽然 iOS和 Android两个平台都是基于 Linux&#xff0c;但它们却…

(1)ADS-B接收机

文章目录 前言 1.1 所需硬件 1.2 连接到自动驾驶仪 1.3 设置 1.4 ADSB输出配置 1.5 启用载人飞行器避障功能 1.6 飞行器数据库 1.7 开发者信息包括模拟 前言 本文介绍了如何安装和配置 ADS-B 模块&#xff0c;以便你的飞机能够知道附近的其他飞机和空中交通管制&#x…

MyBatis 的架构

MyBatis 的架构 MyBatis 是一个基于 Java 的持久层框架&#xff0c;可以将 SQL 语句和 Java 代码进行分离&#xff0c;通过 XML 或注解的方式配置 SQL 语句并执行&#xff0c;从而实现数据访问的功能。MyBatis 的架构包括以下几个部分&#xff1a; SqlSessionFactory&#xff…

学习opencv.js之基本使用方法(读取,显示,灰度化,边缘检测,特征值点检测)

opencv.js是什么 OpenCV.js 是 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;的 JavaScript 版本。OpenCV 是一个广泛使用的计算机视觉和图像处理库&#xff0c;提供了一系列功能强大的算法和工具&#xff0c;用于处理图像、视频、特征提取、对象识别等…

Storage、正则表达式

1 LocalStorage 2 SessionStorage 3 正则表达式的使用 4 正则表达式常见规则 5 正则练习-歌词解析 6 正则练习-日期格式化 Storage-Storage的基本操作 // storage基本使用// 1.token的操作let token localStorage.getItem("token")if (!token) {console.log(&q…

python开发项目基于语音识别的智能垃圾分类系统的设计与实现

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…

平安养老险党委书记、董事长甘为民:助推养老保障事业高质量发展

随着人口老龄化趋势加剧&#xff0c;中国养老金融市场呈现出巨大的潜力&#xff0c;逐步迈入养老新时代。 平安养老险党委书记、董事长甘为民提出&#xff0c;养老保险公司要想在市场中展现出独特的市场影响力&#xff0c;需要聚焦养老主业&#xff0c;发挥自身专业特色&#…

详解c++---特殊类设计

目录标题 设计一个不能被拷贝的类设计一个只能从堆上创建对象的类设计一个只能在栈上创建对象的类设计一个无法被继承的类什么是单例模式饿汉模式饿汉模式的缺点懒汉模式懒汉模式的优点懒汉模式的缺点特殊的懒汉 设计一个不能被拷贝的类 拷贝只会放生在两个场景中&#xff1a;…

(Linux)基础命令

帮助文档 公式功能man 命令名访问Linux手册页命令名 – helpinfo 命令名查看命令的功能&#xff0c;来源&#xff0c;选项等whatis 命令名 ls 公式功能ls [选项][目录或文件]对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及…

自学网络安全究竟该从何学起?

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入行…

webrtc源码阅读之视频RTP接收JitterBuffer

在音视频通信中&#xff0c;网络抖动和延迟是常见的问题&#xff0c;会导致音视频质量下降和用户体验不佳。为了解决这些问题&#xff0c;WebRTC引入了Jitter Buffer&#xff08;抖动缓冲区&#xff09;这一重要组件。Jitter Buffer是一个缓冲区&#xff0c;用于接收和处理网络…

未来驾驶新标配;CarLuncher车载开发塑造智能娱乐导航系统

车载开发在新能源汽车的快速市场占有率增长背景下具有广阔的前景。随着环境保护意识的增强和政府对清洁能源的支持&#xff0c;新能源汽车&#xff08;如电动汽车&#xff09;在全球范围内呈现出快速增长的趋势。这种趋势为车载开发提供了许多机会和潜在市场。 新能源汽车的普…

基于SpringBoot+微信小程序的医院预约叫号小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 该项目是基于uniappWe…

【C++初阶】构造函数和析构函数

文章目录 一、类的六个默认成员函数二、构造函数三、析构函数 一、类的六个默认成员函数 &#x1f4d6;默认成员函数 用户没有显式实现&#xff0c;编译器会自动生成的成员函数&#xff0c;称为默认成员函数。 构造函数&#xff1a;完成对象的初始化工作。析构函数&#xff…

【http-server】http-server的安装、前端使用http-server启动本地dist文件服务:

文章目录 一、http-server 简介:二、安装node.js:[https://nodejs.org/en](https://nodejs.org/en)三、安装http-server:[https://www.npmjs.com/package/http-server](https://www.npmjs.com/package/http-server)四、开启服务&#xff1a;五、http-server参数&#xff1a;【1…

docker FTP服务器搭建与排坑

在使用FTP时&#xff0c;如果客户端机器和FTP服务器双方之间的所有端口都是开放的&#xff0c;那连接不存在问题。如果客户端与服务器之间有防火墙&#xff0c;如果没配置好防火策略和采用合适的连接模式&#xff0c;会导致登录成功&#xff0c;但无法List列表的问题。要避免出…

rust的函数和注释

1.函数 声明函数使用 fn 关键字rust使用snake case 命名规范&#xff0c;即所有的字母都是小写的&#xff0c;单词之间使用下划线分开 fn main(){println!("hello world");another_function(); fn another_function(){println!("Another function"); }函…

Vscode配置grpc+c#+proto

首先是环境配置&#xff0c;用的dotnet5.0的sdk&#xff0c;所以Vscode的C#插件版本要选择1.24&#xff0c;然后需要配置C# Snippets、NuGget Package Manager、vscode-proto3、vscode-solution-extension&#xff08;可选&#xff09;。 以vscode-solution-extension为例新建A…

学习day47

Vue 现在开始学Vue了&#xff0c;问了同学&#xff0c;他说这个东西的内容很多。然后就跟着尚硅谷来学Vue了 用的是visual studio code&#xff0c;所以又下了一个visual。 首先时下载Vue&#xff0c;我再Vue3的官网是没有看到下载的&#xff0c;所以是跑到Vue2去下载的 将它…