JavaScript 核心

一門(mén)語(yǔ)言,無(wú)論是機(jī)器語(yǔ)言蚀乔,還是人類(lèi)語(yǔ)言烁竭。在學(xué)習(xí)的時(shí)候,我們從入門(mén)到精通會(huì)在以下幾個(gè)角度做分別的深入:

  • 詞法
  • 語(yǔ)法
  • 句法

對(duì)于 JavaScript核心而言吉挣,有些人喜歡將 ECMAScript 看作是 JavaScript 的詞法核心派撕,規(guī)定了其內(nèi)置的語(yǔ)言特性,關(guān)鍵詞和基本邏輯的構(gòu)造過(guò)程睬魂。
這些基本句法相對(duì)固定终吼,加之在日常開(kāi)發(fā)中我們反復(fù)練習(xí),并無(wú)太多出入之分氯哮。區(qū)別大多在于孰能生巧記得住和光說(shuō)不練假把式际跪。而與之相對(duì)的,圍繞對(duì)象展開(kāi)的 JavaScript 語(yǔ)言特性部分喉钢,結(jié)合了面向?qū)ο蟮臉?gòu)造過(guò)程垫卤,引入大量中英文互譯中模棱兩可的概念。導(dǎo)致部分工程師并不知其所以然出牧。
JavaScript 每一次進(jìn)步穴肘,都值得一線(xiàn)工程師來(lái)修正處于語(yǔ)言特性核心的東西。在這篇之前舔痕,相關(guān)的知識(shí)碎片化嚴(yán)重评抚。
說(shuō)到核心,可以理解為精粹伯复,當(dāng)然更重要的是中心理論中的重點(diǎn)難點(diǎn)慨代。

[TOC]

  • 作用域鏈
  • 閉包
  • this
  • 執(zhí)行上下文
  • 對(duì)象
  • 變量對(duì)象
  • 活動(dòng)對(duì)象
  • 原型鏈
  • 繼承

理解這些關(guān)鍵詞之前,首先需要明白兩件事

  • JavaScript 沒(méi)有類(lèi)的概念啸如,所以也沒(méi)有類(lèi)概念中的封裝侍匙,繼承,多態(tài)
  • 引入與類(lèi)相似的概念的目的叮雳,是為了實(shí)現(xiàn)代碼重用

對(duì)象

對(duì)象是 JavaScript 中的引用類(lèi)型想暗。

JavaScript中妇汗,一個(gè)對(duì)象就是一個(gè)屬性和方法的集合,每一個(gè)對(duì)象都擁有一個(gè) 原型對(duì)象 说莫,其本身也是個(gè)對(duì)象杨箭。

構(gòu)造一個(gè)對(duì)象常見(jiàn)方式有三種:對(duì)象字面直接量,使用 new 關(guān)鍵字構(gòu)建储狭,使用Object.create()方法互婿。
為了優(yōu)化構(gòu)造對(duì)象的過(guò)程(解決對(duì)象的來(lái)源,對(duì)象構(gòu)造的重復(fù)問(wèn)題辽狈,對(duì)象構(gòu)造的重用性問(wèn)題)引入一部分設(shè)計(jì)模式

  • 工廠(chǎng)模式(P 批量構(gòu)造 N 看不出來(lái)源)
  • 構(gòu)造函數(shù)(P 聲明來(lái)源 N 資源浪費(fèi))
  • 原型模式(P 實(shí)現(xiàn)重用 N 相互影響)

拿字面直接量方式構(gòu)造來(lái)說(shuō)明原型對(duì)象的存在慈参。

var foo = {
  x: 10,
  y:20
}

就構(gòu)成了如下對(duì)象與對(duì)象所具有的原型對(duì)象的關(guān)聯(lián)關(guān)系。如圖(1)

對(duì)象及其所具有的原型對(duì)象

其中 __proto__ 已被各大瀏覽器棄用刮萌,此屬性的值用來(lái)存儲(chǔ)內(nèi)部指向自己所對(duì)應(yīng)原型對(duì)象的指針驮配。
原型對(duì)象有什么用呢?我們用原型鏈來(lái)解釋尊勿。

原型鏈

原型對(duì)象也是對(duì)象,也擁有自己的 __proto__ 屬性指向自己的原型對(duì)象畜侦,這種串聯(lián)多個(gè)原型對(duì)象的模式叫做原型鏈元扔。

原型鏈?zhǔn)怯脕?lái)實(shí)現(xiàn)繼承和屬性共享的有限對(duì)象鏈

