js 闭包(单例模式封装) 立即执行函数 块级作用域(个人理解剖析篇)
一想到闭包我们通常会想到下面这个代码
一.for循环
var li = document.getElementsByTagName('li');
var len = li.length;
for(var i = 0; i < len; i++){
li[i].onclick = function(){
console.log(i);
}
}
或
var len = li.length;
for(var i = 0; i < len; i++){
setTimeout(function(){
console.log(i);
},2000);
}
毫无疑问最后输出len个值为len,原因为for循环为同步执行,而计时器是异步的,(这里的事件存在很大争议不能说是异步还是同步),然后在经历计时后输出(事件在经历点击后输出)。可以对比以下例子:
for(var i = 0; i < 5; i++){}
function foo() {
console.log(i);
}
foo();
最终先自执行for循环,然后再执行foo函数,并不会因为在不在函数而不执行。
二.闭包
1.保存变量
针对上面问题闭包就出现了,代码如下:
for(var i = 0; i < li.length; i++){
(function(n){
li[i].onclick = function(){
console.log(n);
}
})(i)
}
加入一个立即执行函数,闭包就形成了,何为闭包?闭包就是能够读取其他函数内部变量的函数(这里读取n,n为内部变量),可以理解成定义在一个函数内部的函数。在执行for循环后,先执行立即执行函数,不会因为异步的问题需要等待for循环执行完毕,在被调用时保存i变量自执行赋给n。
2.延长变量生命周期之单例模式
在设计模式中的封装单例时也可以用到闭包来保存变量在空间中或者严格来说是延长变量生命周期从而实现单一实例。
const singleTon1 = (function () {
function Person() {
}
let instance = null;//因变量保存返回的instance不会被销毁
return function singleTon2() {//函数被返回没有被执行,变量被保存
if (!instance) {
instance = new Person();
return instance;
}
};
})();//自执行singleTon1方法
const p = singleTon1();//执行singleTon2方法
在这里就牵扯到垃圾回收机制,在有变量的地方js就会开辟空间,分配内存,内存使用,当执行完函数,使用完变量后销毁。全局变量会在网页关闭后进行销毁。
3.防止全局变量污染
当然我们还要讲闭包的另一个重要的作用:防止全局变量污染
有时为了防止变量污染,保存在局部变量,闭包是有所必要的。何为变量污染?参考如下代码。
var name = 'lihua';
function obj() {
console.log(name);
}
name = "zhangsan"//中间插入,会被恶意修改全局变量。或者多人协作,如果出现多人命名相同,就会造成污染。
obj();
三.块级作用域
es6中let的块级作用域
var len = li.length;
for(let i = 0; i < len; i++){
setTimeout(function(){
console.log(i);
},2000);
}
使用let后每个i都有自己的作用域,所以输出0~len-1。