变量作用域和执行上下文(上)

JavaScript基础知识系列

作者 Trevor Cui 日期 2018-02-22
变量作用域和执行上下文(上)

参考自:深入理解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;

function fn() {
console.log(a); // a 是自由变量
} // 函数创建时,就确定了函数要取值的作用域

a += 20;

function bar(f) {
var a = 20;
f(); // 打印 “50”,既不是 “20”,也不是 “10”
}

a += 20;

bar(fn);

从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。另外,函数在定义的时候(不是调用的时候)就已经确定了函数体内部自由变量的作用域

一定要切记的是,只有函数可以创造上下文环境,也只有函数可以创建作用域。