最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 作用域和闭包

    正文概述 掘金(zoew)   2021-02-24   487

    前言

    这一块的概念其实被反复讲了很多次,每个人都有自己不一样的理解。两者的重要性不言而喻,基本我们每天的代码都会多多少少接触到,而两者的又有千丝万缕的联系。以此做梳理前端知识体系的入口,我觉的有一定的门槛,但是对后续学习而言,这个基础打扎实了,以后万千大楼平地起便有了可能。

    作用域

    概念:这是一套规则,一套可以用来存储变量的,且方便后续查找变量的规则。

    我们通过一个例子来说明这个概念,“ var=1,里面发生了什么 ”,

    • 首先编译器会询问作用域内是否存在 a 这个名称的变量,存在的话,编译器会直接忽略声明,然后继续编译。但不存在的话,它会要求作用域声明出一个变量,并命名为a。
    • 接着,引擎开始执行,在作用域里面查找叫做 a 的变量,找到的话 执行 =2 的赋值操作,没有的话,会继续从局部找到全局,再找不到的话,会直接抛出异常。<br /

    ( 引擎:负责程序的编译和执行过程,编译器:负责语法分析和代码生成 )
    可见作用域是一个存储变量的地方,无论是引擎还是编译器都要向它拿变量或者添加变量。 接着说明几点规则,
    首先是查找规则,我画了一个简陋的图,如图有两层作用域,当在fn 里面需要a时,本身fn是没有的,但是它会向上查找,一层层网上寻找,直到全局环境,找不到的话就报异常。而且作用域会在找到第一个匹配的标识符停止,那以上不过有多少个相同的变量都会被屏蔽掉,这叫“遮蔽效应”。

    作用域和闭包

    词法作用域

    作用域有两种工作模型: 词法作用域和动态作用域,大部分编程语言使用的都是词法作用域,JS也是。动态作用域可自行查阅了解。

    概念:顾名思义就是定义在词法阶段的作用域,何谓词法阶段,就是语言编译器的第一个阶段词法化的过程,将字符串分解组成有含义的代码块。简单点说就是由你在写代码时将变量和块作用域写在哪里决定的。
    所以变量也好,作用域也好,早在书写的时候就可以预见到了,这也是区别动态作用域的一点,因为动态作用域是在执行代码的是才确定的。

    作用域的具体类型分两种:函数作用域和块作用域

    function  fn () {
     var a = 1
     console.log(a)
    }
    

    这样形成一个函数作用域,函数内的变量可以再整个函数范围内使用及复用,其中有嵌套他的作用域的话,也能使用。而且外部是无法主动访问内部的变量的,除非使用闭包。这种作用域有几个特点,隐藏内容实现,因为外部无法访问到,而且可以避免同名标识符的冲突。但是也个有问题,一旦定义了一个函数,它将势必“污染”全局作用域,其次无法自动执行,需要手动调动 fn()。

    因此便衍生出了,一种概念 IIFE,又名立即执行函数

    (function () {
        var a = 2;
        console.log(a)
    })()
    !function(){
       var a = 2;
       console.log(a)
    }()
    

    当然类似的写法有好多种如 !,~,+,-等开头的,原理是第一个()将里面包裹函数,成了一个表达式,第二个()执行这个表达式,于是函数就执行了。

    块作用域,代码块{},也能有属于自己的变量和函数

    在let,const 出来之前,js并没有具体的块作域的功能,但倒是有几个不显而易见的语法,
    with(),这个语法很少用,但是它声明出来的内容仅在里面有用,外面并不能用到。

    var obj = {a: 1,b:2,c:3}
    with(obj) {
      a = 3;
      b = 4;
      c = 5;
    }
    

    其次就是 try/catch,这里的catch分句也会形成一个块作用域。但是这两种算是很少用,也很难用,对于要是使用块作用来讲的话,直到es6推出了let,const。

    let,const 其所在的区域,大部分是{},会自行形成块级作用域,并且不会出现变量提升,不可重复定义或赋值(const),形成暂时性死区(不声明,不可直接使用)。大大提升了代码的质量。

    提升

    变量提升是js中非常有趣的一点,在不使用let或const的时候,你要好好分析,变量的执行顺序并适当用到变量提升的知识。但这往往也是令人头大。

    a = 2;
    var a; 
    console.log( a );
    

    来看看这段代码会打印什么吧。答案是2,因为var a 在编译时被提升了,于是后续的赋值操作也能继续.当然就算不定义a 在浏览器里面也会正常执行,因为这时候的 a 会自动挂载在windwo下面,也就是window.a
    首先明确一点,包括变量和函数在内的所有声明都会在代码被执行前首先被处理。 而它们的声明从代码出现的位置被移动到最上面的过程,叫做“提升”
    注意一点,函数声明会被提升,函数表达式却不会。

    function foo () {}   //会提升
    let foo = function bar () {}  //这里只会吧 foo 提升上去,后面的bar 不会赋值
    

    当出现多个重复的声明时,函数会被先提升,然后才是变量

    foo(); // 1
    var foo;
    function foo() { console.log( 1 );
    }
    foo = function() { console.log( 2 );
    };
    

    这里的 function foo 会被提升首先执行,然后执行 foo(), 最后再重新赋值。尽管中间插了 var foo,但是它是重复声明,会被忽略,以函数为主。这也符合函数的JS一等公民的说法。

    闭包

    这个概念其实很难讲,很多文章会把闭包讲的很复杂,虽然他也确实可以变得很复杂,但我觉得还是按简单的,通俗易懂的说法来讲比较好,深度化的东西,只能看自己慢慢探索。
    概念:函数可以记住并访问所在的词法作用域,就算函数在外部执行,这个函数就是一个闭包

    function foo() { 
      var a = 2;
      function bar() { 
          console.log( a );
      }
      return bar; 
    }
    var baz = foo();
    baz();
    

    闭包的使用场景很多,定时器,事件监听,ajax请求,Web Workers等实际上都是闭包的使用。它能使外部访问函数内部的变量,做到即是函数执行结束了,内部的变量也不会被垃圾回收机制回收掉。其内部的作用域依赖存在,可供随时使用。但是缺点也是显而易见的,因为变量无法被回收,当你的代码里面存在大量的闭包时,有可能导致内存泄露
    ES6的模块化本身其实就是闭包思想的体现。

    结语

    以上概念均来自于《你不知道的JavaScript》,这算是对书上知识的总结。有很多细节的地方,没有提到,感兴趣的同学可以去翻翻这本书。
    以前总是看文章啊,看书之类的。但是这样的吸收效率其实很低,不久后就忘了。于是想着把它输出出去,事实证明输出才是真正去了解和掌握的过程。写文章真的累,要思考逻辑是否连贯,行文是否清晰,就算全部照搬文字,也要思考如何搬运。期间也加深了理解。
    能力有限,不足也有,轻喷。


    起源地下载网 » 作用域和闭包

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元