這里提出了 繼承 的概念,如果你沒(méi)有 OOP 的概念旋膳,那恭喜你了澎语,你能很快理解這種便捷的方式和概念,如果你是 Java/PHP 出身验懊,你直接把這里的 繼承 理解為 重用吧擅羞。

JavaScript 引入繼承概念,就是為了實(shí)現(xiàn)屬性和方法的重用义图。

如果一個(gè)屬性或方法在自身中無(wú)法找到减俏,那么會(huì)進(jìn)入原型鏈查找這個(gè)屬性或方法,依次遍歷整個(gè)鏈碱工,第一個(gè)被查找到的將會(huì)使用
如果沒(méi)有明確的指明原型對(duì)象的指向娃承,那么原型對(duì)象的原型指針會(huì)指向 Object 的原型對(duì)象,而后者的原型對(duì)象指針指向 null怕篷,也就是原型鏈的重點(diǎn)历筝。

舉個(gè) ??,有三個(gè)對(duì)象廊谓,b,c 對(duì) a 有不同程度繼承梳猪,代碼如下

let a = {
  x: 10,
  calc(z){return this.x+this.y+z }
}
let b = {
  y:20,
  __proto__: a
}
let c = {
  y:30,
    __proto__: a
}
b.calc(30);//?
c.calc(40);//?

這段代碼簡(jiǎn)明扼要,通過(guò) __proto__ 代指原型鏈的鏈接過(guò)程蒸痹,實(shí)際實(shí)現(xiàn)有所不同春弥。由此構(gòu)成了 a,b,c 之間的原型鏈如下圖(2)

通過(guò)原型鏈實(shí)現(xiàn)代碼復(fù)用

上述例子中呛哟,重用了很多魔術(shù)變量,實(shí)際實(shí)現(xiàn)繼承的過(guò)程惕稻,我們對(duì)類(lèi)的希望是抽象的 AST結(jié)構(gòu)竖共,擁有相同或相似的狀態(tài)結(jié)構(gòu),不同的狀態(tài)值和方法俺祠。于是公给,要引入構(gòu)造函數(shù)對(duì)類(lèi)進(jìn)行初始化。

構(gòu)造函數(shù)

由構(gòu)造函數(shù)組成的類(lèi)型蜘渣,我們使用 new 關(guān)鍵字新建一個(gè)新的對(duì)象淌铐。用這個(gè)方式重寫(xiě)上邊 abc 的例子,重用屬性和方法的時(shí)候使用構(gòu)造函數(shù)+原型模式蔫缸。

function Foo(y) { 
  this.y = y; 
}
Foo.prototype.x = 10;
Foo.prototype.calculate = function (z) { return this.x + this.y + z; };

var b = new Foo(20); 
var c = new Foo(30);

b.calculate(30); // ?
c.calculate(40); // ?
b.constructor === Foo, // ?
c.constructor === Foo, // ?
Foo.prototype.constructor === Foo // ?

上邊代碼改進(jìn)后腿准,可以看到多了兩個(gè)關(guān)鍵詞,在 Foo 構(gòu)造函數(shù)的原型對(duì)象中有一個(gè) contstructor 屬性指回了構(gòu)造函數(shù)本身拾碌。代碼邏輯圖如圖(3)所示

構(gòu)造函數(shù)的原型鏈變動(dòng)

構(gòu)造函數(shù)+原型對(duì)象 合在一起吐葱,被我們稱(chēng)為 JavaScript 中的類(lèi)。

ES6 對(duì)類(lèi)的封裝過(guò)程進(jìn)行了優(yōu)化校翔,引入 class extends super 等關(guān)鍵字弟跑,其實(shí)質(zhì)還是基于原型鏈的委托繼承又叫原型繼承。

執(zhí)行上下文

JavaScript 在 ES6之前是沒(méi)有塊級(jí)作用域的防症,基于這樣的人設(shè)孟辑,每段代碼都在自己的上下文環(huán)境中進(jìn)行求值。此時(shí)函數(shù)作用域就被認(rèn)為是局部作用域或者塊級(jí)作用域蔫敲。
JavaScript 是通過(guò)棧結(jié)構(gòu)來(lái)管理保存系統(tǒng)運(yùn)行時(shí)的上下文狀態(tài)轉(zhuǎn)換的饲嗽,稱(chēng)為執(zhí)行上下文棧。堆棧頂部的執(zhí)行上下文稱(chēng)為活動(dòng)上下文奈嘿。
觸發(fā)上下文堆棧中其他上下文的執(zhí)行上下文稱(chēng)作 caller貌虾,被觸發(fā)的執(zhí)行上下文稱(chēng)為callee,從函數(shù)角度更容易理解裙犹,執(zhí)行函數(shù)叫做 caller酝惧,函數(shù)的容器稱(chēng)為 callee。

