【前端面试系列】JS中的闭包-来龙去脉

引言

经历过面试的前端人被问得做多的一个话题或许就是js的闭包了吧,就好像高考中总要出些难题来拉开不同人的差距,而闭包就是如此,哪怕平时用的不多,但是因为其特殊性总被拿来衡量一个前端人的js掌握情况。
以前也陆陆续续看过不少资料,书籍,对其的解释五码八门,大差不差,但从来无法回答自己几个核心问题(例如为啥叫closure,到底使用场景是什么),所以一直无法深入去理解,也就更别提灵活的使用了。
最近查阅资料时发现w3school中对其的介绍颇为深入,翻译给大家,希望对大家有所启迪和帮助。

资源地址:https://www.w3schools.com/js/js_function_closures.asp

w3school不清楚原因被墙了,明显纯技术的网站

js中的变量可以是属于local的或是全局global的。global变量可以利用closure闭包技术变成local本地的。

全局变量

一个函数可以获取函数内部所有的变量,就像下面这样(感觉像废话)

Example
function myFunction() {
    var a = 4;
    return a * a;
}

但函数通过可以获取外面定义的变量(js特色):

Example
var a = 4;
function myFunction() {
    return a * a;
}

在上一个例子中,a是一个全局的变量。在一个网页中,全局变量属于window对象。当然了,全局变量可以在页面的所有脚本中被使用和修改。在第一个例子中,a是一个局部变量。一个局部变量只能被用在函数定义的地方,对于其他函数和脚本是隔离的。

全局变量和局部变量就算名字取得一样了,还是不同的变量,改变其中一个,另一个不受影响。

如果变量创建的时候是不带关键字var的,那就都是全局变量,哪怕他们是在函数内部的定义的。

变量的生命周期

全局变量存活的时间就是应用(可以是window或是你的网页)存在的时间。局部变量生命可就更短了,在幻术被调用的时候创建,然后在函数结束的时候被删除掉。

A Counter Dilemma(计数器的悖论)

翻译的不好,其实不是悖论,更确切的说是一种两难的境地,没有好的办法来处理这个计数器,当然后面得通过闭包来实现

假定你想要借助某个变量来统计某些东西,而且你希望所有的函数都可以使用这个。你当然可以使用一个全局变量和一个函数来实现上面的需求:

Example
// Initiate counter
var counter = 0;

// Function to increment counter
function add() {
    counter += 1;
}

// Call add() 3 times
add();
add();
add();

// The counter should now be 3

上面的方案存在一个问题,页面上的代码可以直接修改这个全局变量counter,而且不需要调用add(而我们希望的是counter只作为add的私有变量来处理)

Example
// Initiate counter
var counter = 0;

// Function to increment counter
function add() {
    var counter; 
    counter += 1;
}

// Call add() 3 times
add();
add();
add();

//The counter should now be 3. But it is 0

完全不起作用,因为我们显示的是全局变量的counter而不是局部的counter。我们可以通过让函数返回它来使删除全局变量counter而只访问局部的counter。

Example
// Function to increment counter
function add() {
    var counter; 
    counter += 1;
    return counter;
}

// Call add() 3 times
add();
add();
add();

//The counter should now be 3. But it is 1.

还是不起作用,因为我们在每次调用函数的时候都重置了局部变量counter。而一个js的内部函数就可以解决这个问题。

JS嵌套函数

所有的函数都可以访问全局的作用域。事实上,在js中,所有的函数都可以访问父域的变量。JS支持嵌套函数,嵌套函数就可以访问父域。在下面的例子中,内部函数plus()可以在父函数中访问counter变量。

Example
function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}

这可以解决counter的两难情况,只要我们可以在外部获取到plus函数。我们需要让counter=0只执行一次。因此我们需要closure,闭包。

JS闭包

还记得自调用函数么,来看看下面的函数:

Example
var add = (function () {
    var counter = 0;
    return function () {counter += 1; return counter}
})();

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

// the counter is now 3

变量add 设置了自调用函数来返回一个函数。自调用函数正好只执行一次,实现了counter的初始化。这就是一个js的闭包,它能够让一个函数有自己私有的变量。counter的作用域被限制在匿名函数中,而且只能通过add函数来改变。所以说闭包就是一个能够获取父域的函数,即使父函数已经关了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章