js中的闭包

首先,js闭包对于哪怕是有很多年前端开发经验的人,也是很晦涩难懂的东西.

所以,我不敢保证能把你说明白,但是如果你有兴趣可以接着看下去.

再说之前,咱们先探讨一个现象.就是闭包在前端开发面试过程中的出现率几乎是100%,很多同学一定会纳闷这是为啥.

要想明白这个,我们可以换个角度去思考,那就是如果你是一个公司的前端开发,此时你的上司交派给你一个任务,去面试即将到来的一大波前端面试者.你该如何去面?

此时的你,一定迫切的想要用最少的题目,去考量一个前端面试者尽可能多的技术能力.因为这样会节省很多时间和精力.还能达到考核目的.

那么什么是前端开发的核心呢?

javaScript

javaScript的核心又是什么呢?

函数

为什么我说javaScript的核心是函数呢?

我们都知道js是一门面向过程的语言,什么是面向过程呢?面向过程就是基于很多函数功能模块的执行来实现各种各样功能的概念

函数的核心又是什么?

1.作用域
2.作用域链
3.活动对象
4.执行环境
5.浏览器的垃圾回收机制

这5个东西并不是孤立存在的,他们之间有着很强的依赖关系.

先来说说作用域,提到作用域就必须提到另外一个东西,那就是变量,我前面的文章已经具体说了什么是变量,在这我只概括一下,作用域就是变量存储的规则,变量是以作用域为单位进行存储的.作用域可以相互嵌套,但是不可以重叠

可以相互嵌套,不可以重叠

作用域链,是变量被访问的规则,作用域链的访问是单向的,只能从下向上访问,不能从上向下访问.这也就是为什么外部作用域不能访问内部作用域的变量,内部作用域可以访问外部作用域的变量.
作用域链产生的前提条件是作用域,没有作用域就没有作用域链.

好想养条狗

作用域链这个名字很形象,我们知道栓狗用的铁链吧,铁链是由一个个铁扣组成,一条铁链我们可以拆卸下一节,也可以增加一节,每一个铁扣就是我们即将要说到活动对象,每当函数执行前,都会产生一个活动对象,这个活动对象(铁扣)会与外部作用域的活动对象(铁扣)进行对接,此时的内部作用域就包含了2个活动对象,所以变量被访问实际上是依靠作用域链上的活动对象实现的,内部作用域当函数执行完毕就会销毁,同时销毁的还有此作用域生成的活动对象,这也是造成了外部作用域不能访问内部作用域变量的原因

活动对象(也称之为变量对象)是在函数执行之前函数初始化的过程中产生的一个对象,这个对象包含了函数中的所有变量,这个所有变量包括:
Ⅰ.用var声明的变量
Ⅱ.声明的函数
Ⅲ.arguments对象(也就是形参)

活动对象的生成

执行环境,就是当执行流(执行流就是代码执行的动作过程)进入到一个新的作用域(函数)时产生的一个新的环境,这个环境中同时会产生一个执行对象和一个this,this指向的就是新生成的执行对象,而不是当前执行环境,换句话说,执行环境只是一个概念,执行对象是这个概念的载体,js解释器需要知道当前的执行任务到底是属于谁的,因为它要确定这个函数到底是为谁服务.

this与执行对象是成对出现的

<script>
    function fn(){  
        function sonFn(){
            console.log(this)
        }
        var o ={
            fn:function(){
                console.log(this)
            }
        }
        var g = o.fn;
        
        console.log(this);
        sonFn();
        o.fn();
        g();
    }
    fn()
</script>

注意:执行环境分为
Ⅰ.全局执行环境,它是基于Global对象的
Ⅱ.局部执行环境,他是基于函数的.

垃圾回收机制,在C语言中,垃圾回收是要由程序员自己来手动完成的,而例如java,jiavascript之流的语言,是不需要程序员来操心这些事的.

简单来说,垃圾回收就如同我们平时把果皮纸屑等这些对于你来说没有使用意义的东西丢进垃圾箱一样,而浏览器的垃圾回收机制相当于你在家里请了一个保姆,你只需要制造垃圾,而把垃圾扔到垃圾箱的事是由这个保姆来完成.

垃圾回收机制与变量也是密不可分的,在变量一章已经说过,这里不再阐述

巴拉巴拉说了这么多,完全是为闭包做铺垫

什么是闭包

很多人在看关于闭包网络文章很容易乱,原因就是解释的人其实在定义闭包的概念上就有分歧,而造成的这种分歧的主要原因就是各类JS权威书籍上对于闭包的解释不尽相同


两本流传的javascript神书

《JavaScript权威指南》对于闭包的解释是:

闭包是指在函数声明时的作用域以外的地方被调用的函数
 <script>
    function fn(){
        var i = 0;
        return function(){
            console.log(++i)
        }
    }
    var a = fn();
    a();
</script>

概括为三点:
Ⅰ.访问所在作用域;
Ⅱ.函数嵌套;
Ⅲ.在所在作用域外被调用;

《JavaScript高级程序设计》对于闭包的解释是:

闭包是指有权访问另一个函数作用域中的变量的函数
<script>
    function fn(){
        var i = 0;
        function fn2(){
            console.log(++i)
        }
        fn2();
    }

    fn();
</script>

概括为二点:
Ⅰ.访问所在作用域;
Ⅱ.函数嵌套;

