在 ES6 之前, javascript 只有全局作用域和函数作用域. 所谓作用域就是一个变量定义并能够被访问到的范围. 也就是说如果一个变量定义在全局 (window) 上, 那么在任何地方都能访问到这个变量, 如果这个变量定义在函数内部, 那么就只能在函数内部访问到这个变量.
全局作用域只要页面没关闭就会一直存在, 而函数作用域只有在函数执行的时候才存在, 执行完就销毁. 且每次执行函数都会创建一个新的作用域.
那么什么是作用域链呢?
在了解作用域链之前, 我们先了解一个执行期上下文的概念.
执行期上下文: 当函数执行时, 会创建一个称为执行期上下文的内部对象(即 AO 或 GO), 一个执行期上下文定义了一个函数的执行环境, 函数每次执行时对应的执行期上下文都是独一无二的, 所以每次调用一个函数都会创建一个新的执行期上下文, 当函数执行完毕, 所产生的执行期上下文被销毁.
作用域链就是函数中 [[scope]] 属性所存储的执行期上下文对象的集合, 这个集合呈链式链接, 我们把这种链式链接叫做作用域链.
作用域链更像是一种包含的关系. 比如说函数 A 内部定义了一个函数 B, 所以 B 的定义是依赖于 A 的, 也就是说 B 在 A 的内部, 那么 B 中就可以访问 A 的中的变量和方法. 这种一层一层向上依赖的关系就构成了作用域链.
为了更好理解, 我们直接看例子.
- var name = 'xiaoyu';
- function fn1() {};
- function fn2() {
- var num = 10;
- function fn3() {
- var num1 = 10;
- console.log(num);
- };
- return fn3;
- }
- var fn4 = fn2();
在上个例子我们知道, fn2 执行的时候返回 fn3, 产生了闭包. 但是一个函数执行然后返回另一个函数都会产生闭包嘛? 我们来看一下.
- var name = 'xiaoyu';
- function fn1() {};
- function fn2() {
- var num = 10;
- function fn3() {//fn3 函数没有依赖 fn2 函数内的变量
- var num1 = 10;
- console.log(num1);
- };
- return fn3;
- }
- var fn4 = fn2();
了解了作用域链之后, 我们来看一个小例子, 巩固一下.
- var age = 10;
- var obj = {
- age: 12,
- test: function() {
- console.log(age);
- console.log(obj.age);
- console.log(this.age);
- }
- }
- obj.test();
console.log(this.age)打印出 12 不难理解, 但是为什么 console.log(age)不也应该打印出 12 嘛.
我们说 test 执行时首先会在自己的作用域内查看有没有 age 变量, 然后再沿着作用域链往上到全局作用域查找 age 变量, 全局作用域下有 age 变量和 data 变量. 所以 console.log(age)打印出的 10, 如果要打印出 12 则需要访问 obj.age.
ES6 的块级作用域
在 ES6 之后, 通过 let 和 const 引入了块级作用域. 即通过 let 和 const 声明的变量只在声明所在的块级作用域内有效, 并且 let 声明的变量虽然属于全局变量, 但不再属于全局对象 window.
我们通过一段代码来看一下引入块级作用域后, 函数的作用域链的变化.
- var age = 10;
- let obj = {
- age: 12,
- test: function() {
- console.log(age);
- console.log(obj.age);
- console.log(this.age);
- }
- }
- obj.test();
来源: http://www.jb51.net/article/145077.htm