JavaScript 的闭包是什么

2018-01-05 16:38:41 浏览数 (1)

本文翻译自 w3schools:

  • 原文地址:https://www.w3schools.com/js/js_function_closures.asp
  • 译文地址:shixinzhang 的博客

JavaScript 中的变量有两种:

  • 全局变量
  • 局部变量

使用 闭包 我们可以将全局变量变为局部变量。

两种变量

一个函数可以访问它内部定义的变量,比如这样:

代码语言:javascript复制
function myFunction() {
    var a = 4;
    return a * a;
}

同时函数也可以访问它外部定义的变量,比如这样:

代码语言:javascript复制
var a = 4;
function myFunction() {
    return a * a;
}

第一个例子中,a 是一个 局部变量

局部变量只能在创建它的函数内使用,其他范围(其他函数等)都访问不到它。

在第二个例子中,a 是一个 全局变量

在网页中,全局变量都属于 window 对象。

全局变量可以被当前页面的所有脚本使用或者修改。

和 Java 中相同的是: 全局变量和局部变量即使名称相同,也是不同的变量,修改其中一个不会影响另一个。

注意,不使用关键字 var,直接创建的变量,永远是全局变量,哪怕它是在函数中创建的!

变量的生命周期

全局变量的生命周期和创建它的网页/ window 声明周期一致。

局部变量则短一些,它在函数调用(不是创建)时创建,在函数结束时被删除。

计数器的困境

假设你想要用一个变量计数,你想要让所有函数都可以使用这个计数器。

你可以使用一个全局变量,然后提供一个方法来增加它:

代码语言:javascript复制
var counter = 0;

function add() {
    counter  = 1;
}

add();
add();
add();

// 现在计数器的值是 3

我们想要的效果是只能通过 add() 方法增加计数器的值。

但由于 counter 是全局变量,当前页面的脚本,不使用 add() 也能修改它。

那我们就把 counter 声明为局部变量,这样所有人要使用它只能通过 add() 方法:

代码语言:javascript复制
function add() {
    var counter = 0;
    counter  = 1;
}

add();
add();
add();

// 我们想要的效果是 counter 等于 3,但它并没有这样

每次调用 add() 方法,它都会将 counter 设置为 1.

JavaScript 的内部函数可以解决这个问题。

JavaScript 的嵌套函数

在 JavaScript 中,所有的函数都可以访问全局变量,除此外,它们还可以访问 “上一级函数” 中声明的变量(类似 Java 内部类)。

代码语言:javascript复制
function add() {
    var counter = 0;
    function plus() {counter  = 1;}
    plus();    
    return counter; 
}

在上面的例子中,内部函数 plus() 可以访问父函数的 counter 变量。

现在我们有了局部变量,也有了内部函数,只要能在最外部范围访问内部函数 plus(),我们就能逃离计数器的困境了。

哦对了,我们还需要只初始化一次 counter

我们需要使用闭包。

JavaScript 的闭包

还记得自调用函数 IIFE (Immediately Invoked Function Expression)吗?它做了什么?

代码语言:javascript复制
var add = (function () {
    var counter = 0;
    return function () {return counter  = 1;}
})();

add();
add();
add();

// 现在 counter 的值就是 3

上述代码,先创建了一个自调用匿名函数,这个函数在创建时就进行了自调用,完成了 counter 的初始化。

然后将函数 {return counter = 1;} 赋值给 add 变量,add 就变成了函数。

关键的部分在于:add() 方法可以访问父函数声明的 counter 变量

counter 被匿名函数的作用域保护着,我们只能通过 add() 方法修改它。

这就是闭包,它让函数可以拥有“私有”变量。

闭包就是一个函数即使在父函数关闭之后,也可以访问父函数中的变量。

0 人点赞