思维导图作用域分类静态作用域(核心:写到哪里)不同的编程语言使用不同的作用域类型。js使用静态作用域,也就是词法作用域。另一种称为动态作用域,目前仍在某些编程语言中使用(例如Bash脚本、Perl中的某些模式等)。动态作用域(核心:wheretoexecute)动态作用域是运行时动态确定的,而不是写代码时静态确定的。函数foo(){console.log(a);//static:2dynamic:3}functionbar(){vara=3;foo();}vara=2;bar();在静态作用域中:foo函数将查找定义foo函数的作用域,并找到a=2。在动态作用域中:foo函数将查找执行foo函数的作用域,并找到a=3。词法作用域的词法阶段定义词法作用域是在词法阶段定义的作用域。源代码写在什么地方是确定的,一旦写完,范围就确定了(作弊词法语法除外)。瞄准镜泡泡类似于套娃玩具,这里借用《你不知道的js》的图。包含整个全局范围,只有一个标识符:foo。包含由foo创建的作用域,它具有三个标识符:a、bar和b。包含bar创建的作用域,它只有一个标识符:c。寻找阴影效应就是就近原理。最内层作用域可以找到变量,外层作用域的同名变量将找不到。换句话说,内部作用域中的变量将覆盖外部作用域中同名的变量。全局变量全局变量自动成为全局对象(例如浏览器中的window对象)的属性,因此不能直接通过全局对象的词法名称访问它们,而是通过对全局对象属性的引用间接访问varnum=0functionfoo(){varnum=1;控制台日志(数量);//1functionfxx(){letnum=2;控制台日志(数量);//2console.log(window.num);//0}fxx();}foo();一级标识符的词法作用域查找只查找一级标识符,如a、b、c。如果在代码中引用了foo.bar.baz,词法范围查找只会尝试查找foo标识符,当找到这个变量时,对象属性访问规则将分别接管对bar和baz属性的访问。欺骗词法作用域的方法在词法分析器处理完eval修改作用域后,仍然可以修改作用域,写一个字符串作为eval函数的参数,可以看作是在源代码中写的“钉子户”beginning”。也就是在源码的任意位置动态插入一个代码块,实现已经确定的修改范围。functionfoo(str,a){eval(str);//cheat!console.log(a,b);}varb=2;foo("varb=3;",1);//1,3引擎会认为我们一开始写的代码是这样的functionfoo(a){varb=3;console.log(a,b);}varb=2;foo(1);//1,3在任何情况下,eval(..)都可以在运行时范围内修改词法语法。严格模式注意:在严格模式下的程序中,eval(..)在运行时有自己的词法范围,这意味着其中的声明不能修改该范围。functionfoo(str){"usestrict";eval(str);console.log(a);//ReferenceError:a未定义}foo("vara=2");setTimeout,setInterval,newFunctionsetTimeout(..)和setInterval(..)的第一个参数可以是字符串,字符串的内容可以解释为动态生成的函数代码。这些功能已过时,不推荐使用。不要使用它们!(《你不知道的js》)newFunction(..)函数的行为类似,最后一个参数可以接受一个代码字符串并将其转换为动态生成的函数with创建一个新的作用域with通常被视为重复引用Shortcutsfor对象中的多个属性,而无需重复引用对象本身。函数foo(obj){with(obj){a=2;}}varo1={a:3,};varo2={b:3,};foo(o1);console.log(o1.a);//2foo(o2);console.log(o2.a);//undefinedconsole.log(a);//2-不好,a泄漏到全局范围!with块可以把一个对象当作一个词法作用域,但是块内普通的var声明不会局限于块的作用域,而是会被添加到with所在函数的作用域中。注意:with在严格模式下被禁用。性能修改范围将导致:1.编译时优化将失败。2.标识符位置判断失败总结:欺骗词法作用域的方法在词法分析器处理完后仍然可以修改作用域。但是js是在编译时被js引擎优化的。一旦范围发生变化,这些优化中的大部分都会被破坏。参考《你不知道的js(上卷)》
