成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

我所認(rèn)識的JavaScript作用域鏈和原型鏈

Bmob / 3271人閱讀

摘要:為了防止之后自己又開始模糊,所以自己來總結(jié)一下中關(guān)于作用域鏈和原型鏈的知識,并將二者相比較看待進一步加深理解。因此我們發(fā)現(xiàn)當(dāng)多個作用域相互嵌套的時候,就形成了作用域鏈。原型鏈原型說完了作用域鏈,我們來講講原型鏈。

  畢業(yè)也整整一年了,看著很多學(xué)弟都畢業(yè)了,忽然心中頗有感慨,時間一去不復(fù)還呀。記得從去年這個時候接觸到JavaScript,從一開始就很喜歡這門語言,當(dāng)時迷迷糊糊看完了《JavaScript高級程序設(shè)計》這本書,似懂非懂。這幾天又再次回顧了這本書,之前很多不理解的內(nèi)容似乎開始有些豁然開朗了。為了防止之后自己又開始模糊,所以自己來總結(jié)一下JavaScript中關(guān)于 作用域鏈和原型鏈的知識,并將二者相比較看待進一步加深理解。以下內(nèi)容都純屬于自己的理解,有不對的地方歡迎指正。

作用域鏈 作用域

  首先我們需要了解的是作用域做什么的?當(dāng)JavaScript引擎在某一作用域中遇見變量函數(shù)的時候,需要能夠明確變量和函數(shù)所對應(yīng)的值是什么,所以就需要作用域來對變量和函數(shù)進行查找,并且還需要確定當(dāng)前代碼是否對該變量具有訪問權(quán)限。也就是說作用域主要有以下的任務(wù):

收集并維護所有聲明的標(biāo)識符(變量和函數(shù))

依照特定的規(guī)則對標(biāo)識符進行查找

確定當(dāng)前的代碼對標(biāo)識符的訪問權(quán)限

  舉一個例子:

function foo(a) {
    console.log( a ); // 2
}

foo( 2 );

  對于上述代碼,JavaScript引擎需要對作用域發(fā)出以下的命令

查詢標(biāo)識符foo,得到變量后執(zhí)行該變量

查詢標(biāo)識符a,得到變量后對其賦值為2

查詢標(biāo)識符console,得到變量后準(zhǔn)備執(zhí)行屬性log

查詢標(biāo)識符a,得到變量后,作為參數(shù)傳入console.log執(zhí)行

  我們省略了函數(shù)console.log內(nèi)部的執(zhí)行過程,我們可以看到對JavaScript引擎來說,作用域最重要的功能就是查詢標(biāo)識符。從上面的例子來看,引擎對變量的使用其實不是都一樣的。比如第一步引擎得到標(biāo)識符foo的目的是執(zhí)行它(或者說是為了拿到標(biāo)識符里存儲的值)。
但第二步中引擎查找標(biāo)識符a的目的是為了對其賦值(也就是改變存儲的值)。所以查找也分為兩種:LHSRHS。

  我在之前的一篇文章中從LHS與RHS角度淺談Js變量聲明與賦值曾經(jīng)介紹過LHSRHS,這兩個看起來很高大上的名詞其實非常簡單。LHS指的是Left-hand Side,而RHS指的是Right-hand Side。分別對應(yīng)于兩種不同目的的詞法查詢。LHS所查詢的目的是為了賦值(類似于該變量會位于賦值符號=的左邊),例如第二步查找變量a的過程。而RHS所查詢的目的是為了引用(類似于變量會位于賦值符號=的右邊),例如第一步查找變量foo的過程。
  

作用域鏈

  我們知道代碼不僅僅可以訪問當(dāng)前的作用域的變量,對于嵌套的父級作用域中的變量也可以訪問。我們先只在ES5中表述,我們知道JavaScript在ES5中是沒有塊級作用域的,只有函數(shù)可以創(chuàng)建作用域。舉個例子:
  

function Outer(){
    var outer = "outer";
    Inner();
    function Inner(){
        var inner = "inner";
        console.log(outer,inner) // outer inner
    }
}

  當(dāng)引擎執(zhí)行到函數(shù)Inner內(nèi)部的時候,不僅可以訪問當(dāng)前作用域而且可以訪問到Outer的作用域,從而可以訪問到標(biāo)識符outer。因此我們發(fā)現(xiàn)當(dāng)多個作用域相互嵌套的時候,就形成了作用域鏈。詞法作用域在查找標(biāo)識符的時候,優(yōu)先在本作用域中查找。如果在本作用域沒有找到標(biāo)識符,會繼續(xù)向上一級查找,當(dāng)?shù)诌_最外層的全局作用域仍然沒有找到,則會停止對標(biāo)識符的搜索。如果沒有查找到標(biāo)識符,會根據(jù)不同的查找方式作出不同的反應(yīng)。如果是RHS,則會拋出Uncaught ReferenceError的錯誤,如果是LHS,則會在查找最外層的作用域聲明該變量,這就解釋了為什么對未聲明的變量賦值后該變量會成為全局變量。所以上面的代碼執(zhí)行