一個(gè)局部作用域生效時(shí)伯诬,將其壓入執(zhí)行上下文堆棧晚唇,作為活動(dòng)執(zhí)行上下文

執(zhí)行上下文在 JavaScript 中也被實(shí)現(xiàn)為對(duì)象。對(duì)象中包含追蹤相關(guān)代碼執(zhí)行過(guò)程的屬性盗似。常見(jiàn)的幾個(gè)屬性有

  • 變量對(duì)象
  • 作用域鏈
  • this 指針

變量對(duì)象

變量對(duì)象是一個(gè)抽象概念哩陕,變量對(duì)象中存儲(chǔ)了在當(dāng)前上下文中存儲(chǔ)的變量和函數(shù)聲明灵份。

函數(shù)表達(dá)式不包含在變量對(duì)象之中荠察。

在全局執(zhí)行上下文中翼馆,變量對(duì)象就是全局對(duì)象本身埃跷,考慮以下這個(gè)例子。

var foo = 10;
function bar() {} // function declaration, FD 
(function baz() {}); // function expression, FE

console.log( this.foo == foo); // true 
console.log( window.bar == bar );// true 
console.log(baz); // ReferenceError, "baz" is not defined

此時(shí)心赶,全局變量對(duì)象的數(shù)據(jù)結(jié)構(gòu)如圖(4)所示

全局變量對(duì)象的數(shù)據(jù)結(jié)構(gòu)

其中函數(shù)表達(dá)式(閉包)并未進(jìn)入全局變量對(duì)象扣讼,所以在全局調(diào)用也會(huì)失敗。
當(dāng)一個(gè)變量對(duì)象進(jìn)入執(zhí)行時(shí)缨叫,稱(chēng)作活動(dòng)對(duì)象椭符。

活動(dòng)對(duì)象

活動(dòng)對(duì)象是特殊的變量對(duì)象,也是實(shí)際存在的對(duì)象耻姥,在函數(shù)被觸發(fā)的時(shí)候創(chuàng)建销钝,活動(dòng)對(duì)象中默認(rèn)包含 形參和arguments 對(duì)象。

函數(shù)表達(dá)式不在變量對(duì)象中琐簇,所以也不在活動(dòng)對(duì)象中

函數(shù)在運(yùn)行過(guò)程中蒸健,不僅可以使用活動(dòng)對(duì)象中的屬性和方法,還可以使用父容器的屬性和方法婉商。這種可以使用的實(shí)現(xiàn)原理就基于執(zhí)行上下文的第二個(gè)屬性似忧,作用域鏈。

作用域鏈

作用域鏈?zhǔn)遣檎易兞恐档逆準(zhǔn)浇Y(jié)構(gòu)丈秩,是一個(gè)對(duì)象列表盯捌。其實(shí)現(xiàn)原理與原型鏈相似。

如果一個(gè)變量在當(dāng)前作用域中未找到定義癣籽,則遞歸上溯到父容器中查找挽唉,直到查找到作用域鏈的尾部滤祖。未找到返回 undefinded

如果函數(shù)引用了一個(gè)不是當(dāng)前上下文中的標(biāo)識(shí)符(變量筷狼,參數(shù),方法)那么被引用的這個(gè)標(biāo)識(shí)符被稱(chēng)作自由變量匠童,搜索自由變量的過(guò)程就是依次遍歷作用域鏈的過(guò)程埂材。

看一個(gè)例子

var x = 10;
(function foo() { 
  var y = 20; 
  (function bar() { 
      var z = 30;  
      console.log(x + y + z); 
  })(); 
})();

其中三個(gè)變量 x,y,z 在調(diào)用時(shí)發(fā)現(xiàn),x,y 針對(duì) bar 而講屬于自由變量汤求,需要搜索作用域鏈獲取值俏险。其搜索過(guò)程如圖(5)所示。

作用域鏈的查找

當(dāng)使用 with/catch 語(yǔ)句時(shí)扬绪,將其中的語(yǔ)句插入作用域鏈的前端竖独,使得被插入的片段既包含proto屬性又包含parent屬性,原型鏈的查找邏輯中優(yōu)先查找proto屬性鏈挤牛。

