闭包
是一种特性,由作用域和垃圾回收机制共同作用而出现的一种特性。
是指有权访问另一个函数作用域中的变量的函数。
建闭包的常见方式:就是在一个函数内部创建另一个函数。
本质上,闭包是将函数内部和函数外部连接起来的桥梁。
闭包的特点:
- 作用域空间不销毁
优点:因为不销毁,变量也不会销毁,增加了变量的生命周期
缺点:因为不销毁,会一直占用内存,多了以后就会导致内存溢出 - 可以利用闭包访问在一个函数外部访问函数内部的变量
优点:可以再函数外部访问内部数据
缺点:必须要时刻保持引用,导致函数执行栈不被销毁 - 保护私有变量
优点:可以把一些变量放在函数里面,不会污染全局
缺点:要利用闭包函数才能访问,不是很方便
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f();
}
checkScope(); //=> "local scope"
checkScope被invoke时,return f(),运行内部嵌套函数f,f沿着作用域链从内向外寻找变量scope,
找到“local scope”,停止寻找,因此,函数返回 “local scope”;
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f;
}
checkScope()(); //=> "local scope"
*
checkScope被invoke时,将内部嵌套的函数f返回,因此checkScope()()这句执行时,其实运行的是f(),f函数返回scope变量。
*
*词法作用域的基础规则*:函数被执行时(executed)使用的作用域链(scope chain)是被定义时的scope chain,而不是执行时的scope chain。
嵌套函数f(), 被定义时,所在的作用域链中,变量scope是被绑定的值是“local scope”,而不是"window scope",因此,以上代码的结果是"local scope"。
这就是闭包的神奇*特性*:闭包可以捕获到局部变量和参数的外部函数绑定,即便外部函数的调用已经结束。
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return this.scope;
}
return f;
}
checkScope()(); //=> "window scope"
闭包的this指向的是它定义的地方的this,非严格模式下,函数内部的this指向全局对象
(严格模式下,this为undefined),函数 checkScope 的this指向的是window对象,所以返回了window scope
闭包的应用场景
1.为节点循环绑定click事件
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
<script>
let btnList = document.querySelectorAll("button");
for(var i = 0;i<btnList.length;i++){
//错误的代码 onclick是异步触发的,
// btnList[i].onclick = function(){
// console.log(i)
// }
//正确的代码
//采用“立即执行函数Immediately-Invoked Function Expression (IIFE)”的方式创建作用域
(function(i){
btnList[i].onclick = function(){
console.log(i)
}
})(i);
}
</script>
</body>
- 延续局部变量的寿命
<script>
var report = (function() {
var imgs = [];
return function(src) {
var img = new Image();
imgs.push(img);
img.src = src;
}
})()
(function(){
//i在外部就不认识啦
for(var i=0;i<count;i++){}
})();
console.log(i);//报错,无法访问
</script>
3.对结果进行缓存
<script>
var fn=function(){
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
console.log(fn(1,2));//3
//优化版本
var fn=(function(){
var cache={}//将结果缓存到该对象中
return function(){
var str=JSON.stringify(arguments);
if(cache[str]){//判断缓存中是否存在传递过来的参数,存在直接返回结果,无需计算
return cache[str];
}else{//进行计算并返回结果
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return cache[str]=sum;
}
}
})();
console.log(fn(3,8));//11
</script>
4.设计模式之 构造器模式
<script>
// 构造器模式
function Car(model, year, miles){
this.model = model;
this.year = year;
this.miles = miles;
Car.prototype.toString = function(){
return this.model + ' has done ' + this.miles + ' miles';
}
}
var civic = new Car('honda civic', 2019, 2000);
console.log(civic);
console.log(civic.toString());
</script>