We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
动态运行js字符串的方案有哪些?
这些方案在运行环境,代码来源,访问权限,执行时机上有什么差异?
为什么我们需要动态执行js字符串?
为什么大家都不建议使用动态运行js字符串的方案?
如果必须要选择一种方案 我们应该推荐哪种方案?
<script> var outerName = 'outer' function test() { let localName = 'local' // 可以访问局部变量 const jsString = "var innerName = 'inner'; " + " console.log('这是一段动态执行的js字符串', outerName, innerName, localName) "; eval(jsString) console.log('外部打印 innerName:', innerName) } test(); </script>
eval()函数会在其所在的函数作用域去执行代码
<script type="module"> // 定义要执行的 JavaScript 字符串 const jsString = "var innerName = 'hahha'; " + " console.log('这是一段动态执行的js字符串', innerName) "; // 创建一个新的 <script> 元素 const script = document.createElement('script'); // 将 type 属性设置为 text/javascript,告诉浏览器这是 JavaScript 代码 script.type = 'text/javascript'; // 将 JavaScript 字符串设置为 script 元素的 textContent script.textContent = jsString; // 将 script 元素添加到页面的 head 或 body 中 document.head.appendChild(script); console.log('外部打印 innerName', innerName) </script>
我们创建了一个新的 <script> 元素,将其 type 属性设置为 'text/javascript',并将我们的 JavaScript 字符串赋值给 textContent 属性。最后,我们将这个 <script> 元素添加到文档的 <head> 中,这种方法会立即执行 <script> 元素中的代码,因此它可以用于按需执行或延迟执行 JavaScript 代码。然而,这种方法也意味着一旦 <script> 元素被添加到 DOM 中,代码就会被执行,这可能不适用于所有场景。
<script>
type
'text/javascript'
textContent
<head>
webpack动态加载的原理就是这个方案....
var outerName = 'outer' function test() { let localName = 'local' // 不能访问局部变量 const jsString = "function sayHello() {" + " var innerName = 'inner'; " + " console.log('这是一段动态执行的js字符串', outerName, innerName ) " + "}" + "sayHello();"; // 使用new Function()来创建一个新的函数 var fun = new Function(jsString); // 调用这个新创建的函数 var result = fun(); } test();
需要注意的是 通过new Function() 创建的函数不会继承其创建时的局部作用域。 这种特殊的作用域行为,与普通 JavaScript 函数的作用域闭包机制不同。
<script> var outerName = 'outer' function test() { let localName = 'local' // 不能访问局部变量 const jsString = "var innerName = 'inner'; " + " console.log('这是一段动态执行的js字符串', outerName, innerName) "; setTimeout(jsString, 2 * 1000) setTimeout(() => { console.log('外部打印 innerName:', innerName) }, 4 * 1000) } test(); </script>
向 setTimeout 函数提供一个字符串作为参数时,该函数会把这个字符串当作脚本来解析和执行。这类似于使用 new Function(),也就是说 setTimeout 也是全局作用域运行 不会访问创建它的局部作用域。
字符串内部定义的遍历 外部依然可以访问 会存在污染全局变量的问题。
<script> // 创建一个可执行的 JavaScript 代码字符串 var code = `self.onmessage = function(e) { var result = e.data[0] + e.data[1]; self.postMessage(result); self.close(); }`; // 创建一个 Blob 对象来作为 worker 的源 var blob = new Blob([code], { type: 'application/javascript' }); // 根据 Blob 对象创建一个 object URL,并创建 worker var worker = new Worker(URL.createObjectURL(blob)); // 设置 worker 的消息回调 worker.onmessage = function (e) { console.log('Result: ' + e.data); }; // 向 worker 发送数据 worker.postMessage([1, 2]); </script>
JavaScript 的 with 语句本身并不是用来执行 JavaScript 字符串的。它主要被用来修改一个特定的执行环境的作用域。
然而,我们可以将 with 语句与 eval 函数结合使用来在特定的对象环境中执行 JavaScript 字符串。
<script> var someObject = { someProperty: 'Hello, world!' }; with (someObject) { eval('console.log(someProperty)'); } </script>
在这个例子中,with 语句更改了 eval 中代码的作用域,使其可以直接访问 someObject 的属性。因此,当 eval 执行字符串 'console.log(someProperty)' 时,它将输出 'Hello, world!'。
动态运行字符串的方案都会有安全风险,这些风险主要有以下两个因素引起:
性能问题:
解析和编译:与直接调用事先定义好的函数相比,动态执行字符串代码需要额外的解析(将字符串形式的代码解析成可执行的代码)和编译(编译成机器代码)步骤。这些额外的步骤需要时间,尤其是在必须频繁执行字符串代码的场合,性能损耗将会更加明显。
优化难度:现代JavaScript引擎通常对常规的JavaScript代码进行优化,以提高执行效率。然而,对于动态生成且运行的代码,在执行前是不可见的,引擎无法提前知道代码的结构,难以进行相同水平的优化。 另外,因为每次执行动态脚本都是一次全新的脚本执行,这使得引擎难以利用以往的代码信息进行优化。
垃圾回收:动态执行的代码可能会频繁创建新的对象和作用域,依赖于这些对象和作用域的动态代码则可以导致JavaScript引擎更频繁地进行垃圾回收,而垃圾回收是影响应用性能的一个关键因素。
如果有场景必须要使用动态运行js字符串的情况下,应优先考虑动态创建 script 标签, new Function() 和 Web Worker。
new Function()
动态创建 script 标签:这种方式相对安全,可以用于加载外部的 JavaScript 代码,适用于需要动态加载并立即执行脚本的场景。但要尽量避免使用外部不可信的源。
new Function() 提供了较好的安全性和灵活性,适用于需要动态执行但不需要访问外部局部变量的场景。
Web Worker 适合于计算密集型任务,可以避免阻塞 UI 线程,适合于需要长时间运行的后台任务
The text was updated successfully, but these errors were encountered:
No branches or pull requests
先有问题再有答案
动态运行js字符串的方案有哪些?
这些方案在运行环境,代码来源,访问权限,执行时机上有什么差异?
为什么我们需要动态执行js字符串?
为什么大家都不建议使用动态运行js字符串的方案?
如果必须要选择一种方案 我们应该推荐哪种方案?
eval 函数(不建议)
eval()函数会在其所在的函数作用域去执行代码
,而不是创建一个新的作用域。所以,如果eval()函数在函数内部调用时可以访问函数的局部变量,而且还会污染全局对象。使用script元素
示例
我们创建了一个新的
<script>
元素,将其type
属性设置为'text/javascript'
,并将我们的 JavaScript 字符串赋值给textContent
属性。最后,我们将这个<script>
元素添加到文档的<head>
中,这种方法会立即执行<script>
元素中的代码,因此它可以用于按需执行或延迟执行 JavaScript 代码。然而,这种方法也意味着一旦<script>
元素被添加到 DOM 中,代码就会被执行,这可能不适用于所有场景。webpack动态加载的原理就是这个方案....
使用 new Function() 构造函数
需要注意的是 通过new Function() 创建的函数不会继承其创建时的局部作用域。
这种特殊的作用域行为,与普通 JavaScript 函数的作用域闭包机制不同。
优点:
缺点
使用 setTimeout 或 setInterval
向 setTimeout 函数提供一个字符串作为参数时,该函数会把这个字符串当作脚本来解析和执行。这类似于使用 new Function(),也就是说 setTimeout 也是全局作用域运行 不会访问创建它的局部作用域。
字符串内部定义的遍历 外部依然可以访问 会存在污染全局变量的问题。
使用 Web Workers
使用 with 语句(不推荐)
JavaScript 的 with 语句本身并不是用来执行 JavaScript 字符串的。它主要被用来修改一个特定的执行环境的作用域。
然而,我们可以将 with 语句与 eval 函数结合使用来在特定的对象环境中执行 JavaScript 字符串。
在这个例子中,with 语句更改了 eval 中代码的作用域,使其可以直接访问 someObject 的属性。因此,当 eval 执行字符串 'console.log(someProperty)' 时,它将输出 'Hello, world!'。
总结
优点
缺点:
动态运行字符串的方案都会有安全风险,这些风险主要有以下两个因素引起:
性能问题:
解析和编译:与直接调用事先定义好的函数相比,动态执行字符串代码需要额外的解析(将字符串形式的代码解析成可执行的代码)和编译(编译成机器代码)步骤。这些额外的步骤需要时间,尤其是在必须频繁执行字符串代码的场合,性能损耗将会更加明显。
优化难度:现代JavaScript引擎通常对常规的JavaScript代码进行优化,以提高执行效率。然而,对于动态生成且运行的代码,在执行前是不可见的,引擎无法提前知道代码的结构,难以进行相同水平的优化。 另外,因为每次执行动态脚本都是一次全新的脚本执行,这使得引擎难以利用以往的代码信息进行优化。
垃圾回收:动态执行的代码可能会频繁创建新的对象和作用域,依赖于这些对象和作用域的动态代码则可以导致JavaScript引擎更频繁地进行垃圾回收,而垃圾回收是影响应用性能的一个关键因素。
建议
如果有场景必须要使用动态运行js字符串的情况下,应优先考虑动态创建 script 标签,
new Function()
和 Web Worker。动态创建 script 标签:这种方式相对安全,可以用于加载外部的 JavaScript 代码,适用于需要动态加载并立即执行脚本的场景。但要尽量避免使用外部不可信的源。
new Function()
提供了较好的安全性和灵活性,适用于需要动态执行但不需要访问外部局部变量的场景。Web Worker 适合于计算密集型任务,可以避免阻塞 UI 线程,适合于需要长时间运行的后台任务
The text was updated successfully, but these errors were encountered: