JavaScript闭包 - 面试重灾区,如何应对?

1.闭包-面试重灾区

最近面试的过程中,但凡问到JavaScript闭包的时候,一脸懵逼的情况的占多数。

闭包这个东西,你说它重要吧,确实很重要,毕竟这是很多前端框架广泛使用的开发技巧。你说他不重要吧,也确实没那么重要,因为我们开发项目基本都是套用现有的框架,在框架的范围里面写代码,很少会用到闭包。

可是,耐不住面试经常要问啊。

这一节,我们就来聊聊闭包的话题!

2.什么情况会产生闭包

先看一个例子:

function f(){
    let a = 1;

    return function(){
        console.log(a);
    }
}  

f()()

a是函数f内部的一个变量,按理说函数结束后,外面就没法访问到a了。

可是假如我们用一个函数访问到a,然后再把这个函数返回出去,就延续了a的生命周期。

上面代码的结果是1。

不仅如此,我们还可以在外面反复地操作a。

3.闭包将产生幽灵般的鬼魅变量

上面的例子中,我们把a变量悄悄地送出了函数f,接下来我们可以对它进行反复蹂躏!

function f(){
    let a = 1;

    return function(increment){
        a += increment;
        console.log(a);
    }
}  

f()(1)

答案是2,可见,我们可以在外面对a变量进行操作。下面思考一个问题,假如我多次操作会怎样?

var inner = f();
inner(1);
inner(1);
inner(1);

答案

 可见,多次操作的话,操作的是同一个a变量,这个a变量就如同鬼魅一般,你在外面看不到他,他却真实存在着。

4.闭包可以和对象结合起来用

直接看下面的例子,有一定的实战意义

var obj = {}

function f(){
    let a = 1;
    obj.num = a;
    obj.get = function(){
        return a;
    }
    obj.set = function(v){
        a = v;
    }

}  

f()

这个代码和之前的有不同,它没有在函数f中直接返回另一个函数,而是给外面的obj对象赋予了一些属性和方法,很神奇吧,可这是语法允许的。

函数f被运行后,那么obj.num的值想必大家一看就知道是1

那假如我这么做呢?

obj.num += 1;

 这样一来,obj.num就等于2

但是obj.get()得到的值是多少?

还是1,这是因为a是基本类型的数字1,而 obj.num = a 其实是值拷贝,我们没法通过改变num的大小去同步改变a,他们是两个变量了。而 obj.getobj.set 操作的才是真正的a

上面我们说 a是基本类型的数字1,而 obj.num = a 其实是值拷贝 ,那如果a是对象类型,就可以操作了。

let a = {};
obj.a = a;
obj.get = function(){
    return a;
}
obj.set = function(v){
    a = v;
}

好了,从这个例子中,我们不难发现,闭包的最大作用就是延续函数内部某些变量的生命周期。但是也正因为此,闭包可能会引发一些内存泄漏的问题。不过,大部分情况,这点内存泄漏真的无伤大雅。

可能有的同学会说,我不用闭包也可以啊,大不了在obj里面设置一个属性a,一样的。

在这个例子中,确实是的。

可是,在某些特殊的时候,用闭包是真的很有必要。

5.闭包的实战 - Vue数据绑定 

在Vue中,我们知道,会有一个初始化过程,这个过程会对data中的所有属性进行挟持,以便触发watcher更新。以下是简化的代码

var data = {
    username:'',
    password:''
}

for(let key in data){
    Object.defineProperty(data,key,{
        set(v){
            data[key] = v
        },
        get(){
            return data[key]
        }
    })
}

这个代码乍一看没啥问题,可是当我们给username赋值,就会触发set方法

 报错是因为发生了循环引用,set被重复触发。

同样的,get也会报错

 

 这可怎么办,我们是不是必须得有一个临时变量,来保存当前属性的value呢?

像这样

for(let key in data){
    let value = data[key]
    Object.defineProperty(data,key,{
        set(v){
            value = v
        },
        get(){
            return value
        }
    })
}

瞬间完美了。

 按理说,value是for循环中的一个临时变量而已,因为用的是let,所以只在当前作用域有效。而这个临时的value因为被set和get方法访问到了,于是其生命周期也得以延续!

所以,我对闭包的理解就是:函数访问了外部的变量,并且这个函数会在未来的某个时刻用到这个变量,就是闭包。

猜你喜欢

转载自blog.csdn.net/weixin_39570751/article/details/123161134