活動(dòng)變量在函數(shù)執(zhí)行完畢后莹痢,將交付垃圾回收機(jī)制回收,如果不想被回收掉。那么就需要引入閉包的概念竞膳。

閉包

閉包是為了讓函數(shù)成為一等公民航瞭,解決函數(shù)作為參數(shù)和函數(shù)作為返回值時(shí)作用域鏈存活的問(wèn)題。

當(dāng)函數(shù)作為參數(shù)或返回值時(shí)坦辟,函數(shù)中自由變量訪(fǎng)問(wèn)的容器函數(shù)尚可訪(fǎng)問(wèn)(未被銷(xiāo)毀)刊侯,該函數(shù)會(huì)在創(chuàng)建的時(shí)候,保存容器函數(shù)的作用域鏈锉走。

閉包創(chuàng)建的函數(shù)作用域鏈 = 活動(dòng)對(duì)象 + 父函數(shù)作用域鏈

保存是為了未來(lái)訪(fǎng)問(wèn)的時(shí)候能夠訪(fǎng)問(wèn)到滨彻。此時(shí),父函數(shù)的作用域鏈被調(diào)用函數(shù)凍結(jié)了挠日,稱(chēng)此為靜態(tài)作用域疮绷。靜態(tài)作用域是一門(mén)語(yǔ)言能夠創(chuàng)造閉包的必需條件,JavaScript 就具備這個(gè)條件嚣潜,現(xiàn)在給閉包下一個(gè)準(zhǔn)確的定義:

閉包是一個(gè)方便查找自由變量的代碼塊冬骚,以塊級(jí)作用域?yàn)榛A(chǔ)構(gòu)造靜態(tài)作用域,以保存父容器作用域鏈的集合體懂算。

很抱歉只冻,又說(shuō)迷糊了。什么是閉包计技?JavaScript 中所有函數(shù)都是閉包喜德。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垮媒,隨后出現(xiàn)的幾起案子舍悯,更是在濱河造成了極大的恐慌,老刑警劉巖睡雇,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萌衬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡它抱,警方通過(guò)查閱死者的電腦和手機(jī)秕豫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)观蓄,“玉大人混移,你說(shuō)我怎么就攤上這事∥甏” “怎么了歌径?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)亲茅。 經(jīng)常有香客問(wèn)我回铛,道長(zhǎng)金矛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任勺届,我火速辦了婚禮驶俊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘免姿。我一直安慰自己饼酿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布胚膊。 她就那樣靜靜地躺著故俐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪紊婉。 梳的紋絲不亂的頭發(fā)上药版,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音喻犁,去河邊找鬼槽片。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肢础,可吹牛的內(nèi)容都是我干的还栓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼传轰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剩盒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起慨蛙,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辽聊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后期贫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跟匆,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年唯灵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贾铝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隙轻。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埠帕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玖绿,到底是詐尸還是另有隱情敛瓷,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布斑匪,位于F島的核電站呐籽,受9級(jí)特大地震影響锋勺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狡蝶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一庶橱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贪惹,春花似錦苏章、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至硼端,卻和暖如春并淋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背珍昨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工县耽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镣典。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓酬诀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親骆撇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞒御,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 作者:Dmitry A. Soshnikov編譯地址:http://dmitrysoshnikov.com/ecm...
    IT程序獅閱讀 3,341評(píng)論 2 12
  • 原文:http://dmitrysoshnikov.com/ecmascript/javascript-the-c...
    jaysoul閱讀 474評(píng)論 0 0
  • 繼承 一、混入式繼承 二神郊、原型繼承 利用原型中的成員可以被和其相關(guān)的對(duì)象共享這一特性肴裙,可以實(shí)現(xiàn)繼承,這種實(shí)現(xiàn)繼承的...
    magic_pill閱讀 1,062評(píng)論 0 3
  • 1,javascript 基礎(chǔ)知識(shí) Array對(duì)象 Array對(duì)象屬性 Arrray對(duì)象方法 Date對(duì)象 Dat...
    Yuann閱讀 911評(píng)論 0 1
  • ①為了應(yīng)對(duì)自己所學(xué)的專(zhuān)業(yè)涌乳,自己有點(diǎn)欲望買(mǎi)個(gè)《本草綱目》看看蜻懦,,誰(shuí)叫我學(xué)生物工程嘞夕晓,宛乃,想讓自己專(zhuān)業(yè)有所發(fā)展就需自己多...
    雷帥帥閱讀 140評(píng)論 0 0