console.log(outer,inner)

的時候,引擎會首先要求Inner函數(shù)的詞法作用域查找(RHS)標(biāo)識符outer,被告知該詞法作用域不存在該標(biāo)識符,然后引擎會要求嵌套的上一級Outer詞法作用域查找(RHS)標(biāo)識符outer,Outer詞法作用域的查找成功并將結(jié)果返回給引擎。

換個角度理解作用域鏈

  上面我們理解作用域鏈都是從作用域鏈查找變量的角度去考慮的,其實這已經(jīng)足夠了,大部分作用域鏈的場景都是查找標(biāo)識符。但是我們可以換一個角度去理解作用域鏈。其實JavaScript的每個函數(shù)都有對應(yīng)的執(zhí)行環(huán)境(execution context)。當(dāng)執(zhí)行流進入進入一個函數(shù)時,該函數(shù)的執(zhí)行環(huán)境就會被推入環(huán)境棧,當(dāng)函數(shù)執(zhí)行結(jié)束之后,該函數(shù)的執(zhí)行環(huán)境就會被彈出環(huán)境棧,執(zhí)行環(huán)境被變更為之前的執(zhí)行環(huán)境。而每創(chuàng)建一個執(zhí)行環(huán)境時,會同時生成一個變量對象(variable object)(函數(shù)生成的是活動變量(activation object)),用來存儲當(dāng)前執(zhí)行環(huán)境中定義的變量和函數(shù),當(dāng)執(zhí)行環(huán)境結(jié)束時,當(dāng)前的變量(活動)對象就會被銷毀(全局的變量對象是一直存在的,不會被銷毀)。雖然我們無法訪問到變量(活動)對象,但詞法作用域查找標(biāo)識符會使用它。
  當(dāng)對于函數(shù)的執(zhí)行環(huán)境生成的活動對象,初始化就會存在兩個變量:thisarguments,因此我們在函數(shù)中就直接可以使用這兩個變量。對于作用域鏈存儲都是變量(活動)對象,而當(dāng)前執(zhí)行環(huán)境的變量對象就存儲在作用域鏈的最前端,優(yōu)先被查找。從這個角度看,標(biāo)識符解析是沿著作用域鏈一級一級地在變量(活動)對象中搜索標(biāo)識符的過程。搜索過程始終從作用域鏈的前端開始,然后逐級地向后回溯,直至找到標(biāo)識符為止。
  

閉包

  這年頭出去面試JavaScript的崗位,各個都要問你閉包的問題,開始的時候覺得閉包的概念蠻高級的,后來覺得這個也沒啥東西可講的。老早的之前就寫過一篇關(guān)于閉包的文章淺談JavaScript閉包,講到現(xiàn)在我覺得把閉包放到作用域鏈一起將會更好。還是繼續(xù)講個例子:

function fn(){
    var a = "JavaScript";
    function func(){
        console.log(a);
    }
    return func;
}

var func = fn();
func(); //JavaScript

  首先明確一下什么是閉包?我認(rèn)為閉包最好的概念解釋就是:

函數(shù)在定義的詞法作用域以外的地方被調(diào)用,閉包使得函數(shù)可以繼續(xù)訪問定義時的詞法作用域。

  func函數(shù)執(zhí)行的位置和定義的位置是不相同的,func是在函數(shù)fn中定義的,但執(zhí)行卻是在全局環(huán)境中,雖然是在全局函數(shù)中執(zhí)行的,但函數(shù)仍然可以訪問當(dāng)定義時的詞法作用域。如下圖所示:

  我們之前說過,當(dāng)函數(shù)執(zhí)行結(jié)束后其活動變量就會被銷毀,但是在上面的例子中卻不是這個樣子。但函數(shù)fn執(zhí)行結(jié)束之后,fn對象的活動變量并沒有被銷毀,這是因為fn返回的函數(shù)func的作用域鏈還保持著fn的活動變量,因此JavaScript的垃圾回收機制不會回收fn活動變量。雖然返回的函數(shù)func是在全局環(huán)境下執(zhí)行的,但是其作用域鏈的存儲的活動(變量)對象的順序分別是:func的活動變量、fn的活動變量、全局變量對象。因此在func函數(shù)執(zhí)行時,會順著作用域鏈查找標(biāo)識符,也就能訪問到fn所定義的詞法作用域(即fn函數(shù)的活動變量)也就不足為奇了。這樣看起來是不是覺得閉包也是非常的簡單。
  

