参考自:深入理解javascript原型和闭包系列(王福朋 - 博客园)。如有侵权,请联系删除。
变量作用域
JavaScript 变量的作用域无非就两种:全局变量和局部变量。
每次定义一个函数,都会产生一个作用域链(scope chain)。当 JavaScript 寻找变量 varible 时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性 varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止。
下面继续说作用域。作用域是一个很抽象的概念,类似于一个“地盘”:

如上图,全局代码和 fn、bar 两个函数都会形成一个作用域。而且,作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的。例如,fn 作用域下创建了 bar 函数,那么“ fn 作用域”就是“ bar 作用域”的上级。
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。例如以上代码中,三个作用域下都声明了“ a ”这个变量,但是他们不会有冲突。各自的作用域下,用各自的“ a ”。
执行上下文
先来看以下几段代码:
1、console.log(a);
// Uncaught ReferenceError: a is not defined
2、console.log(a);
// undefined
var a = 10;
// 尽管对 a 的声明在 console 之后,console 的时候 a 是已定义的,但是不知道值为 10
3、console.log(this);
// Window {postMessage: ƒ, blur: ƒ, ...
4、console.log(f1);
// ƒ f1() {}
function f1() {}; // 函数声明
5、console.log(f2);
// undefined
var f2 = function() {}; // 函数表达式
由以上几段代码,可以看出 JavaScript 的一些特点:在一段 js 代码真正运行之前,浏览器已经做了一些准备工作,具体工作主要有以下三点:
1、变量、函数表达式——变量声明,默认赋值为undefined;
2、this 赋值
3、函数声明赋值
这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用 undefined 占个空。
函数体
再先看一段代码:
var a = 10; |
从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。另外,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
一定要切记的是,只有函数可以创造上下文环境,也只有函数可以创建作用域。