JavaScript闭包
在我看来,闭包是一种现象。
一、垃圾回收机制
在JS中,如果解释器知道某个变量不会再被使用到了,那么就会把这个变量回收。如果是前面的变量是函数类型的,因为函数变量会额外创建一个作用域,所以必须要这个函数作用域里面变量全部被回收,那么这个函数才会被回收。对于像这样作用域内部由于某些变量被作用域外部使用而无法回收当前作用域的情况叫闭包。
function foo(){
var a = 1
}
foo() //执行完毕之后a就被回收了
//但是下面这个例子
function foo(){
var test = function(){}
return test
}
var a = foo() //这时的a为test函数,因为a后面还会用到,所以不会回收test,然后也不会回收foo
a()
二、三种闭包常见的情况
1. for循环内部
//没有使用闭包的情况
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i)
})
}
/**结果:输出的全是5,
这里是因为当for循环执行完毕之后i的值为5,然后开始执行5次"function(){console.log(i)}"
在执行过程中解释器会去寻找i的值,在window作用域下找到了5自然就会输出5了
**/
//使用闭包的解决方案
for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i)
})
})(i)
}
/**结果:0、1、2、3、4
这里是因为:这里是定义了一个匿名函数,然后迅速执行它,并且执行它的时候给他传递一个参数i,
然后函数会复制这个形参,对于基本类型,复制的是值,因此每个作用域都会有一个不同的i。这样执行
5次"function(){console.log(i)}"函数的时候,它首先找到的是它外层的那个作用域,而不是window
作用域,所以找到i是我们想要的值。
**/
//使用let的解决方案
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i)
})
}
/** 结果:0、1、2、3、4
因为i是let声明的,导致块作用域也会对其产生限制,所以找i的时候它的上层作用域是for循环,因此也是我们想要的结果。
**/
//下面是一个无聊的例子
for(var i=0;i<5;i++){
let j = i;
setTimeout(function(){
console.log(i,j)
})
}
//块作用域会限制j但不会限制i
2. 自执行函数
//自执行函数
(function(){
})()
//也可以带上名字
(function foo(){
})()
//也可以分解
function foo(){
}
foo()
像这样,用()包裹匿名函数看成一个整体,然后在加一个()表示执行函数,这样叫做自执行函数。还有其他的格式,怎么写都随意,我习惯第一种格式。下面是我复制的其他的写法:
// 下面2个括弧()都会立即执行
(function () { /* code */ } ()); // 推荐使用这个
(function () { /* code */ })(); // 但是这个也是可以用的
// 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了
// 不过,请注意下一章节的内容解释
var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();
// 如果你不在意返回值,或者不怕难以阅读
// 你甚至可以在function前面加一元操作符号
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
//来源:https://www.cnblogs.com/jiangshichao/p/7152855.html
自执行函数闭包的用法一般是在模块写法中。如果知道作用域链的机制的话,这样的写法其实就很好理解了。
var MyModule = (function(window){
//计数器
var count = 0
//定义这个模块通用的变量
var baseValue1 = "xxxxx"
//定义这个模块复用的函数
function basefun1(){
}
return {
fun1:function(){
count++;
}
}
})(window)
MyModule.fun1()
3. 偏函数
偏函数也可以说是返回值为函数的函数。(函数的返回结果是一个函数)
//因为我没想出什么比较有实用性的例子,所以使用官方例子
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