资讯专栏INFORMATION COLUMN

前端学习笔记之闭包——看了一张图终于明白啥是闭包了

andycall / 3400人阅读

摘要:在一个闭包环境内修改变量值,不会影响另一个闭包中的变量。直到看到函数闭包闭包这篇文章的代码一部分,终于明白其中的逻辑了。

闭包

闭包定义:指拥有多个变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
函数内部可以直接读取全局变量。
函数内部变量无法在函数外部访问。
函数内部声明要用var或者let声明,不然会变成全局变量
链式作用域:子对象会一级级向上寻找父对象的变量,父对象的变量子对象都是可见的,反之则不行。
在一个闭包环境内修改变量值,不会影响另一个闭包中的变量。
普通的函数内嵌,内部函数是先执行;而闭包则是:先把内部函数赋给外部函数,然后在执行。

下面这段代码就是一根典型的闭包

function f1(){
    var a = 10;
    function f2(){
        alert(a);
    }
    f2();    //①
}
f1();        //10  ②

f1f1()的区别不加括号是代码,加()是执行这段代码,加return是返回一个值,可以把返回的值赋值给变量,不加return默认返回undefined

所以处有三种写法:

第一种:处写f2();处调用需要这样写f1();。具体执行过程:f1体内调用f2函数,并执行。

第二种:处写return f2();处调用需这样写f1();。具体执行过程:同上;区别是多了个return,因为现在f2函数中没有返回值,所以f1在调用f2只是执行一下alert(a)f1的返回值是undefined

第三种:处写return f2;处调用需这样写f1()();。这里返回的是f2函数的代码,所以在调用f1时要加上2个括号,第一个括号是执行f1函数,第2个括号是执行f2函数,如果处省略return会报错。

return和函数调用时是否加括号的意思都明白,但是把它俩结合起来,就搞不清了。

正好今天学闭包时碰上了,顺便就把它搞清楚了。

到底什么是闭包

对于新人(当然了是说我了),看很多闭包的定义,代码,还是不知啥是闭包,云里雾里的,这里感谢方方老师的文章JS 中的闭包是什么?,看完后,虽然还是说不出啥是闭包,但现在已经知道啥是闭包了,果然用图说话最牛逼。(图在文章中,我就不放出来了)

闭包的应用

MDN 上这个例子也写的很好

调用Counter.value()时,返回的是Counter内部的变量privateCounter
increment内部没有返回值,这个方法只是执行了privateCounter + 1操作,没有返回值;
同理decrement是将privateCounter - 1,也没有返回值;
所以执行Counter.increment,会返回undefined,但是接着操作Counter.value()时就可以得到1,因为执行上一步Counter.incrementprivateCounter+1了。

今天在下面三段代码上花费了大量的时间,一直似懂非懂,心里不踏实。

代码一:

var name = "window";
var obj = {
    name: "object",
    getName: function() {
        return this.name;
    }
};
obj.getName(); //object
(obj.getName = obj.getName)(); //window 非严格模式下

代码二:

var name = "window";
var obj = {
    name: "object",
    getName: function() {
        return function(){
            return this.name;    
        }
    }
};
obj.getName()(); //window

代码三:

var name = "window";
  var obj = {
    name : "object",
    getName : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
obj.getName()();    //object

今天在看阮一峰的博客学习Javascript闭包(Closure),对代码二、代码三部分很是不解,看到一网友搬出犀牛书(还没看过,我买了红宝石书才看了一点点)里的话,实在不解什么是作为函数调用,什么是作为方法调用;

《Javascript权威指南》上说:如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下); 如果嵌套函数作为方法调用,其this值指向调用它的对象。

又有一位网友说

每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量(这一点通过前面的图可以看得更清楚)。意思就是说找到匿名函数中的this和arguments就不会再往下找了(这里的往下指的是外层的包含函数,和最外层的window全局环境),而匿名函数的this对象通常指向window,所以输出的是全局的那个字符串。不过,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

看到这里大概明白匿名函数的作用域是全局,继续翻看下面评论,大概意思是说“把this保存在obj作用域下的一个变量中,this就在当前函数的作用域下了”。直到看完也是,似懂非懂,反正就是感觉哪里不对劲,但也说不上了。

