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

2018-11-12  本文已影响0人  前端小学生_f675

之所以写这篇文章,是跟我经历有关,前两天面试碰到一个很无理的面试官,年纪不大,电话面试,说话傲气,自称是写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

某简书作者:

https://www.jianshu.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

上一篇下一篇

猜你喜欢

热点阅读