闭包

闭包的定义:在函数中创建子函数,并且子函数中调用了函数中的变量。称之为闭包

闭包和普通函数的区别是:
  • 多了一层外部函数的作用域链
  • 普通函数的作用域链为:函数本身的变量 -> 全局变量;而闭包作用域链为:函数本身的变量 -> 父函数的变量-> 全局变量
  • 应尽量少用闭包,因为会增加内存的占用

标准的闭包实现:

 1 (function () {
 2     function createComparisonFunction(propertyName) {
 3         return function (obj1, obj2) {
 4             var value1 = obj1[propertyName]
 5             var value2 = obj2[propertyName]
 6 
 7             var result = 0
 8             if (value1 > value2) {
 9                 result = 1
10             }
11             else if (value1 < value2) {
12                 result = -1
13             }
14 
15             return result
16         }
17     }
18 
19     var compare = createComparisonFunction('name')
20     var obj1 = { name: 'a' }
21     var obj2 = { neme: 'b' }
22     console.log(compare(obj1, obj2)) // -1
23 })();
闭包与变量
  因为闭包会共享父级作用域链的变量,所以下面demo中的闭包,打印出的index结果都是10
 1 (function () {
 2     function createFunction() {
 3         var arr = []
 4 
 5         for (var index = 0; index < 10; index++) {
 6             arr[index] = function () {
 7                 return index
 8             }
 9         }
10 
11         return arr
12     }
13 
14 
15     var arr = createFunction()
16 
17     for (var index = 0; index < arr.length; index++) {
18         var element = arr[index];
19         console.log(element()) // 全部都是10
20     }
21 })();

为了解决上面的变量共享问题,我们可以给数组赋值时,增加一个立即执行的闭包。

思路:因为参数是按值传递的,所以多写一个闭包,并且马上执行,就可以将每个参数都隔离开,达到预期的效果

 1 (function () {
 2     function anotherCreateFunction() {
 3         var arr = []
 4 
 5         for (var index = 0; index < 10; index++) {
 6             arr[index] = (function (num) {
 7                 return function () {
 8                     return num
 9                 }
10             })(index)
11         }
12 
13         return arr
14     }
15 
16     var arr = anotherCreateFunction()
17 
18     for (var index = 0; index < arr.length; index++) {
19         var element = arr[index];
20         console.log(element()) // 1~10
21     }
22 })();
 
闭包中的this
  
  默认闭包中的this会指向window
 1 (function () {
 2     var name = 'Cheery'
 3     var thisObject = {
 4         name: 'Colyn',
 5         getNameFunc: function () {
 6             return function () {
 7                 return this.name
 8             }
 9         }
10     };
11 
12     console.log(thisObject.getNameFunc()()) // Cheery
13 })();
 如果想要在闭包中访问父级函数的作用域,可以通过提前将this的值保存起来,以达到预期效果
 1 (function () {
 2     var name = 'Cheery'
 3     var thisObject = {
 4         name: 'Colyn',
 5         getNameFunc: function () {
 6             var that = this
 7             return function () {
 8                 return that.name
 9             }
10         }
11     };
12 
13     console.log(thisObject.getNameFunc()()) // Colyn
14 })();
闭包的内存泄漏问题
  • IE9之前的javascript和COM对象的回收机制不同
  • 如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁
下面这个方法,只要匿名函数存在,element的引用数至少是1,因此它所占用的内存就永远不会被回收
1 (function () {
2     function assignHandler() {
3         var element = document.getElementById('someElement')
4         element.click = function () {
5             alert(element.id)
6         }
7     }
8 })();
解决办法
  • 将闭包中要用到的变量提取到父级作用域链中
  • 销毁element
 1 (function () {
 2     function anotherAssignHandler() {
 3         var element = document.getElementById('someElement')
 4         // 重点1
 5         var id = element.id
 6         element.click = function () {
 7             alert(id)
 8         }
 9 
10         // 重点2
11         element = null
12     }
13 })();
模仿块级作用域
 
以下函数的index会存在于整个函数
1 (function () {
2     function blockFunction() {
3         for (var index = 0; index < 10; index++) {
4 
5         }
6 
7         console.log(index) //10
8     }
9 })();

想要解决变量污染的问题,可以创建一个立即执行的闭包来解决

 1 (function () {
 2     function anotherBlockFunction() {
 3         (function () {
 4             for (var blockIndex = 0; blockIndex < 10; blockIndex++) {
 5 
 6             }
 7         })()
 8 
 9         console.log(blockIndex) //Uncaught ReferenceError: blockIndex is not defined
10     }
11 })();
 
特权方法:指的是有权访问私有变量的共有方法
 
方式一:每个实例独享变量
 
 1 (function () {
 2     function PrivilegeFunction(name) {
 3         var privateName = name || 'Cheery'
 4 
 5         this.publicSetName = function (name) {
 6             privateName = name
 7         }
 8 
 9 
10         this.publicGetName = function () {
11             return privateName
12         }
13     }
14 
15     var p1 = new PrivilegeFunction
16     var p2 = new PrivilegeFunction
17 
18     console.log(p1.publicGetName()) // Cheery
19     console.log(p2.publicGetName()) // Cheery
20 
21     p1.publicSetName('Colyn')
22 
23     console.log(p1.publicGetName()) // Colyn
24     console.log(p2.publicGetName()) // Cheery
25 })();
方式二:实例间共享变量
 1 (function () {
 2     var staticPrivateName
 3 
 4     function StaticPrivilegeFunction(name) {
 5         staticPrivateName = name || 'Cheery'
 6     }
 7 
 8     StaticPrivilegeFunction.prototype.publicSetName = function (name) {
 9         staticPrivateName = name
10     }
11 
12     StaticPrivilegeFunction.prototype.publicGetName = function () {
13         return staticPrivateName
14     }
15 
16     var p1 = new StaticPrivilegeFunction
17     var p2 = new StaticPrivilegeFunction
18 
19     console.log(p1.publicGetName()) // Cheery
20     console.log(p2.publicGetName()) // Cheery
21 
22     p1.publicSetName('Colyn')
23 
24     console.log(p1.publicGetName()) // Colyn
25     console.log(p2.publicGetName()) // Colyn
26 })();
原文地址:https://www.cnblogs.com/ch11ry/p/7501233.html