原型鏈 原型

  說完了作用域鏈,我們來講講原型鏈。首先也是要明確什么是原型?所有的函數(shù)都有一個特殊的屬性: prototype(原型),prototype屬性是一個指針,指向的是一個對象(原型對象),原型對象中的方法和屬性都可以被函數(shù)的實例所共享。所謂的函數(shù)實例是指以函數(shù)作為構(gòu)造函數(shù)創(chuàng)建的對象,這些對象實例都可以共享構(gòu)造函數(shù)的原型的方法。舉個例子:
  

var Person = function(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log("name: ",this.name)
};

var person = new Person("JavaScript");
person.sayName(); //JavaScript

  在上面的例子中,對象person是構(gòu)造函數(shù)Person創(chuàng)建的實例。所謂的構(gòu)造函數(shù)也只不過是普通的函數(shù)通過操作符new來調(diào)用。在使用new操作符調(diào)用函數(shù)時主要執(zhí)行以下幾個步驟:

創(chuàng)建新的對象,并將函數(shù)的this指向新創(chuàng)建的對象

執(zhí)行函數(shù)

返回新創(chuàng)建的對象

  通過構(gòu)造函數(shù)返回的對象,其中含有一個內(nèi)部指針[[Prototype]]指向構(gòu)造函數(shù)的原型對象,當(dāng)然我們是無法訪問到這個標(biāo)準(zhǔn)的內(nèi)部指針[[Prototype]],但是在Firefox、Safari和Chrome在上都支持一個屬性__proto__,用來指向構(gòu)造函數(shù)的原型對象。下圖就解釋了上面的結(jié)構(gòu):

  

  我們可以看到,構(gòu)造函數(shù)Personprototype屬性指向Prototype的原型對象。而person作為構(gòu)造函數(shù)Person創(chuàng)建的實例,其中存在內(nèi)部指針也指向Person的原型對象。需要注意的是,在Person的原型對象中存在一個特殊的屬性constructor,指向構(gòu)造函數(shù)Person。在我們的例子中,執(zhí)行到:

person.sayName(); //JavaScript

  當(dāng)執(zhí)行personsayName屬性時,首先會在對象實例中查找sayName屬性,當(dāng)發(fā)現(xiàn)對象實例中不存在sayName時,會轉(zhuǎn)而去搜索person內(nèi)部指針[[Prototpe]]所指向的原型對象,當(dāng)發(fā)現(xiàn)原型對象中存在sayName屬性時,執(zhí)行該屬性。關(guān)于函數(shù)sayNamethis的指向,有興趣可以戳這篇文章一個小小的JavaScript題目。
  

原型鏈

  講完了原型,再講講原型鏈,其實我們上面的圖并不完整,因為所有函數(shù)的默認(rèn)原型都是Object的實例,所以函數(shù)原型實例的內(nèi)部指針[[Prototype]]指向的是Object.prototype,讓我們繼續(xù)來完善一下:
  

  
  這就是完整的原型鏈,假如我們執(zhí)行下面代碼:

person.toString()

  
  執(zhí)行上面代碼時,首先會在對象實例person中查找屬性toString方法,我們發(fā)現(xiàn)實例中不存在toString屬性。然后我們轉(zhuǎn)到person內(nèi)部指針[[Prototype]]指向的Person原型對象去尋找toString屬性,結(jié)果是仍然不存在。這找不到我們就放棄了?開玩笑,我們這么有毅力。我們會再接著到Person原型對象的內(nèi)部指針[[Prototype]]指向的Object原型對象中查找,這次我們發(fā)現(xiàn)其中確實存在toString屬性,然后我們執(zhí)行toString方法。發(fā)現(xiàn)了沒有,這一連串的原型形成了一條鏈,這就是原型鏈
  
  其實我們上面例子中對屬性toString查找屬于RHS,以RHS方式尋找屬性時,會在原型鏈中依次查找,如果在當(dāng)前的原型中已經(jīng)查找到所需要的屬性,那么就會停止搜索,否則會一直向后查找原型鏈,直到原型鏈的結(jié)尾(這一點有點類似于作用域鏈),如果直到原型鏈結(jié)尾仍未找到,那么該屬性就是undefined。但執(zhí)行LHS方式的查找卻截然不同,當(dāng)發(fā)現(xiàn)對象實例本身不存在該屬性,直接在該對象實例中聲明變量,而不會去查找原型鏈。例如:

