前段时间一直在投一些中小型公司吧,感觉好久都收不到反馈,也不知道是被淘汰了还是没出结果呢,最近开始投一些大一点的公司准备尝试一下,就在昨天接到面试电话的时候,接受到了滴滴的毒打。跟一些面试不一样的是不只是一些基础的基本概念吧,比如说什么是原型和原型链,说一下继承,讲一下this指向之类的。更多的是为什么要这样用,手写算法,预测输出结果之类的面试题。
印象最深刻的应该就是那道关于闭包的题目了吧,是预测一个程序的输出结果,当时看的我是晕头转向,大厂的面试也是招架不住,真的是把我给面到自闭,感觉自己啥也不是,估计不用等结果直接就知道自己已经凉了。今天一天也没有接到面试,同样也没有约到面试机会。
所以就索性去学了一下关于闭包的知识,也算是给自己补补洞吧,同样还是老规矩把学习的东西记录分享出来,分享之前还是看一下昨天那道题目。
—————————————————正经分界线———————————————————
什么是闭包
要理解闭包就要去理解变量的作用域,在JS中存在两种变量的作用域,一种是全局变量,一种是局部变量。两种变量的区别就是函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
那什么是闭包呢?闭包是指有权访问另外一个函数作用域中的局部变量的函数。声明在一个函数中的函数,叫做闭包函数。而且内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包的特点
1、让外部访问函数内部变量成为可能
2、局部变量会常驻在内存中
3、可以避免使用全局变量,防止全局变量污染
4、会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,并且互不干扰。闭包会发生内存泄漏,每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。大概意思就是说当外部函数运行结束甚至销毁时,局部的变量key=value,尽管key被垃圾回收机制给回收了,但是value仍不会被回收,会变成一个自由变量留下引用的指针。
这一天也是看了不少的例子,还是有一些没理解清楚的,回来理解一下再过来更新,还是先把我理解了的分享出来,记录讲解一下我的理解。
例子1
var age = 10;
function foo(){
console.log(age);//-----------------1
var name = "hunt_bo";
return function(){
console.log(name);
}
}
console.log(name);//---------------2
var bar = foo();
bar();
这个应该说是最简单的一个闭包的例子了,那就用他来介绍一下闭包的基本概念,首先呢是函数内部肯定是可以访问到全局变量的值的,所以在foo()函数中去打印age肯定是可以打印出来的,就像代码中的标记1,但是呢在标记2处打印的函数内部的变量name是打印不出来的。
但是当我们用到闭包的时候,在foo函数内部返回一个函数,在返回的函数中去用到局部变量name,最后呢在外部调用,这时候的bar仍然在函数外部,但是完全可以拿到局部变量的name值。这就是闭包的定义吧,同样呢也是闭包的第一个特点。
例子2
function addCount(){
var count = 0;
return function(){
count += 1;
console.log(count);
}
}
var fun1 = addCount();
var fun2 = addCount();
fun1();//1
fun1();//2
fun1();//3
fun2();//1
fun2();//2
这个例子呢,跟第一个也算是差不多的,就是在返回函数中做了一个递增并打印的操作,把addCount的返回函数给到fun1和fun2,当我向上面代码一样调用的时候呢,就会发现替他打印的不是12345,而是12312,那这是什么原因呢,这就回到了上边介绍的那句话:每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。所以说虽然fun1和fun2都是addCount(),但是呢都创建了新地址,都是自己的,互不干扰。
例子3
var i = 0;
function outerFn(){
function innerFn(){
i++;
console.log(i);
}
return innerFn
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();//1
inner2();//1
inner1();//2
inner1();//3
inner2();//2
inner2();//3
这个跟例子2是一样的道理,只是变量的位置不一样,但是呢,对于内部的innerFn来说,变量是外部函数的局部变量和全局变量来说都是一样的,就不过多的解释了。
例子4
function fn(){
var a = 3;
return function(){
return ++a;
}
}
console.log(fn()());//4
console.log(fn()());//4
console.log(fn()());//4
var newFn = fn();
console.log(newFn());//4
console.log(newFn());//5
console.log(newFn());//6
还是一样的,相当于fn()()的直接调用都是在开辟一个新的地址,但是把它赋给一个变量的时候,地址就不会再改变了,这个时候就会发现输出只是在递增的。
例子5
(function() {
var m = 0;
function getM(){
return m;
}
function seta(val){
m = val;
}
window.g = getM;
window.f = seta;
})();
f(100);
console.log(g());//100
(function(){ /* code */ })()是立即执行函数,当js执行到(function {// code})();时, 由于(function {// code})是表达式, js会去对它求解得到返回值, 由于返回值是一个函数, 故而遇到()时, 便会被执行。然后把getM和seta给到了window,这时候呢,我想到了大佬总结的一句话:闭包找到的是同一地址中父级函数中对应变量最终的值。
例子6
var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
console.log(i);
};
})(i);
}
这个呢是在事件处理中闭包的应用,也就是说我们按照传统的思想去写的话,相信我,不管点击第几个都会给你打印5的,但是闭包处理过的就可以实现我们想要的功能。
说句题外话就是在现实应用中我是不会这样用,let它不香么,这么复杂。
再来最后一个例子吧,就是我在面试中面试官给我的那道题:
function fnnn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
}
}
return arr;
}
var list = fnnn();
for(var i = 0,len = list.length;i < len ; i ++){
console.log(list[i]());
}
这个题我刚开始看的时候吧,感觉就是一个简单的函数的数组,当时想都没想,直接就说了12345,但是当时没有看清楚var定义的i,在函数中直接返回,在调用的时候i早就变成5了呀,然而现在已经晚了,继续准备后面的面试吧。
忙了一天,就为了这一篇博客,生气
当我去搜闭包为什么会造成内存泄漏的时候,突然在知乎上发现了一篇帖子,关于js闭包是否真的会造成内存泄漏?大家可以去看一下,感觉挺有趣的,不过真的把我搞蒙了,到底会不会造成内存泄漏。
咱也不知道咱也不敢问啊
在学习的过程中也是参考了不少大佬的博客文章,感觉我写的太菜的话,建议大家去这几篇博客学习一下,因为我是从这些文章里看懂的,理解的闭包。
什么是闭包?闭包的优缺点?
什么是闭包?闭包的作用,用法及优缺点
闭包与自执行函数 区别
闭包,看这一篇就够了——带你看透闭包的本质,百发百中
JavaScript内存泄露,闭包内存泄露如何解决
闭包
春招在即,面试不慌:带你复习什么是闭包
闭包-廖雪峰的官方网站
关于js闭包是否真的会造成内存泄漏?
今天的文章前端面试题:闭包_js面试闭包完美回答「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/72314.html