《初窥JS的“域”望法则:让V8引擎为你 揭开作用域的面纱》

93 阅读5分钟

JS的引擎

众所周知,任何一门语言都会有专门的执行器来执行,JS也不例外。JS的执行环境有两个,分别是浏览器和node。 嗯? 这个时候就会有些小伙伴有疑问了:为什么这两个东西都可以执行呢? 因为这两个执行环境都有--V8引擎。

V8引擎

  1. what is that ?

    V8 引擎的本质就是是一段庞大的函数,它可以读懂 JS 并执行

    给大家举个简单的例子

     console.log(a);  
    var a = 1
    

    如果咱们要跑这段代码,按照常理是不是得报错?

    好,那咱们用 node 跑一下就知道了,结果居然是 undefined , 而不是想象中的红色字体

    这个时候不得不谈到 JS 的执行顺序

    JS 的执行

      代码被 V8 读取到的第一时间, 并不是执行,而是会先编译(梳理)
    • 梳理
      1. 分词/词法分析
      2. 解析/语法分析 -- AST(抽象语法树) 获取有效标识符 在该例子中有效标识符为
      3. 生成代码

    因此,在 V8 眼里代码长成这个样子

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

    作用域

    分类

    1. 全局作用域(整份份代码被称为全局作用域)
    2. 函数作用域(参数也是该作用域的一个有效标识)
    3. 块级作用域 ( const , let 和 {} 语法配合使用会导致声明的变量处在一个作用域中)
    作用机制

    由内向外(就近原则,找到了就不找了),不能由外向内找 举个例子 那现在我们的小伙伴可以先思考一下,把你的手指停在这里,思考一下现在输出的a会是多少呢?

    696419cf168b237f7d08411aab68a77a.jpg

    从上往下V8引擎执行,到最下面。函数调用的时候,函数才执行,函数执行的时候,我在函数作用域里它就已经找到了a的值,所以输出的a就是20了

    例子2 好,下面这个例子大家先思考一下它的值是多少呢?思考一下,随后sigma老师再给大家揭晓答案。

    38e6d9b52023f30449177c80c7381c8d.jpg

    与上一个例子同理V8引擎从上执行到下面,它并没有调用函数。而是在调用函数之前就已经把a的值给输出了,那么这个时候的a它在哪里呢?显而易见。全局作用域,所以这个时候a输出的值就是10。 为了让大家更好的理解,Sigma老师专门把全局作用域和函数作用域给标注出来了,加上自己的理解小伙伴们好好的捋一下哟。

    5ca96de170470c5eff3bfbd98627dc2a.jpg

    Sigma老师相信小伙伴们掌握的不错,那么大家可以挑战一下下面这个题目哦

    fac89863bdc3324cc12a6ffbdea75f39.jpg

    那么此时a输出的值是什么呢? a的值是20吗?咱们拿node跑一下就知道了,报错! 看到报错,咱们不要灰心,咱们要看一下报错的内容是什么.

    35447889d0e3b74ccf639ddf09cce51c.jpg

    找到了a is not defined. a尽然没有被定义! 诶?不对劲啊,怎么会出现这种情况呢?假装咱们是V8引擎,咱们来跑一下,前面第三行到第五行不执行,因为函数没有调用,第六行函数被调用了,然后我们再去函数里面找,找到了a被定义并且被赋值成20,然后咱们走到第七行输出a,但是a竟然是没有被定义!说明什么?说明在全局作用域里面没有一个a被定义,但是函数作用域里面a被定义了,并且被赋值了。相信咱们聪明的小伙伴就已经想到了,在js这门语言中作用域是不是有一个特殊的性质呢?没错,在这个例子中全局作用域不能访问到函数作用域。在js这门语言中有一条这样的铁律,外层作用域不能向内层作用域作用域只能由内往外查找

    既然知道这条规则,那我们来尝试假装自己是V8引擎跑一下下面这段代码。

    54d94506c02c75efa6fb9ee3c175fc3b.jpg

    首先全局定义了一个a为10,其次,全局定了一个foo函数,然后全局调用了foo函数,foo函数定义了一个a为20,foo函数里面还定义一个函数为bar函数,好,现在问题来了,那bar函数里面这个输出的a找的是谁呢?怎么找?首先找bar函数作用域有没有a?没有,那下一步找它的外层作用就是负函数里面。的作用域有没有a? a等于多少?20,所以找到了a之后就停止了,不会再到全局作用域里面去找。

    Well,那么sigma老师再给大家介绍一个函数中 参数怎样和作用域联系在一起

    822faad800953443dc5cd42961eea7be.jpg

    这个时候输出的是什么呢?a我们已经知道了等于20,那么b呢?先从bar这个函数作用域里面找,找不到向外到foo这个函数作用域里面找,也找不到再向外找全局作用域去找,找到了等于2,所以输出的就是22啦。

    块级作用域特点

    区别是const不能修改值,let可以 只要和带 {} 的语法配合使用,自动形成作用域 有些小伙伴不相信能形成,那咱们跑一下

    93634f31bb8b0f57e08d70be78e93bcf.jpg

    db74e0902b30f97e90347761d71165cb.jpg

    访问不到a,由此可见形成了块级作用域

    80d162b5a9c7b957930f463e37ac87e9.jpg

    此时用node去跑这个代码,结果是a is not defined.为什么呢?这个现象被称为--暂时性死区

    当一个 {} 语句中存在let x时,在该 {} 中访问的x永远都只能访问 {} 内部的x,就算内部访问不到,也不能访问外部的x这种规则称为--- 暂时性死区