欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

全面了解JavaScript的作用域鏈

 更新時間:2019年04月03日 16:35:02   作者:Skylor.min  
這是一個非常重要的知識點了,了解了JavaScript的作用域鏈的話,能幫助我們理解很多‘異?!瘑栴}。文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
JavaScript的作用域鏈

這是一個非常重要的知識點了,了解了JavaScript的作用域鏈的話,能幫助我們理解很多‘異常'問題。

下面我們來看一個小例子,前面我說過的聲明提前的例子。

var name = 'Skylor.min';
 function echo() {
 alert(name);
 var name = 'mm';
 alert(name);
 alert(age);
 }

 echo();

 對于這個例子,沒有接觸過這方面的時候,第一反應是會糾結下,這第一個的name,到底調用全局變量的name,還是函數(shù)內部的name呢,如果調用全局的,可是函數(shù)內部也用定義和賦值啊, 如果調用函數(shù)內部的局部變量的話,那么他的值是mm嗎?還是引用全局的'Skylor.min'呢?

于是這個小例子就會有這樣的錯誤答案:

Skylor.min
mm
[腳本出錯]
 

其實不然,知道函數(shù)內的提前說明,就知道這是不正確的。

    undefined
    mm
    [腳本出錯]
 

應該是這樣的,那到底為什么是這個答案呢,提前聲明這又是什么呢?一切的一切,涉及到JavaScript的作用域鏈。

原理

首先來說說,JavaScript的作用域的原理:

在JavaScript權威指南中有一句很精辟的描述: JavaScript中的函數(shù)運行在它們被定義的作用域里,而不是它們被運行的作用域里。

另外在JavaScript中有個很重要的概念,那就是: 在JavaScript中,一切皆對象,函數(shù)也是。

在JS中,作用域的概念和其他語言差不多, 在每次調用一個函數(shù)的時候 ,就會進入一個函數(shù)內的作用域,當從函數(shù)返回以后,就返回調用前的作用域

JS的語法風格和C/C++類似, 但作用域的實現(xiàn)卻和C/C++不同,并非用“堆?!狈绞?,而是使用列表,具體過程如下(ECMA262中所述):

  • 任何執(zhí)行上下文時刻的作用域, 都是由作用域鏈(scope chain, 后面介紹)來實現(xiàn)
  • 在一個函數(shù)被定義的時候, 會將它定義時刻的scope chain鏈接到這個函數(shù)對象的[[scope]]屬性
  • 在一個函數(shù)對象被調用的時候,會創(chuàng)建一個活動對象(也就是一個對象), 然后對于每一個函數(shù)的形參,都命名為該活動對象的命名屬性, 然后將這個活動對象做為此時的作用域鏈(scope chain)最前端, 并將這個函數(shù)對象的[[scope]]加入到scope chain中.

看個例子吧:

var func = function(lps, rps){
        var name = 'Skylor.min';
        ........
    }
    func();
 

在執(zhí)行func的定義語句的時候, 會創(chuàng)建一個這個函數(shù)對象的[[scope]]屬性(內部屬性,只有JS引擎可以訪問, 但FireFox的幾個引擎(SpiderMonkey和Rhino)提供了私有屬性__parent__來訪問它), 并將這個[[scope]]屬性, 鏈接到定義它的作用域鏈上(后面會詳細介紹), 此時因為func定義在全局環(huán)境, 所以此時的[[scope]]只是指向全局活動對象window active object.

在調用func的時候, 會創(chuàng)建一個活動對象(假設為aObj, 由JS引擎預編譯時刻創(chuàng)建, 后面會介紹),并創(chuàng)建arguments屬性, 然后會給這個對象添加倆個命名屬性aObj.lps, aObj.rps; 對于每一個在這個函數(shù)中申明的局部變量和函數(shù)定義, 都作為該活動對象的同名命名屬性.

然后將調用參數(shù)賦值給形參數(shù),對于缺少的調用參數(shù),賦值為undefined。

然后將這個活動對象做為scope chain的最前端, 并將func的[[scope]]屬性所指向的,定義func時候的頂級活動對象, 加入到scope chain.

