JavaScript內(nèi)部原理實(shí)踐——真的懂JavaScript嗎?

通過翻譯了Dmitry A.Soshnikov的關(guān)于ECMAScript-262-3 JavaScript內(nèi)部原理的文章,
從理論角度對JavaScript中部分特性的內(nèi)部工作機(jī)制有了一定的了解。
但是,鄧爺爺說過:“實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)”
所以,我打算通過從內(nèi)部原理來解釋一些經(jīng)常在筆試或者面試中遇到的關(guān)于JavaScript語言層面的題目來進(jìn)一步學(xué)習(xí)和掌握J(rèn)avaScript內(nèi)部工作原理。

那么,首先就是要去找那些題目,google了一圈終于找到了來自Dmitry Baranovskiy的非常著名的5個(gè)問題,
這5個(gè)問題,NCZ給出了非常清楚的解釋。
不過,我還是想嘗試下從low-level——JavaScript內(nèi)部工作機(jī)制的角度去解釋下這些問題。

好吧,我承認(rèn)我廢話很多,那就開始吧。

問題 #1


<pre><code>if (!("a" in window)) {
var a = 1;
}
alert(a);</code></pre>

  • 正確答案: undefined

解釋:
這個(gè)問題,初一看感覺答案很自然是1。因?yàn)閺纳系较聢?zhí)行下去,if語句中的條件應(yīng)該為 true,因?yàn)?a"的確是沒有定義啊。
隨后,順理成章地進(jìn)入 var a = 1;,最后,alert出來就應(yīng)該是1。

而事實(shí)上,從JavaScript內(nèi)部工作原理去看,在變量對象中講過,
JavaScript處理上下文分為兩個(gè)階段:

  • 進(jìn)入執(zhí)行上下文
  • 執(zhí)行代碼

可以理解為,第一個(gè)階段是靜態(tài)處理階段,第二個(gè)階段為動態(tài)處理階段。

而在靜態(tài)處理階段,就會創(chuàng)建 變量對象(variable object),并且將變量申明作為屬性進(jìn)行填充。
到了執(zhí)行階段,才會根據(jù)執(zhí)行情況,來對變量對象中屬性(就是申明的變量)的值進(jìn)行更新。

針對這個(gè)問題,其實(shí)際過程如下:

  • 進(jìn)入執(zhí)行上下文: 創(chuàng)建VO,并填充變量申明 a,VO如下所示:
    <pre><code>VO(global) = {
    a: undefined
    }</code></pre>
    所以,這個(gè)時(shí)候,a其實(shí)已經(jīng)存在了。

  • 執(zhí)行代碼: 進(jìn)入 if語句,發(fā)現(xiàn)條件判斷 "a" in windowtrue。于是就不會進(jìn)入if代碼塊,直接執(zhí)行alert語句,因此,最終為undefined。

問題 #2


<pre><code>var a = 1,
b = function a(x) {
x && a(--x);
};
alert(a);</code></pre>

  • 正確答案: 1

解釋:
這個(gè)問題,第一反應(yīng)可能會是將 function a打印出來。因?yàn)槊髅骶涂吹搅薴unction a了??此?,也順其自然。

但是,事實(shí)并非如此。還是和此前一個(gè)問題一樣。從兩個(gè)階段來分析:

  • 進(jìn)入執(zhí)行上下文: 這個(gè)時(shí)候要注意了, b = function a(){},這里的 function a并非函數(shù)申明,因?yàn)檎麄€(gè)這個(gè)句話屬于賦值語句(assignment statement),所以,這里的 function a會被看作是函數(shù)表達(dá)式。
    函數(shù)表達(dá)式是不會對VO造成影響的。所以,這個(gè)時(shí)候VO中其實(shí)只有 a和x(函數(shù)形參)
    <pre><code>VO(global) = {
    a: undefined,
    b: undefined
    }</code></pre>

  • 執(zhí)行代碼: 這個(gè)時(shí)候a的值修改為1:
    <pre><code>VO(global) = {
    x: undefined,
    a: 1
    }</code></pre>

所以,最后alert(a)的結(jié)果是1。

問題 #3


<pre><code>function a(x) {
return x * 2;
}
var a;
alert(a);</code></pre>

  • 正確答案: 函數(shù)a

解釋:
這個(gè)問題,很多人可能會以為是: undefined。理由可能是,明明看到了 var a定義在了function a的后面,感覺應(yīng)該會覆蓋之前a的申明。

事實(shí)又是怎樣的呢? 老套路,從兩個(gè)階段來分析:

  • 進(jìn)入執(zhí)行上下文: 這里出現(xiàn)了名字一樣的情況,一個(gè)是函數(shù)申明,一個(gè)是變量申明。那么,根據(jù)變量對象
    介紹的,填充VO的順序是: 函數(shù)的形參 -> 函數(shù)申明 -> 變量申明。
    上述例子中,變量a在函數(shù)a后面,那么,變量a遇到函數(shù)a怎么辦呢?還是根據(jù) 變量對象中介紹的,當(dāng)變量申明遇到VO中已經(jīng)有同名的時(shí)候,不會影響已經(jīng)存在的屬性。因此,VO如下所示:
    <pre><code>VO(global) = {
    a: 引用了函數(shù)申明“x”
    }</code></pre>

  • 執(zhí)行代碼:啥也沒變化

所以,最終的結(jié)果是:函數(shù)a。

問題 #4


<pre><code>function b(x, y, a) {
arguments[2] = 10;
alert(a);
}
b(1, 2, 3);</code></pre>

  • 正確答案: 10

解釋:
個(gè)人感覺這個(gè)問題其實(shí)不是很復(fù)雜。這里也不需要從兩個(gè)階段去分析了。根據(jù) 變量對象中介紹的,arguments對象的properties-indexes和實(shí)際傳遞的參數(shù)是共享的
也就是說,通過arguments[2]修改的參數(shù),也會影響到a,所以,這里的值是10。但是,要注意的是和實(shí)際傳遞的值,所以,如果把上述問題改成如下形式:
<pre><code>function b(x, y, a) {
arguments[2] = 10;
alert(a);
}
b(1, 2);</code></pre>

結(jié)果就會是: undefined。因?yàn)?,并沒有傳遞a的值。

問題 #5


<pre><code>function a() {
alert(this);
}
a.call(null);</code></pre>

  • 正確答案: 全局對象(window)

解釋:
這個(gè)問題,可能會比較困惑。因?yàn)槎甤all的童鞋都會覺得,call的時(shí)候把null傳遞為了當(dāng)前的上下文了。里面的this應(yīng)該是null才對啊。

事實(shí)卻是: 前面都沒錯(cuò),this會是null。但是,this中介紹過,null是沒有任何意義的,因此,最終會變成全局對象。
所以,這里結(jié)果就變成了全局對象。 這里還有ECMAScript-262-3標(biāo)準(zhǔn)文檔中的一句話作為證據(jù):
“If thisArg is null or undefined, the called function is passed the global object as the this value. Otherwise, the called function is passed ToObject(thisArg) as the this value.”

總結(jié)


上面這5個(gè)問題其實(shí)也只是牽涉到了JavaScript內(nèi)部原理中的部分知識點(diǎn),要想了解更多,還是建議讀完JavaScript內(nèi)部原理系列
以及去看Dmitry A.Soshnikov的文章。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容