闭包的应用场景

安稳与你 提交于 2019-11-30 22:43:43
123456789
var scope = "global scope";function () {    var scope = "local scope";    function f() {        return scope;    }    return f();}checkScope();   

代码执行过程分析

checkScope被invoke(调用)时,return f(),运行内部嵌套函数f,f沿着作用域链从内向外寻找变量scope,找到“local scope”,停止寻找,因此,函数返回 “local scope”;

代码稍作修改:

123456789
var scope = "global scope";function () {    var scope = "local scope";    function f() {        return scope;    }    return f;}checkScope()();   //=> "这次返回什么?"

代码执行过程分析

checkScope被invoke时,将内部嵌套的函数f返回,因此checkScope()()这句执行时,其实运行的是f(), f函数返回scope变量,在这种情况下,f会从哪个作用域里去寻找变量scope呢?

词法作用域的基础规则:函数被执行时(executed)使用的作用域链(scope chain)是被定义时的scope chain,而不是执行时的scope chain

嵌套函数f(), 被定义时,所在的作用域链中,变量scope是被绑定的值是“local scope”,而不是”global scope”,因此,以上代码的结果是啥?没错,是”local scope”!

这就是闭包的神奇特性:闭包可以捕获到局部变量和参数的外部函数绑定,即便外部函数的调用已经结束。

闭包的使用场景

通过循环给页面上多个dom节点绑定事件

场景描述:假如页面上有5个button,要给button绑定onclick事件,点击的时候,弹出对应button的索引编号

12345678910111213
<html><head>     <meta charset="UTF-8"></head><body>    <button>Button0</button>    <button>Button1</button>    <button>Button2</button>    <button>Button3</button>    <button>Button4</button></body></html>

先上一手 for 循环:

123456
var btns = document.getElementsByTagName('button');for(var i = 0, len = btns.length; i < len; i++) {    btns[i].onclick = function() {        alert(i);    }}

执行代码,发现不论点击那个 button,均 alert 5。因为,onclick事件是被异步触发的,当事件被触发时,for循环早已结束,此时变量 i 的值已经是 5 。所以,当onlick事件函数顺着作用域链从内向外查找变量 i 时,找到的值总是 5 。

那么怎么循环给每个 button 添加点击事件,并且 alert 出不同的值呢?这就要使用到“闭包”。

在闭包的作用下,定义事件函数的时候,每次循环的i值都被封闭起来,这样在函数执行时,会查找定义时的作用域链,这个作用域链里的i值是在每次循环中都被保留的,因此点击不同的button会alert出来不同的i。

代码如下:

1234567
for(var i = 0, len = btns.length; i < len; i++) {    (function(i) {        btns[i].onclick = function() {            alert(i);        }    }(i))}

在js中,没有块级作用域 ,只有函数作用域。可以采用“立即执行函数Immediately-Invoked Function Expression (IIFE)”的方式创建作用域。

使用闭包封装变量

闭包可以将一些不想暴露在全局的变量封装成“私有变量”。

假如有一个计算乘积的函数,mult函数接收一些number类型的参数,并返回乘积结果。为了提高函数性能,我们增加缓存机制,将之前计算过的结果缓存起来,下次遇到同样的参数,就可以直接返回结果,而不需要参与运算。这里,存放缓存结果的变量不需要暴露给外界,并且需要在函数运行结束后,仍然保存,所以可以采用闭包。

代码如下:

12345678910111213141516171819
var mult = (function(){    var cache = {};    var calculate = function() {        var a = 1;        for(var i = 0, len = arguments.length; i < len; i++) {            a = a * arguments[i];        }        return a;    }    return function() {        var args = Array.prototype.join.call(arguments, ',');        if(args in cache) {            return cache[args];        }        return cache[args] = calculate.apply(null, arguments);    }}())

通过闭包延续局部变量的寿命

img对象经常用于数据上报,如下:

12345
var report = function(src) {    var img = new Image();    img.src = src;}report('http://xxx.com/getUserInfo');

这段代码在运行时,发现在一些低版本浏览器上存在bug,会丢失部分数据上报,原因是img是report函数中的局部变量,当report函数调用结束后,img对象随即被销毁,而此时可能还没来得及发出http请求,所以此次请求就会丢失。

因此,我们使用闭包把img对象封闭起来,就可以解决数据丢失的问题:

12345678
var report = (function() {    var imgs = [];    return function(src) {        var img = new Image();        imgs.push(img);        img.src = src;    }}())
  • 作者:佩吉秋
  • 链接:原文地址
  • 來源:简书
  • 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原文:大专栏  闭包的应用场景


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!