函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性就是计算机科学文献中称的 “闭包”。这个术语非常古老,是指函数变量可以被隐藏于作用链之内,因此看起来是和函数将变量“包裹”起来。
在 JavaScript 中,只有函数内部的局部变量不能直接访问到,而函数中的子函数能读取局部变量,因此可以把闭包简单理解成 "定义在一个函数内部的函数"。
《JS 高级程序设计》里面的定义是:一个可以访问另一个函数里局部变量的函数既闭包。
阮一峰的网络日志:在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包是一种形式,它是以函数的方式表现出来。
用处:
- 能读取其他函数内部变量的函数,并且是 「间接」访问,不能「直接」访问。
- 让这些变量的值始终保持在内存中。
特点:
- 可以记录诞生他的环境(全局环境是必有的),JS 也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个 作用域是在函数定义时决定的,而不是调用时决定的。
- 闭包是把需要的变量保存到内置属性[[Scopeds]]上,这是一个数组,默认必有一个全局环境变量。其实就是帮我们把变量重新声明在当前的作用域中。(网上看的一些知识,自己也不太懂,逼格太高)
形成闭包的三个条件:
- 函数嵌套(有一个函数 A , 在函数 A 内部返回一个函数 B)
- 访问所在的作用域的父级作用域 (在函数 B 中访问函数 A 的私有作用域变量)
- 在所在的作用域外被调用 (在函数 A 外部,有变量引用函数 B)
function A() {
var num = 100;
return function B() {
// 返回值函数B
var num1 = num; // 在函数B中访问函数A的局部变量
console.log(num1);
return num1;
};
}
var fn1 = A(); // 此处调用函数A,执行的结果就是函数B
var fn2 = fn1(); // 此处调用函数fn1(),执行结果就是引用函数B
使用闭包的注意点:
闭包会使得函数中的变量都被保存在内存中,每个父函数调用完,就会形成新的闭包。父函数中的变量始终会在内存中,相当于缓存。内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,尤其是IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
但是我认为: 使用了闭包那里面的变量明明就是我们需要的变量。不能说是内存泄漏,内存泄露是指: 你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
IE 内存泄漏的解决方法:在退出函数之前,将不使用的局部变量全部删除。大部分浏览器,通过赋值为 null,释放内存。在 IE 中 在 return 返回值下面 delete 变量。
闭包可以在父函数外部,改变父函数内部变量的值。 所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,因为都在内存中,所以不要随便改变父函数内部变量的值,否则一处变其它地方也会随之变化。
个人理解:
其实我觉得,根本不需要知道闭包这个概念,一样可以使用闭包!
因为函数也是一个 var,只是它比较特殊,函数里面的 var 不能被外接直接访问,然后在函数里面定义了一个函数来访问。其实跟调用一个对象里面定义的函数然后访问对象里面 key,区别就是对象里面的一个 key 可以被外面显示调用(造成不能隐蔽的问题),闭包解决这个问题。
闭包是 JS 函数作用域的副产品。
由于 JS 的函数内部可以使用函数外部的变量,正好符合了闭包的定义,而不是 JS 故意要使用闭包。
总结
闭包让程序更加安全,封装数据、暂存变量。不让外部直接访问到我们的数据。