闭包
- 使用chrome调试查看
- 闭包是有权访问另一个函数作用域的变量的函数.
简而言之,这些函数表达式定义在另一个函数的函数体内,它可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包,例如:
function fn1() {
var num = 0
function fn2() { //fn1函数内嵌套着内部函数fn2
num++
console.log(num)
}
return fn2 //将fn2返回
}
var result = fn1() //相当于result = function fn2() {num++;console.log(num);}
result() //1
在上面的例子中fn2就是闭包。
产生闭包的条件
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
- 执行外函数
闭包的生命周期
- 产生: 在嵌套内部函数定义执行完时就产生了(不是调用)
- 死亡: 在嵌套的内部函数成为垃圾对象时
闭包的用途
-
可以读取函数内部的变量。
- 根据作用域链定义可知子对象会一级一级地向上寻找所有父对象的变量,所以父对象的所有变量,对子对象都是可见的。上面的例子中,fn2函数就可以访问fn1函数内部的局部变量num
- 平时我们在函数中的返回值经常是一个值,既然fn2函数能够获取fn1内部的变量,我们将fn2函数返回就能在外部获取num变量了
- 将fn2返回后,相当于
var result = function fn2() {num++;console.log(num);}
,即是外部的函数result也能获取局部变量num了
-
让变量的值始终保存在内存中。
function fn1() {
var num = 0
function fn2() {
num++
console.log('闭包函数' + num)
}
return fn2
}
var result = fn1()
result() //闭包函数1
result() //闭包函数2
function commonFn() {
var num = 0
num++
console.log('普通函数' + num)
}
commonFn() //普通函数1
commonFn() //普通函数1
从上面的例子中可知,重复调用闭包函数时,每次输出的num值都会+1,而普通函数重复执行后num值仍然保持不变,这证明了函数fn1中的局部变量num一直保存在内存中,并没有在f1调用后被自动清除。
原因:fn1是fn2的父函数,fn2被赋值给全局变量result,这导致fn2始终在内存中,而fn2依赖于fn1,故fn1的变量值一直存在于内存中。
闭包的缺点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大而会导致内存泄漏,所以在调用结束之后要将不使用的局部变量删除掉。
内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误
内存泄漏
- 占用的内存没有及时释放
- 内存泄漏积累多了就容易导致内存溢出
- 常见的内存泄漏
- 意外的全局变量
- 没有及时清理的计时器或回调函数
- 闭包
闭包的this指向问题
let obj = {
name: 'Joe',
getName: function() {
return function() {
console.log(this.name)
}
}
}
obj.getName()() //<empty string>
返回的函数最终是在全局作用域中,故无法访问到obj对象中的name属性。
闭包的例子
- 创建函数工厂
function addFunc(x) {
return function(y) {
return x + y
}
}
var add5 = addFunc(5)
var add10 = addFunc(10)
console.log(add5(2)) //7
console.log(add10(2)) //12
以上例子是创建两个数值相加的函数工厂,var add5 = addFunc(5)
这行代码先给x传值5,且这行代码相当于var add5 = function(y) {return 5 + y}
在打印行再给y传值2.
- 返回一个对象的写法
let counter = function() {
let currentVal = 0
function addVal(value) {
currentVal += value
}
return {
increment: function() {
addVal(1)
},
decrement: function() {
addVal(-1)
},
value: function() {
return currentVal
}
}
}
let counter1 = counter()
let counter2 = counter()
console.log(counter1.value()) //0
counter1.increment()
counter1.increment()
console.log(counter1.value()) //2
counter1.decrement()
console.log(counter1.value()) //1
console.log(counter2.value()) //0
由这个例子可知counter1和counter2是独立的,互相不影响的,也就是它们的值是私有的。
网友评论