var:函数作用域,可以在声明前调用;
let:块作用域,在声明前调用会报错;
闭包:闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
var、let与闭包:我们可以利用这种块级作用域的特性来避免闭包中因为变量保留而导致的问题,譬如如下两种异步代码,使用 var 时每次循环中使用的都是相同变量;而使用 let 声明的 i 则会在每次循环时进行不同的绑定,即每次循环中闭包捕获的都是不同的 i 实例:
for(let i = 0;i < 2; i++){
setTimeout(()=>{console.log(`i:${i}`)},0);
}
for(var j = 0;j < 2; j++){
setTimeout(()=>{console.log(`j:${j}`)},0);
}
let k = 0; for(k = 0;k < 2; k++){
setTimeout(()=>{console.log(`k:${k}`)},0);
}
// output
i:0
i:1
j:2
j:2
k:2
k:2
解释:因为var是没有块级作用域的,所以在for循环中声明的i会存在于test()函数作用域中。每一次for循环都会声明一次i,但后面声明的变量会覆盖前面声明的变量,所以当for循环执行完成后(此时setTimeout()还未被执行),函数作用域中的i的值就变成2了。因为块级作用域的原因,let声明的i都会存在于for块级作用域中,每一次for循环都会生成一个块级作用域。闭包只能取得包含函数中任何变量的最后一个值,这是因为闭包所保存的是整个变量对象,而不是某个特殊的变量
闭包中的this对象:函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
代码1、
var name = "The Window";
var obj = {
name: "My Object",
getName: function(){
return function(){
return this.name;
};
}
};
console.log(obj.getName()()); // The Window
代码2、
var name = "The Window";
var obj = {
name: "My Object",
getName: function(){
var that = this;
return function(){
return that.name;
};
}
};
console.log(obj.getName()()); // My Object
解释:obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。这里要理解函数名与函数功能(或者称函数值)是分割开的,不要认为函数在哪里,其内部的this就指向哪里。匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。
闭包的注意事项:通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
// 释放对闭包的引用
add5 = null;
add10 = null;
闭包的应用:应用闭包的主要场合是:设计私有的方法和变量,定义模块。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数、局部变量和函数内定义的其他函数。匿名函数最大的用途是创建闭包,并且还可以构建命名空间,以减少全局变量的使用。从而使用闭包模块化代码,减少全局变量的污染。把有权访问私有变量的公有方法称为特权方法(privileged method)。
function Animal(){
// 私有变量
var series = "哺乳动物";
function run(){
console.log("Run!!!");
}
// 特权方法
this.getSeries = function(){
return series;
};
}
闭包的缺陷:
-
闭包的缺点就是常驻内存会增大内存使用量,并且使用不当很容易造成内存泄露。
-
如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其它函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。