直到看到【JavaScript】【函数】闭包闭包!这篇文章的代码一部分,终于明白其中的逻辑了。

下面就来分析其中的逻辑,我分析的方法就是把不懂的地方一个个用console打印出来

代码二和代码一的区别是多了一层嵌套函数,this值就不一样了。
ps:我一开始以为在代码二中再嵌套一层函数,就会打印出object =_=|||

先来看代码一,明白之后,另外两段代码自然就懂了。

为什么obj.getName()打印出来的是object,因为这时getName方法是在obj的作用域下,所以this指向obj,返回值当然就是object了。

接着看(obj.getName = obj.getName)()删掉右边后,打印出的结果变成了object,这就纳闷了。
ps:第一眼看上去,这啥玩意,把自己赋值给自己?这不是多此一举,直接用不就行了!

console.log打印出obj.getName后,终于拨云见天,obj.getName = obj.getName这句话的意思就是把getName函数赋值给自己,这个时候就不是obj.getName,而是getName匿名函数了,匿名函数通常用的方法是()()立即执行,此时再看匿名函数已经脱离obj了,当然this也就指向了全局,打印出window

再来看代码二,用console打印出obj.getName()会发现是一个匿名函数,而匿名函数的this通常会指向全局,所以也就不难理解了

理解上面两段代码,代码三也就很好理解了。

闭包中引用循环变量

廖雪峰的闭包在文中就很形象的讲解了函数中的引用会变化的变量会有什么后果,我节选了他的结论和代码。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

这里的核心就是立即执行,如果不是立即执行的话,变量i就是for循环结束后的值了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95041.html

相关文章

  • Koa源码阅读笔记(4) -- ctx对象

    摘要:本笔记共四篇源码阅读笔记源码阅读笔记源码阅读笔记服务器启动与请求处理源码阅读笔记对象起因前两天终于把自己一直想读的源代码读了一遍。首先放上关键的源代码在上一篇源码阅读笔记服务器启动与请求处理中,我们已经分析了的作用。 本笔记共四篇Koa源码阅读笔记(1) -- coKoa源码阅读笔记(2) -- composeKoa源码阅读笔记(3) -- 服务器の启动与请求处理Koa源码阅读笔记(4...

    ityouknow 评论0 收藏0
  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0
  • Python

    摘要:最近看前端都展开了几场而我大知乎最热语言还没有相关。有关书籍的介绍,大部分截取自是官方介绍。但从开始,标准库为我们提供了模块,它提供了和两个类,实现了对和的进一步抽象,对编写线程池进程池提供了直接的支持。 《流畅的python》阅读笔记 《流畅的python》是一本适合python进阶的书, 里面介绍的基本都是高级的python用法. 对于初学python的人来说, 基础大概也就够用了...

    dailybird 评论0 收藏0
  • 前端补集 - 收藏集 - 掘金

    摘要:原文地址一个非常适合入门学习的博客项目前端掘金一个非常适合入门学习的项目,代码清晰结构合理新闻前端掘金介绍一个由编写的新闻。深入浅出读书笔记知乎专栏前端专栏前端掘金去年的一篇老文章,恰好今天专栏开通,迁移过来。 破解前端面试(80% 应聘者不及格系列):从闭包说起 - 掘金修订说明:发布《80% 应聘者都不及格的 JS 面试题》之后,全网阅读量超过 6W,在知乎、掘金、cnodejs ...

    YorkChen 评论0 收藏0
  • 前端补集 - 收藏集 - 掘金

    摘要:原文地址一个非常适合入门学习的博客项目前端掘金一个非常适合入门学习的项目,代码清晰结构合理新闻前端掘金介绍一个由编写的新闻。深入浅出读书笔记知乎专栏前端专栏前端掘金去年的一篇老文章,恰好今天专栏开通,迁移过来。 破解前端面试(80% 应聘者不及格系列):从闭包说起 - 掘金修订说明:发布《80% 应聘者都不及格的 JS 面试题》之后,全网阅读量超过 6W,在知乎、掘金、cnodejs ...

    AbnerMing 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<