翼度科技»论坛 编程开发 JavaScript 查看内容

[JS] 动态执行JS与修改词法作用域

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
相关可行的操作


  • eval: 同步执行,当前作用域;
  • setTimeout: 异步执行,全局作用域;
第1个参数可以传入函数对象,也可以传入字符串,即要执行的代码。

  • script: 同步执行,全局作用域;
创建script标签,并设置innerHTML为要执行的代码。

  • Function: 同步执行,全局作用域。
Function构造函数可以传入字符串,生成一个函数对象。
对词法作用域的影响

eval

eval可以通过动态地执行JS代码从而修改(欺骗)当前的词法作用域,观察如下代码:
  1. function foo(str, a){
  2.     eval(str);
  3.     console.log(a, b);
  4. }
  5. var b = 2;
  6. foo("var b=3;", 1); // 1, 3
复制代码
在函数中,执行eval(..)将var b=3;带入该词法作用域,导致console.log(..)中对b的右值引用找到的是3,而不会查询到外部的b=2。
在默认情况下,eval会对所处的词法作用域进行修改。
严格模式下,eval在运行时有其自己的词法作用域,即其动态执行的JS声明语句不会影响到eval语句所处的词法作用域。
  1. function foo(str){
  2.     "use strict";
  3.     eval(str);
  4.     console.log(a); // ReferenceError: a is not defined
  5. }
  6. foo("var a = 2;");
复制代码
with

除了eval另外可以修改词法作用域的语法是with关键字。
  1. function foo(obj){
  2.     with(obj){
  3.         a = 2;
  4.     }
  5. }
  6. var o1 = {a: 3};
  7. var o2 = {b: 3};
  8. foo(o1);
  9. console.log(o1.a);  // 2
  10. foo(o2);
  11. console.log(o2.a); // undefined
  12. console.log(a); // 2 (a被泄露到全局作用域了)
复制代码
with接受一个对象,并将这个对象处理为一个完全隔离的词法作用域,这个对象的属性会被处理为定义在这个作用域内的词法标识符。
所以当o1传递给with时,with声明的作用域是o1,包含了同o1.a对应的标识符a,这个左值引用可以找到目标,并成功完成赋值操作。
当o2传递给with时,with声明的词法作用域会包含同o2.b对应的标识符b,但是没有标识符a,此时赋值操作会进行LHS标识符查询,向外层作用域查找。
由于在o2的作用域、foo的函数作用域、全局作用域都没有找到标识符a,因此当a = 2执行时,会在全局作用域自动创建一个全局变量(如果是严格模式则不会)。
性能问题

JS引擎会在编译阶段进行性能优化,其中部分优化依赖于根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。
eval和with这种可能动态更改词法作用域的操作会导致JS引擎无法在词法分析阶段明确标识符的位置,因此所有优化可能都是无意义的,甚至JS引擎可能在读取代码中使用了eval和with,就放弃优化了。
因此,在开发中应该避免使用eval和with。
引用

[1] Scope and Closures, Kyle Simpson著(O'Reilly, 2014) 978-1-491-33558-8。

来源:https://www.cnblogs.com/feixianxing/p/18252203/dynamically-execute-javascript-and-scope
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具