有了上面的作用域鏈, 在發(fā)生標識符解析的時候, 就會逆向查詢當前scope chain列表的每一個活動對象的屬性,如果找到同名的就返回。找不到,那就是這個標識符沒有被定義。

注意到, 因為函數(shù)對象的[[scope]]屬性是在定義一個函數(shù)的時候決定的, 而非調用的時候, 所以如下面的例子:

var name = 'Skylor.min';
 function echo() {
 alert(name);
 }

 function env() {
 var name = 'mm';
 echo();
 }

 env();

他的運行結果是:Skylor.min

結合上面的知識, 我們來看看下面這個例子,還記得那句JavaScript權威指南中的經(jīng)典,JavaScript中的函數(shù)運行在它們被定義的作用域里,而不是它們被運行的作用域里。

function factory() {
 var name = 'Skylor.min';
 var intro = function(){
  alert('I am ' + name);
 }
 return intro;
 }

 function app(para){
 var name = para;
 var func = factory();
 func();
 }

 app('mm');

當調用app的時候, scope chain是由: {window活動對象(全局)}->{app的活動對象} 組成.

在剛進入app函數(shù)體時, app的活動對象有一個arguments屬性, 其他倆個值為undefined的屬性: name和func. 和一個值為'mm'的屬性para;

此時的scope chain如下:

[[scope chain]] = [
 {
  para : 'mm',
  name : undefined,
  func : undefined,
  arguments : []
 }, {
  window call object
 }
 ]

 當調用進入factory的函數(shù)體的時候, 此時的factory的scope chain為:

[[scope chain]] = [
 {
  name : undefined,
  intor : undefined
 }, {
  window call object
 }
 ]

注意到, 此時的作用域鏈中, 并不包含app的活動對象.

在定義intro函數(shù)的時候, intro函數(shù)的[[scope]]為:

[[scope chain]] = [
 {
  name : 'Skylor.min',
  intor : undefined
 }, {
  window call object
 }
 ]

從factory函數(shù)返回以后,在app體內調用intor的時候, 發(fā)生了標識符解析, 而此時的sope chain是:

[[scope chain]] = [
 {
  intro call object
 }, {
  name : 'Skylor.min',
  intor : undefined
 }, {
  window call object
 }
 ]

 因為scope chain中,并不包含factory活動對象. 所以, name標識符解析的結果應該是factory活動對象中的name屬性, 也就是'Skylor.min'.

所以運行結果是: I am Skylor.min

至此,完整的一個運行流程,很清晰的能讀懂“JavaScript中的函數(shù)運行在它們被定義的作用域里,而不是它們被運行的作用域里。”這句話講的是什么了。

為了解釋上面的一些問題,還得說說JavaScript的預編譯。

JavaScriptの預編譯

預編譯,學過C等的我們都知道,可是問題來了,JavaScript是腳本語言,JavaScript的執(zhí)行過程是一種翻譯執(zhí)行的過程,那在JavaScript的執(zhí)行中,有沒有類似編譯的過程呢?

如果不是很確定,先通過一個例子:

alert(typeof fun); //function
    function fun() {
        alert('I am Skylor.min');
    };
 

這時候彈出來的是?-----我去,是“I am Skylor.min”然而這時為什么呢,為啥不是undefined呢。

恩, 對, 在JS中, 是有預編譯的過程的, JS在執(zhí)行每一段JS代碼之前, 都會首先處理var關鍵字和function定義式(函數(shù)定義式和函數(shù)表達式).

如上文所說, 在調用函數(shù)執(zhí)行之前, 會首先創(chuàng)建一個活動對象, 然后搜尋這個函數(shù)中的局部變量定義,和函數(shù)定義, 將變量名和函數(shù)名都做為這個活動對象的同名屬性, 對于局部變量定義,變量的值會在真正執(zhí)行的時候才計算, 此時只是簡單的賦為undefined.

而對于函數(shù)的定義,是一個要注意的地方:

