JavaScript系列——作用域和作用域链
JavaScript系列——作用域和作用域链
作用域
JavaScript通过函数管理作用域。在函数内部声明的变量只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。在实现项目中,为了避免全局变量冲突导致,我们应该尽量的减少全局变量的使用。1
2
3
4
5
6
7
8
9
10
11
12
13
14function sum(x, y) {
// 不推荐写法: 隐式全局变量
result = x + y; //优化:var result = x + y; 使用var
return result;
}
// 反例,勿使用
function foo() {
//相当于:
//var a = 0 ;
//b = 0; 隐式全局变量
var a = b = 0;
// ...
}
隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。
- 通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
- 无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:
1 | // 定义三个全局变量 |
Javascript没有代码块作用域的概念,局部作用域是针对函数来说的。
1 | function fun() |
作用域链
在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
1 | var name="hello"; |
当执行到ss()的时候,创建ss的运行期上下文对象,并将它放入到作用域链表的头部,之后还有s()的运行期上下文对象,之后就是全局对象,所有再查找name的时候,先从链表头部中查找,也就是会输出“hello2”;当执行到sss()的时候,在sss的运行期上下文中找不到name就会继续从下一个找,也就是s()的运行期上下文,也就会输出“hello1”。
预编译
JavaScript解析过程可以分为编译和执行两个阶段。编译也就是我们常说的JavaScript预处理(即预编译)
- 先预定义var变量,在预定义function函数
- 变量的预定义只做变量声明,不做初始化,此时值为undefined
- function函数也做变量声明,值初始化为函数体
- 匿名函数不做预编译
1 | alert(om); // 显示 undefined |