闭包

闭包

  • 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量参考链接
  • 前文了解了,预编译的过程,了解了作用域和作用域链。都为闭包准备的基础
  • 当内部函数被返回到外部并保存时一定会产生闭包,闭包会产生原来的作用域链不释放,过渡的闭包可能会导致内存的泄露,或加载过慢。
  • 通过作用域链的图解分析来说明不释放问题
function test1() {
    function test2() {
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2()
}
var c = 3;
var test3 = test1();
test3();
/*
当test1函数被定义时,系统生成[[scope]]属性,[[scope]]保存该函数的作用域链,该最作用链的第0位存储当前环境下的全局执行上下文GO. GO中存储全局下的所有对象
*/
函数test1“”
[[scope]]Scope Chain 作用域链

Scope Chain

Scope Chain作用域链“”
0GO

GO 全局执行上下文

GO全局执行上下文“”
thiswindow
windowobject
documentobject
test1function
c3
test3function
/*
当test1函数执行时(前一刻)函数test2被定义
*/
函数test1“”
[[scope]]Scope Chain 作用域链

test1 Scope Chain

Scope Chain作用域链“”
0test1的AO
1GO

test1的AO

函数test1的AO函数执行期上下文“”
thiswindow
arguments[]
test2function
a1

test2被定义

函数test2“”
[[scope]]Scope Chain 作用域链

test2 Scope Chain

Scope Chain作用域链“”
0test1的AO
1GO

GO 全局执行上下文

GO全局执行上下文“”
thiswindow
windowobject
documentobject
test1function
c3
test3function
/*
当test1函数被执行结束时,因为test2被返回到外部,且被全局变量test3接收,这时test1的AO并没有销毁,test2的作用域链中还连着
*/
函数test1“”
[[scope]]Scope Chain 作用域链

test1 Scope Chain

Scope Chain作用域链“”
0test1的AO 这个关联被销毁
1GO

test1的AO

函数test1的AO函数执行期上下文“”
thiswindow
arguments[]
test2function
a1

test2被定义

函数test2“”
[[scope]]Scope Chain 作用域链

test2 Scope Chain

Scope Chain作用域链“”
0test1的AO
1GO

GO 全局执行上下文

GO全局执行上下文“”
thiswindow
windowobject
documentobject
test1function
c3
test3function
/*
全局的test3接收了test2.test3执行时,test2的作用域链增加了自己的AO,当打印a时在自己的AO上没有找到,则向test1的AO上查找,再次执行test3时,实际操作的仍然是原来的test1的AO
*/
函数test1“”
[[scope]]Scope Chain 作用域链

test1 Scope Chain

Scope Chain作用域链“”
0test1的AO 这个关联被销毁
1GO

test1的AO

函数test1的AO函数执行期上下文“”
thiswindow
arguments[]
test2function
a1

test2执行

函数test2“”
[[scope]]Scope Chain 作用域链

test2 Scope Chain

Scope Chain作用域链“”
0test2的AO
1test1的AO
2GO

test2的AO

Scope Chain作用域链“”
thiswindow
arguments[]
b2

GO 全局执行上下文

GO全局执行上下文“”
thiswindow
windowobject
documentobject
test1function
c3
test3function(test2)
/*
当test3执行结束后,test2的AO被销毁,但是原来的原来test1的AO仍然存在
*/
函数test1“”
[[scope]]Scope Chain 作用域链

test1 Scope Chain

Scope Chain作用域链“”
0test1的AO 这个关联被销毁
1GO

test1的AO

函数test1的AO函数执行期上下文“”
thiswindow
arguments[]
test2function
a1

test3执行结束

函数test2“”
[[scope]]Scope Chain 作用域链

test2 Scope Chain

Scope Chain作用域链“”
0test2的AO 这个AO被销毁
1test1的AO test1的仍然存在
2GO

test2的AO

Scope Chain作用域链“”
thiswindow
arguments[]
b2

GO 全局执行上下文

GO全局执行上下文“”
thiswindow
windowobject
documentobject
test1function
c3
test3function(test2)
闭包的用例
// 实例一
function test (num) {
    var bNum = arguments[0] || 10;
    function supply () {
        bNum += 10;
        console.log(bNum)
    }
    function sale () {
        bNum--;
        console.log(bNum);
    }
    return [supply,sale]
}
var bArray = test()
bArray[0]()
bArray[1]()
// 实例二
function test (num) {
    var bNum = arguments[0] || 10;
    var obj = {
        supply: function () {
            bNum += 10;
            console.log(bNum)
        },
        sale: function () {
            bNum--;
            console.log(bNum)
        }
    }
    return obj
}
var obj = test()
obj.supply()
obj.sale()
闭包原理解析
function test() {
    var arr = []
    for (var i = 0; i < 10; i++) {
        arr[i] = functioin() {
            console.log(i)
        }
    }
    return arr;
}
var myArr = test()
for (var j = 0; j < 10; j++) {
    myArr[j]()
}
/*
答案是 10个10
function test() {
    var arr = []
    var i = 0;
    for (; i < 10;) {
        arr[i] = function() {
            console.log(i)
        }
    }
    i++
    return arr;
}
这里arr里面存储的函数形成了闭包,函数读取的值,test作用域中的值。这个问题的解释要用GO和AO上去解释。参照上面的图解
*/
// 修改方案一
问题产生的原因是,数组中保存的函数在执行时读取了test中的i,这个i在经过循环之后被修改为10了。所以要想办法保留住每次循环得到的i
function test() {
    var arr = []
    for (var i = 0; i < 10; i++) {
      (function(j){
        arr[j] = function() {
            console.log(j)
        }
      })(i)
    }
    return arr;
}
var myArr = test()
for (var j = 0; j < 10; j++) {
    myArr[j]()
}
// 修改方案二
// 这个地方是因为在ES6 中let 存在了块级作用域
function test() {
    let arr = []
    for (let i = 0; i < 10; i++) {
      arr[i] = function() {
            console.log(i)
        }
    }
    return arr;
}
let myArr = test()
for (let j = 0; j < 10; j++) {
    myArr[j]()
}

实例在开发中的应用举例

// 给ur中的li添加点击事件
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
    <title>test</title>
</head>
<body>
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
</body>
<script type="text/javascript">
    var oLis = document.querySelectorAll('li')
    for (var i = 0; i < oLis.length; i++) {
        (function(j){
          oLis[j].onclick = function() {
            console.log(j)
          }
        })(i)
    }
</script>
</html>
闭包比较常见的写法
function Persion (name) {
    this.pname = name;
}
Persion.age = 11;
Persion.protoptype.say = function () {
    console.log('hello,world')
}
var p = new Persion('ww')
p.say()
var Persion = function () {
    var obj = new Object()
    obj.aage = 11;
    obj.say = function () {
        console.log('say')
    }
    return obj;
}
var p = Persion()
p.say()
var Persion =  {
   aage: 11,
   say: function () {
        console.log('say')
    }
}
Persion.say()
闭包的用途

结果的缓存

/*
我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留
*/
var CachedSearchBox = (function(){    
    var cache = {},    
       count = [];    
    return {    
       attachSearchBox : function(dsid){    
           if(dsid in cache){//如果结果在缓存中    
              return cache[dsid];//直接返回缓存中的对象    
           }    
           var fsb = new uikit.webctrl.SearchBox(dsid);//新建    
           cache[dsid] = fsb;//更新缓存    
           if(count.length > 100){//保正缓存的大小<=100    
              delete cache[count.shift()];    
           }    
           return fsb;          
       },    
       clearSearchBox : function(dsid){    
           if(dsid in cache){    
              cache[dsid].clearSelection();      
           }    
       }    
    };    
})();    
CachedSearchBox.attachSearchBox("input");   

用闭包模拟私有方法

var Persion = (function(){
    var age = 0
    function realAge (val) {
        age += val
        console.log('age',age)
    }
    return {
        addAge: function (age) {
            realAge(age)
        },
        decAge: function (age) {
            realAge(age)
        }
    }
})()
Persion.addAge(10) // 10
Persion.decAge(2) // 12
console.log('persion.age',Persion.age) // undefined
var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
/*
请注意两个计数器 counter1 和 counter2 是如何维护它们各自的独立性的。每个闭包都是引用自己词法作用域内的变量 privateCounter 。
每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值