两本书之所以会定义有差异,其实还要从两本书的作者写书的出发点考虑

《JavaScript权威指南》与读起来枯燥有难度,需要有一定的编程思想,不太适合新手阅读,作者针对的人群是致力于深度钻研javascript的开发人员.

《JavaScript高级程序设计》只能说相比于《JavaScript权威指南》难度有所降低,比较亲民,适合新手阅读,作者针对的人群是没有什么编程基础或没有计算机基础的javascript的开发人员或小白.

在《JavaScript高级程序设计》的序言中,有这么一段话让我印象深刻,

与那些把大量篇幅花在讲解背景知识上的书,以及那些让人感觉好像是要使用 JavaScript 开发导弹制导系统的书相比,这本书让人感觉细致周到、亲切自然。

这段序言调侃的对象就包括了《JavaScript权威指南》,不好意思,我无耻的笑了

虽然我无比喜欢《JavaScript高级程序设计》,但是我们还要以《JavaScript权威指南》的闭包定义开展,因为这是面试官想要的,也是我们应该追求的.(前提是你不想转行又想拿高薪.)

我们系统阐述一下这段代码的整个过程:

 <script>
    function fn(){
        var i = 0;
        return function(){
            var  b = 55;
            console.log(++i)
        }
    }
    var a = fn();
    a();
</script>
当浏览器中js解释器读到js代码的时候,他首先会创建一个全局的执行环境,这个全局执行环境包含了一个活动对象,这个活动对象包含了所有的全局变量(我们一定要记住函数本身是变量),然后在词法分析阶段,会为每一个全局函数导入这个全局活动对象,这也就造成了全局函数可以再整个全局环境的任何地方进行调用,全局执行环境的生命周期是在打开网页时到关闭网页时.

此时的fn函数包含了一个全局活动对象

在函数执行时,又会执行一遍此法语法分析,全局活动对象会与fn执行环境的活动对象进行对接,使得内部函数拥有两个活动对象,全局的,和自身的

此时的 return function(){console.log(++i)}拥有全局活动对象fn函数,和fn函数内部活动对象 i的访问权,也就是 a函数所拥有的的访问权限

在函数a执行时,他就拥有了三个活动对象
Ⅰ.全局活动对象;
Ⅱ.fn执行环境的活动对象;
Ⅲ.当前执行环境的活动对象;

这一实现阻断了垃圾回收机制,因为并没有明确的指向证明fn函数已经执行完毕,这就造成这三个活动对象一直保存在内存中而无法释放,而这也就造成了另外一个问题,内存泄漏

一般情况下,普通的开发不会涉及太多的闭包,如果你大量的使用了闭包,那么就需要手动清理一下,清理的方法也非常简单

var a = null;

闭包的应用

1.生成不重复的id

<script>
    var a = (function(){
        var a = 'a',b = 0;
        return function(){
            return a + b++;
        }
    })()

    var b = a();
    var c = a();
    console.log(b,c)
</script>

2.生成排序规则函数的函数

<script>
    var json = [{age:55},{age:14},{age:78},{age:66}];

    function createComparisonFunction(propertyName) {

        return function(object1, object2){
            var value1 = object1[propertyName];
            var value2 = object2[propertyName];

            return value1 - value2;
        };
    } 
    json.sort(createComparisonFunction('age'));
    console.log(json)
</script>

3.在循环中为dom绑定不同函数

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <script>
        var list = document.getElementsByTagName('button');

        for(var i =0;i<list.length;i++){
            list[i].onclick = (function(i){
                return function(){
                    console.log(i)
                }           
            })(i)
        }
                
    </script>
</body>

当然也可以使用ES6的let来解决

        for(let i =0;i<list.length;i++){
            list[i].onclick = function(){
                console.log(i)
            }
        }

4.创建单例模式

<script>
    var Person = function(){
        var obj = null;
        return function(name, age){
            if(obj !== null){
                return obj
            }
            obj = this;
            this.name = name;
            this.age = age;
        }
    }();

    var a = new Person('张三', 56); //{name: '张三',age: 56}
    var b = new Person('李四', 25);   //{name: '张三',age: 56}
</script>

这次明白面试官为什么要问闭包了吗?

如果你还是简单的回答:
"闭包就是外部作用域有权访问内部作用域的变量"的话,面试官会青睐于你吗?

此篇文章不定期更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • 在上一篇文章“执行环境和作用域”中,我试着梳理了执行环境和作用域的关系。但实际上,文章中并没有提到作用域,而是介绍...
    海痕阅读 241评论 0 0
  • 1. 定义 一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数), 因而这些变量也是该表达式的一部分....
    寒枫Alex阅读 259评论 0 0
  • 前言 对于js中的闭包,无论是老司机还是小白,我想,见得不能再多了,然而有时三言两语却很难说得明白,反正在我初学时...
    itclanCoder阅读 4,192评论 1 11
  • 不难发现,身边人会有对自己人要求苛刻,但是对外人又非常注意方式…… 经过思考,发现背后有三个原因: 一、感觉自己人...
    国仕空间阅读 266评论 0 0
  • 11、编写一个二分查找函数,下界为low,上界为high 递归法: 非递归法: 注意:二分查找算法前提是已经排好序...
    cpp加油站阅读 1,139评论 1 8