作用域
作用域是什么
作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)。
LHS查询:查找的目的是对变量进行赋值。
RHS查询:目的是获取变量的值。
嵌套作用域:引擎从当前执行作用域开始查找变量,如果没找到,就向上一级继续查找。
当抵达最外层的全局作用域时,无论有没有找到,查找过程都会停止
JS编译原理
例如:var a = 2;
任何JavaScript代码片段在执行前都要进行编译(通常就在执行前)。因此,JavaScript编译器。首先会对var a = 2;这段程序进行编译,然后做好执行它的准备,并且马上就会执行它。
一. JS编译
· 引擎
从头到尾负责整个JavaScript程序的编译及执行过程。
· 编译器
引擎的好朋友之一,负责语法分析及代码生成等脏活累活
· 作用域
引擎的另一位好朋友,负责收集并维护所有声明的标识符(变量)组成的
一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
二.接下来 var a = 2的编译过程
1.编译器询问作用域是否已经有有一个该名称的变量存在作用域集合中。如果是,编译器就会忽略该声明,继续进行编译。否则在该作用域的集合重声明一个新的变量,命名为a。
2.编译器为引擎生成运行所需的代码,用来处理a = 2的操作。首先询问作用域,当前有没有a的变量,有,引擎使用a的变量。没有则引擎继续查找该变量。如果最终找到a变量,把2赋值给a。没找到抛出异常!
词法作用域
词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况时这样的)。
1 | function foo(a) { |

1.整个全局作用句,有一个标识符:foo。
2.foo所创建的作用域,有三个标识符:a,bar和b。
3.bar所创建的作用域,有一个标识符:c。
无论函数在哪里被调用,它的词法作用域都只由函数被声明时所处都位置决定。词法作用域查找只会查找一级标识符,比如a,b,c。如果代码中引用来foo,bar,baz,词法作用域只会试图查找foo标识符,找到这个变量后,对象属性访问规则分别接管bar和baz属性都访问。
函数作用域
函数作用域
函数作用域:属于这个函数的全部变量都可以在整个函数都范围内使用及复用(事实上在嵌套都作用域也可以使用)。这种数据方案非常有用,能充分利用JavaScript变量可以根据需要改变值类型都“动态”特效。
例如:1
2
3
4
5
6
7
8
9function doSomething(a) {
b = a + doSomethingElse(a*2)
console.log(b*3)
}
function doSomethingElse(a) {
return a - 1;
}
var b;
doSomething(2) //15
b和doSomethingElse应该是doSomething内部具体实现的“私有”内容。给予外部作用域对b和doSomethingElse对“访问权限”。导致他们可能被有意无意以非预期对方式使用。
1 | function doSomething(a) { |
b和doSomethingElse都无法从外部被访问,只能被doSomething控制,设计上将具体内容私有化。
函数声明和函数表达式
函数声明:如果function是声明中都第一个词,就是一个函数声明,否则是一个函数表达式。
函数表达式:以(function…而不仅是以function…开始。)
1.例如
1
2
3
4
5
6
7 var a = 2;
function foo() {
var a = 3;
console.log(a); //3
}
foo()
console.log(a); //2
具名函数foo()本身污染所在都作用域,并且必须显式通过foo()调用才能运行其中代码。如果函数不需要函数名自动运行?
1 | var a = 2; |
此foo函数被绑定在函数表达式自身都函数中而不是所在作用域中。意味foo只能在(function foo(){…})…被访问,外部作用域不行。foo被因此在自身意味不会非不要地污染外部作用域
2.匿名和具名1
2
3setTimeout(function() {
},1000)
匿名函数表达式:没有名称和标识符。函数表达式可以是匿名的,而函数声明则不可以省略函数名。