首頁  >  文章  >  web前端  >  一紙搞懂JS系列(1)之編譯原理,作用域,作用域鏈,變數提升,暫時性死區

一紙搞懂JS系列(1)之編譯原理,作用域,作用域鏈,變數提升,暫時性死區

coldplay.xixi
coldplay.xixi轉載
2020-09-28 15:58:591846瀏覽

一紙搞懂JS系列(1)之編譯原理,作用域,作用域鏈,變數提升,暫時性死區

寫在最前面:這是javascript欄位我即將開始寫的一個系列,主要是在框架橫行的時代,雖然上班用的是框架,但是對於面試,以及技術進階,JS基礎知識的鋪墊是錦上添花,也是不得不學習的一塊知識,雖然開汽車的不需要很懂汽車,只需要掌握汽車的常用功能即可。但是如果你懂車,那你也能更好地開車,同理。當然,一篇文章也不會光光只講一個知識點,一般會將有關聯的知識點串聯起來,一邊記錄自己的學習,一邊分享自己的學習,互勉!如果可以的話,也請給我按讚,你的讚也能讓我更努力地更新!

概覽

  • # 食用時間:5-10分鐘
  • 難度:簡單,別跑,看完再走

##### JS編譯原理###############我們先來看一行程式碼###
var name='jack';复制代码
###在我們眼中,這是一行程式碼,一個語句就能搞定的事情呀,但是,在JS的眼裡,這句話的程式碼應該是下面這種方式呈現的###
var name;    //编译阶段处理name='jack';    //执行阶段处理复制代码
###是不是發現這和你原本認識的JS不太一樣,那是因為JS編譯主要分為兩個階段,編譯階段和執行階段,讓我們先來看下這兩個階段分別做了那些事情:###
  • 編譯階段

    #這個階段的主角就是所謂的編譯器,這個編譯器會找遍目前作用域,看看是不是已經存在一個叫做name 的變數。如果已經存在,那麼就什麼都不做,直接忽略var name 這個聲明,繼續編譯下去;如果沒有,則在當前作用域裡新增一個叫name 的變量。然後,編譯器會為引擎產生執行階段所需的程式碼,程式就進入了執行階段

  • ##執行階段

    這個階段的主角就是大家所熟悉的

    JS引擎啦,JS引擎在運作的時候,也會先找遍目前作用域,看看是否有一個叫name 的變量,如果有的話,那麼剛剛好,直接賦值,如果沒有的話,那就是當前作用域沒有,那怎麼辦,那就考慮探出頭去,去外面( 父級作用域)看看有沒有,沒有的話,再去外面查找,一層又一層( 當然如果是還有父層的話),如果最終還是找不到的話,那麼JS引擎也表示無能為力,那就拋個異常給別人看看吧,表示自己已經努力過了。

    上面提到的去外面查找,一層又一層,從目前作用域再到父級作用域,再到父級的父級作用域,以此類推,就是所謂的作用域鏈了,就跟鏈條一樣,一節有一節往上,是不是形容地可以說很貼切了。總結而言就是,

    作用域套作用域,也就有了所謂的作用域鏈

作用域

  • 定义

    大家都知道,变量最基本的能力就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改,而对于变量存储,访问的一套规则,就是所谓的作用域

  • 分类

    • 全局作用域

    在任何函数外或者代码块之外的顶层作用域就是全局作用域,而里面的变量就是全局变量

    var name='jack';   //全局作用域function showName(){    //函数作用域  console.log(name);}{  name='test';    //块级作用域}showName();    //test复制代码

    可以看到全局变量,无论是在全局作用域,函数作用,还是块级作用域中都可以正常访问

    • 函数作用域

    在函数内的作用域就是函数作用域

    function showName(){  var name='jack';    //函数作用域}showName();   //方法调用{  console.log(name);  //块级作用域,Uncaught ReferenceError: name is not defined}console.log(name);  //全局作用域,Uncaught ReferenceError: name is not defined复制代码

    可以看到函数内部变量,在全局作用域以及块级作用域中,都无法访问,只有在函数内部,才能访问的到,所以函数内的变量也被称为局部变量

    • 块作用域

    ES6 中新出的两个新关键字 letconst中,自带块级作用域,块级作用域相当于是只在这块代码块中生效,如果它被大括号 {} 所包围,那么大括号中就是一段代码块,代码块中使用 letconst 声明的变量也被称为局部变量

     {   let name='jack'; } console.log(name);    //Uncaught ReferenceError: name is not defined function showName{   console.log(name); } showName();    //Uncaught ReferenceError: name is not defined复制代码

    可以看到块级作用域中的变量,出了那个代码块,就找不到了

其实上面的三种情况,结合JS编译原理和作用域链向外不向内查找,思考一下,也不难理解

作用域链

回到作用域链,其实在上面已经解释的差不多了,作用域和作用域的嵌套,就产生了作用域链,另外要记住的一个特性就是作用域链的查找,向外不向内,想想探出头去,而不是看着锅里,就可以了

变量提升

先来看一段代码

name='jack';console.log(name);    //jackvar name;复制代码

你会发现,这段代码不会发生报错,并且能正常地运行,结合上面所说的JS编译原理,你就能想到,在JS的眼中,它的代码实际上是这样子的,这就是所谓的变量提升,说白了那就是代码的声明提到代码的最前面

var name;name='jack';console.log(name);    //jack复制代码

其实这个变量提升应该是照道理接着编译原理写下来的,为什么放到了最后呢,因为如果你忘了,正好往上翻一下,重新回温一遍JS编译原理

紧接着上面,让我们来看下不吃变量提升这一套的 letconst ,先来看一段代码

name='jack';console.log(name)    //Uncaught ReferenceError: Cannot access 'name' before initializationlet name;复制代码

黑人问号 ??? ,说好的变量提升呢,记住 letconst 的一个特点,禁用变量提升,这也是 ES6 故意为之的,将生命前不可用做到了强约束,总结而言,** var 存在变量提升, letconst 不存在变量提升**

既然已经提到了 const ,顺带提一下它声明了以后必须赋值的操作

const name;    //Uncaught SyntaxError: Missing initializer in const declaration复制代码

暂时性死区

紧接着上面,让我们来看下什么叫做暂时性死区,先来看一段代码

var name='jack';{  name='bob';  let name;    //Uncaught ReferenceError: Cannot access 'name' before initialization}复制代码

记住 ES6 中的一个特性,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。因为JS清楚地感知到了 name 是用 let 声明在当前这个代码块内的,所以会给这个变量 name 加上了暂时性死区的限制,它就不往外探出头了。

那么,如果我们把上面的let name;去掉,程序也将正常运行, name 的值也被成功修改为了bob,就是正常地按照作用域链的规则,向外探出头去了。

更多相关免费学习推荐:javascript(视频)

以上是一紙搞懂JS系列(1)之編譯原理,作用域,作用域鏈,變數提升,暫時性死區的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除