组团学

ES5进阶-闭包

阅读 (474)

一、作用域链

执行环境对象属性,在一个作用域生成,就会把对象放到这个链中,以栈的形式

//全局作用域 var a = 1; function f(){//局部作用域f var b = 2; function n(){//局部作用域n var c = 3; console.log(a + b + c); } n(); } f();

作用域链.png

二、利用闭包突破作用域链

  • 闭包的概念

    在函数体中定义的内部函数,并且使用了外部函数的变量,然后把内部函数给返回,那么这个内部函数就是闭包

  • 通俗理解

    别人家有某个东西,你想拿到但是因为权限不够(不打死你才怪),但是你可以跟那个家里的孩子套近乎,通过他拿到。这个家就是局部作用域,外部无法访问内部变量,孩子是返回对象,对家里的东西有访问权限,借助返回对象间接访问内部变量!

    通俗理解闭包.png

  • 代码示例

    var a = 1; function f(){ var b = 2; function n(){ return b; } return n; } nn = f(); console.log(nn());
    // 与上面的相同,只是在实现方法上存在一些细微的不同 var nn = null; var a = 1; function f(){ var b = 2; function n(){ return b; } nn = n; } f(); console.log(nn());

三、闭包的优缺点

  • 优点

    避免污染全局环境,这样我们就可以在函数体外使用函数体中定义的变量

  • 缺点

    数据长期驻留在内存中,造成内存极大浪费,在IE浏览器上,特别容易崩溃,所以使用闭包需谨慎

四、闭包的用途(模块化代码)

作用:将属性私有化,避免污染全局环境

var scope = (function(){ var secret = 100 return { getSecret: function(){ return secret; }, setSecret: function(value){ secret = value } } })() console.log(scope.getSecret()); scope.setSecret(10); console.log(scope.getSecret());

五、闭包的用途(循环中找到索引)

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> .box{ width: 100px; height: 100px; } #box1{ background-color: red; } #box2{ background-color: yellow; } #box3{ background-color: green; } </style> </head> <body> <div id="box1" class="box">0</div> <div id="box2" class="box">1</div> <div id="box3" class="box">2</div> <script type="text/javascript"> var jsBoxs = document.getElementsByClassName("box"); for (var i = 0; i < jsBoxs.length; i++){ jsBoxs[i].onclick = function(){ alert(i); } } </script> </body> </html>

原因:每个DIV添加一个点击事件,每个事件函数中都引用了一个共同的变量i,它们所拥有的只是相关域在创建时的一个引用,此时变量i恰巧存在于定义这三个事件函数的域中。对这三个事件函数中任何一个而言,当它要去获取某个变量时,它会从其所在的域开始逐级寻找那个距离最近的i值,由于循环结束时i的值为3,所以这三个函数都指向了一个共同值

使用闭包解决问题:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> .box{ width: 100px; height: 100px; } #box1{ background-color: red; } #box2{ background-color: yellow; } #box3{ background-color: green; } </style> </head> <body> <div id="box1" class="box">0</div> <div id="box2" class="box">1</div> <div id="box3" class="box">2</div> <script type="text/javascript"> var jsBoxs = document.getElementsByClassName("box"); for (var i = 0; i < jsBoxs.length; i++){ jsBoxs[i].onclick = (function(index){ function inner(){ alert(index); } return inner; })(i) } </script> </body> </html>

另一种写法

var jsBoxs = document.getElementsByClassName("box"); for (var i = 0; i < jsBoxs.length; i++){ (function(index){ jsBoxs[index].onclick = function(){ alert(index); } })(i) }

推荐使用:给对象额外添加属性保存需要的信息

var jsBoxs = document.getElementsByClassName("box"); for (var i = 0; i < jsBoxs.length; i++){ // 给标签添加一个额外的属性 jsBoxs[i].index = i; jsBoxs[i].onclick = function(){ alert(this.index); } }

六、闭包的用途(生成器)

使用数据长期驻留在内存中这个特性可以实现生成器机制

生成器:受到内存限制,数组容量肯定是有限的。而且,创建一个包含100万个元素的数组,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果数组元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的数组,从而节省大量的空间。这种一边循环一边计算的机制,称为生成器机制

使用场景:都知道如何用循环遍历一个简单的数组,但是有时候我们需要面对更为复杂的数据结构,它们通常会有着与数组截然不同的序列规则。这时候就需要将一些“谁是下一个”的复杂逻辑封装成易于使用的next()函数。然后,我们只需要简单地调用next()函数就能实现相关的遍历操作

需求:从1开始无限制打印数字的平方值

function numSquare(){ var i = 1; function inner(){ num = i++ return num * num; } return inner; } var next = numSquare(); console.log(next()); console.log(next()); console.log(next()); console.log(next());
需要 登录 才可以提问哦