变量作用域和闭包(Variable scoping and closures)

2018-06-15 18:48 更新

在JavaScript中,你必须使用变量之前,通过var声明变量:

> var x;
> x = 3;
> y = 4;
ReferenceError: y is not defined

你可以用一条var语句声明和初始化多个变量:

var x = 1, y = 2, z = 3;

但我建议每个变量使用一条语句。因此,我将上面的语句重写为:

var x = 1;
var y = 2;
var z = 3;

由于提升(见下文),最好在函数顶部声明变量。

变量和函数作用域(Variables are function-scoped)

变量的作用域总是整个函数(没有块级作用域)。例如:

function foo() {
    var x = -3;
    if (x < 0) {  // (*)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 3
}

我们可以看到tmp变量不仅在(*)所在行的语句块存在,它在整个函数内都存在。

变量提升(Variables are hoisted)

变量声明会被提升:声明会被移到函数的顶部,但赋值过程不会。举个例子,在下面的函数中(*)行位置声明了一个变量。

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (*)
    }
}

在内部,上面的函数被执行像下面这样:

function foo() {
    var tmp;  // declaration is hoisted
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

闭包(Closures)

每个函数保持和函数体内部变量的连接,甚至离开创建它的作用域之后。例如:

function createIncrementor(start) {
    return function () {  // (*)
        return start++;
    }
}

在(*)行开始的函数在它创建时保留上下文,并在内部保存一个start活动值:

> var inc = createIncrementor(5);
> inc()
5
> inc()
6
> inc()
7

闭包是一个函数加上和其作用域链的链接。因此,createIncrementor() 返回的是一个闭包。

IIFE:模拟块级作用域(IIFE: Simulating block scoping)

有时你想模拟一个块,例如你想将变量从全局作用域隔离。完成这个工作的模式叫做 IIFE(立即执行函数表达式(Immediately Invoked Function Expression)):

(function () {  // 块开始
    var tmp = ...;  // 非全局变量
}());  // 块结束

上面你会看到函数表达式被立即执行。外面的括号用来阻止它被解析成函数声明;只有函数表达式能被立即调用。函数体产生一个新的作用域并使 tmp 变为局部变量。

闭包实现变量共享(Inadvertent sharing via closures)

下面是个经典问题,如果你不知道,会让你费尽思量。因此,先浏览下,对问题有个大概的了解。 闭包保持和外部变量的连接,有时可能和你想像的行为不一致:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (*)
}
console.log(result[1]()); // 5 (不是 1)
console.log(result[3]()); // 5 (不是 3)

(*)行的返回值总是当前的i值,而不是当函数被创建时的i值。当循环结束后,i的值是5,这是为什么数组中的所有函数的返回值总是一样的。如果你想捕获当前变量的快照,你可以使用 IIFE:

for (var i=0; i < 5; i++) {
    (function (i2) {
        result.push(function () { return i2 });
    }(i));  // 复制当前的i
}

深入阅读

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号