person.toString = function(){
    console.log("person")
}
person.toString(); //person

  當(dāng)對person執(zhí)行LHS的方式查找toString屬性時,我們發(fā)現(xiàn)person中并不存在toString,這時會直接在person中聲明屬性,而不會去查找原型鏈,接著我們執(zhí)行person.toString()時,我們在實例中找到了toString屬性并將其執(zhí)行,這樣實例中的toString就屏蔽了原型鏈中的toString屬性。
  

作用域鏈和原型鏈的比較

  講完了作用域鏈和原型鏈,我們可以比較一下。作用域鏈的作用主要用于查找標(biāo)識符,當(dāng)作用域需要查詢變量的時候會沿著作用域鏈依次查找,如果找到標(biāo)識符就會停止搜索,否則將會沿著作用域鏈依次向后查找,直到作用域鏈的結(jié)尾。而原型鏈?zhǔn)怯糜?strong>查找引用類型的屬性,查找屬性會沿著原型鏈依次進行,如果找到該屬性會停止搜索并做相應(yīng)的操作,否則將會沿著原型鏈依次查找直到結(jié)尾。
    
  如果覺得閱讀完了本篇文章對你有些許幫助,歡迎大家我關(guān)注我的掘金賬號或者star我的Github的blog項目,也算是對我的鼓勵啦!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/83709.html

相關(guān)文章

  • javascript系列--javascript深入淺出圖解作用閉包

    摘要:變量對象也是有父作用域的。作用域鏈的頂端是全局對象。當(dāng)函數(shù)被調(diào)用的時候,作用域鏈就會包含多個作用域?qū)ο?。?dāng)函數(shù)要訪問時,沒有找到,于是沿著作用域鏈向上查找,在的作用域找到了對應(yīng)的標(biāo)示符,就會修改的值。 一、概要 對于閉包的定義(紅寶書P178):閉包就是指有權(quán)訪問另外一個函數(shù)的作用域中的變量的函數(shù)。 關(guān)鍵點: 1、閉包是一個函數(shù) 2、能夠訪問另外一個函數(shù)作用域中的變量 二、閉包特性 對...

    Jensen 評論0 收藏0
  • 2017-06-29 前端日報

    摘要:前端日報精選如何在非項目中使用知乎專欄編碼規(guī)范最常被遺忘的性能優(yōu)化瀏覽器緩存?zhèn)€人文章譯統(tǒng)一樣式語言掘金新的開發(fā)者提及最多的個視頻眾成翻譯中文第期在中使用譯統(tǒng)一樣式語言掘金前端現(xiàn)狀答題救不了前端新人相學(xué)長懟前端歲以 2017-06-29 前端日報 精選 如何在非 React 項目中使用 Redux - 知乎專欄Javascript編碼規(guī)范 - Clearlove - SegmentFau...

    gaosboy 評論0 收藏0
  • 【進階2-1期】深入淺出圖解作用閉包

    摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對象,在全局環(huán)境中定義的變量就會綁定到全局對象中。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周開始前端進階的第二期,本周的主題是作用域閉包,今天是第6天。 本...

    levius 評論0 收藏0
  • 關(guān)于JavaScript中訪問不帶有this修飾變量搜索順序理解

    摘要:這幾天因為對于中的作用域鏈和原型鏈有點混淆,當(dāng)訪問一個不帶有修飾的變量時,我想知道它的搜索順序,因為作用域鏈的鏈結(jié)點也是一個變量對象,那么當(dāng)在這個變量對象中查找變量時會不會沿著它的原型鏈查找呢這樣就有兩種可能先查找作用域鏈前端的變量對象,然 這幾天因為對于JavaScript中的作用域鏈和原型鏈有點混淆,當(dāng)訪問一個不帶有this修飾的變量時,我想知道它的搜索順序,因為作用域鏈的鏈結(jié)點也...

    jeyhan 評論0 收藏0
  • js深入(三)作用與閉包

    摘要:在之前我們根絕對象的原型說過了的原型鏈,那么同樣的萬物皆對象,函數(shù)也同樣存在這么一個鏈?zhǔn)降年P(guān)系,就是函數(shù)的作用域鏈作用域鏈?zhǔn)紫认葋砘仡櫼幌轮爸v到的原型鏈的尋找機制,就是實例會先從本身開始找,沒有的話會一級一級的網(wǎng)上翻,直到頂端沒有就會報一 在之前我們根絕對象的原型說過了js的原型鏈,那么同樣的js 萬物皆對象,函數(shù)也同樣存在這么一個鏈?zhǔn)降年P(guān)系,就是函數(shù)的作用域鏈 作用域鏈 首先先來回...

    blair 評論0 收藏0

發(fā)表評論

0條評論

Bmob

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<