作用域、作用域链和闭包那些事

之所以写这篇文章,是跟我经历有关,前两天面试碰到一个很无理的面试官,年纪不大,电话面试,说话傲气,自称是写react和ts的。问道了闭包的概念,然后我回答闭包是前端比较容易混淆的概念,而且他的概念很多,阮一峰的定义是:。。。然后就让我跳过去了。。。

额。。。闭包概念多不多,下面我将进行逐次举例:

一、概念:先从带我入门的阮一峰大神开始吧:

1,阮一峰:我的理解是,闭包就是能够读取其他函数内部变量的函数。

2,廖雪峰

image

廖大神没有给明定义而是举了一个例子,简单概括来说:

即在外部函数内定义内部函数,当把内部函数返回时相关的参数和变量都会被保存在返回函数中。

3,MDN:闭包是函数和声明该函数的词法环境的组合。

4,红宝书(JS高级程序设计):有权访问另一个函数作用域中变量的函数。

5,某简书作者(思路非常有意思):

闭包是一种特殊的对象。

它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。

当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。

综上所述:概念真的很多。。。

但是概念多并不妨碍我们去理解闭包,先从作用域、作用域链开始解释吧:

二、作用域(scope):

变量作用域分为两种:全局变量和局部变量(函数作用域)

这两个概念前端都知道我不做多余解释。

三、作用域链(chain scope):

这里面涉及的概念有点多了:

1>基础数据类型与引用数据类型

2>内存空间

3>垃圾回收机制

4>执行上下文

5>变量对象与活动对象

但是可以通过一张图轻松看明白:

image

作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

简单来说就是:

当前执行的代码所在环境的变量对象(如果该环境是函数,则将其活动对象作为变量对象),下一个变量对象来自包含环境(包含当前执行环境的环境),下一个变量对象来自包含环境的环境,依次往上,直到全局执行环境的变量对象。全局执行环境的变量对象始终是作用域链中的最后一个对象。

标识符解析是沿着作用域一级一级的向上搜索标识符的过程。搜索过程始终是从作用域的前端逐地向后回溯,直到找到标识符(找不到,就会导致错误发生)。

作用域链决定了全局变量和局部变量(函数变量)的有限访问权。

看图说话:

image

这里还要引出另外两个概念:

四、提升:

1,变量提升:

看一段代码:

var name = "haha";

function changeName(){

       console.log(name)

       var name = "xixi";
}

changeName();

console.log(name);

输出结果是:undefined haha

为什么不是: haha或者xixi啊?

这个现象就是变量提升,就是把变量提升到函数的顶部,需要注意的是,变量提升只是提升变量的声明,不会吧变量的值也提升上来!

上述代码相当于:

var name="haha";

function changeName(){

     var name;

     console.log(name);

     name="xixi";

}

 changeName();

console.log(name);

2,函数提升:

在JavaScript中函数的创建方式有三种:函数声明(静态的,像函数example()的形式)、函数表达式(函数字面量)、函数构造法(动态的,匿名的)。

注意:只有函数声明才存在提升。

**//函数声明
function myTest1(){

    func();

    function func(){

        console.log("我可以被提升");

    }

}

myTest1();//函数表达式function myTest2(){

    func();

    var func =function(){

        console.log("我不能被提升");

    }

}

myTest2();

打印结果:

image

3,函数提升和变量提升的优先级:
函数提升要比变量提升的优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖。

示例:

console.log(a);// f a() {console.log(10)}

console.log(a());// undefined

var a =3;

function a(){

        console.log(10)//10

}

console.log(a)//3

a =6;

console.log(a());//a is not a function;

五、下面回到闭包的用途:

1,读取函数内部变量(函数作为返回值):

用法:通过函数返回值:

function f1(){

    var n=999;

      nAdd=function(){n+=1}

        function f2(){

                 alert(n);

           }

        return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

2,模拟一个块级作用域(函数作为参数被传递):

for(var i=1; i<=5; i++) {

        (function(i){

             setTimeout(function timer(){

             console.log(i);

         }, i*1000);

      })(i)

}

3,模拟私有方法(模块模式):

(function(){

    var a =10;

    var b =20;

        function add(num1, num2){

             var num1 = !!num1 ? num1 : a;

             var num2 = !!num2 ? num2 : b;

             return num1 + num2;

          }

window.add = add;

})();

add(10,20);

六、闭包的危害:

关于闭包的危害,倒是没什么可争论的:

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。(MDN)

阮一峰:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
----------------一个朋友跟我说父函数看不懂,可以理解成函数外部的。

PS:

作为一个老司机,还是忍不住BB两句,那个自称会写TS的前端小朋友,谦虚点没什么坏处,前端的技术更新很快,但是对经典的学习,是每个开发者必须要走完的路!!!

前端水很深,且行且珍惜,保持一颗学习心,一颗愿意交流的心,比什么都重要!

Be Gentle! && Stay Hungry Stay Foolish!

PPS:

简书附上代码还是第一次,如果引起不适,请多担待,听朋友的话,把富文本改成了Markdown。一时还不太习惯,以后慢慢改进好了!

最后附上学习链接:

廖雪峰官网:

https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00143449934543461c9d5dfeeb848f5b72bd012e1113d15000

阮一峰:

http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

MDN:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

某简书作者:

//www.greatytc.com/p/21a16d44f150

其他:
https://blog.csdn.net/whd526/article/details/70990994

https://www.cnblogs.com/wangfupeng1988/p/3994065.html

https://www.cnblogs.com/oxiaojiano/p/7918967.html

https://www.cnblogs.com/buchongming/p/5858026.html

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

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,131评论 0 13
  • 继承 一、混入式继承 二、原型继承 利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的...
    magic_pill阅读 1,054评论 0 3
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,764评论 0 38
  • 如果说超市是一个城市的温情,那么厨房一定是一个家的软肋。厨房不是婚姻的全部也不是家庭的全部,但一定是很关键的地方。...
    骑着蜗牛闯天下阅读 578评论 0 5
  • 昨天是“霜降”,天气渐冷、初霜出现的意思。是秋季的最后一个气节,也意味着冬天即将开始。 今天是霜降后的第一天,温度...
    子墨非非阅读 165评论 0 0