alert(typeof fun); //結果:function
 alert(typeof fn); //結果:undefined
 function fun() { //函數(shù)定義式
 alert('I am Skylor.min');
 };
 var fn = function() { //函數(shù)表達式
 }
 alert(typeof fn); //結果:function

這就是函數(shù)定義式和函數(shù)表達式的不同, 對于函數(shù)定義式, 會將函數(shù)定義提前. 而函數(shù)表達式, 會在執(zhí)行過程中才計算.

說到這里, 順便說一個問題 :

    var name = 'Skylor.min';
    age = 25;
 

我們都知道不使用var關鍵字定義的變量, 相當于是全局變量, 聯(lián)系到我們剛才的知識:

在對age做標識符解析的時候, 因為是寫操作, 所以當找到到全局的window活動對象的時候都沒有找到這個標識符的時候, 會在window活動對象的基礎上, 返回一個值為undefined的age屬性.

也就是說, age會被定義在頂級作用域中.

現(xiàn)在, 也許你注意到了我剛才說的: JS在執(zhí)行每一段JS代碼之前, 都會首先處理var關鍵字和function定義式(函數(shù)定義式和函數(shù)表達式).

對, 讓我們看看下面的例子:

<script >
 alert(typeof mm); //結果:undefined
 </script >
 <script >
 function mm() {
  alert('I am Skylor.min');
 }
 </script >

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。

相關文章

  • 用js將內容復制到剪貼板兼容瀏覽器

    用js將內容復制到剪貼板兼容瀏覽器

    通過js將內容復制到剪貼板,本來不難,可是若考慮到瀏覽器的兼容性問題,就變的有點麻煩,借助flash實現(xiàn)瀏覽器的兼容
    2014-03-03
  • Javascript的字符串方法詳解

    Javascript的字符串方法詳解

    這篇文章主要介紹了Javascript字符串方法詳解的相關資料,在平時工作中經(jīng)常會用到的,非常不錯,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-09-09
  • 原生js實現(xiàn)tab選項卡切換

    原生js實現(xiàn)tab選項卡切換

    這篇文章主要為大家詳細介紹了原生js實現(xiàn)tab選項卡,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JavaScript實現(xiàn)的簡單冪函數(shù)實例

    JavaScript實現(xiàn)的簡單冪函數(shù)實例

    這篇文章主要介紹了JavaScript實現(xiàn)的簡單冪函數(shù),實例分析了javascript實現(xiàn)冪運算的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • 超實用的全新JavaScript事件Scrollend實例詳解

    超實用的全新JavaScript事件Scrollend實例詳解

    這篇文章主要為大家介紹了超實用的全新JavaScript事件Scrollend實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • JavaScript中的變量聲明你知道嗎

    JavaScript中的變量聲明你知道嗎

    這篇文章主要為大家詳細介紹了JavaScript中的變量聲明,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Layui table field初始化加載時進行隱藏的方法

    Layui table field初始化加載時進行隱藏的方法

    今天小編就為大家分享一篇Layui table field初始化加載時進行隱藏的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • CocosCreator入門教程之網(wǎng)絡通信

    CocosCreator入門教程之網(wǎng)絡通信

    這篇文章主要介紹了CocosCreator的網(wǎng)絡通信,內容不多,涉及到的細節(jié),讀者可以根據(jù)實際情況,自己去延申
    2021-04-04
  • 表單驗證正則表達式實例代碼詳解

    表單驗證正則表達式實例代碼詳解

    這篇文章主要介紹了表單驗證正則表達式實例代碼詳解的相關資料,需要的朋友可以參考下
    2015-11-11
  • 元素的內聯(lián)事件處理函數(shù)的特殊作用域在各瀏覽器中存在差異

    元素的內聯(lián)事件處理函數(shù)的特殊作用域在各瀏覽器中存在差異

    在一個元素的屬性中綁定事件,實際上就創(chuàng)建了一個內聯(lián)事件處理函數(shù)(如<h1 onclick="alert(this);"...>...</h1>),內聯(lián)事件處理函數(shù)有其特殊的作用域鏈,并且各瀏覽器的實現(xiàn)細節(jié)也有差異。
    2011-01-01

最新評論