原文:https://gist.github.com/4158604
本文作者是Peter Rybin,Chrome開發者工具團隊成員.
本文中,我們將通過使用Chrome的開發者工具,來學習JavaScript中的兩個重要概念"閉包"和"內部屬性".
閉包
首先要講的是閉包(closure) – JavaScript中最有名的東西之一.一個閉包就是一個使用了外部變量的函數.查看下面的例子:
function A(a, b, c) { var ar = [a, b, c]; return function B(i) { return ar[i]; }; } var b = A('Here', 'I', 'am'); console.log( b(1) );
函數聲明之后的第一條語句調用了函數A,函數A創建了一個值為數組[a,b,c]的局部變量ar,返回了一個函數B
(存儲在了變量b中
),然后運行結束.
第二條語句調用了函數B
(b
),返回並打印出了數組ar.這就意味着A中的數組ar在
A結束執行后仍然存在
.但是它存儲在什么地方呢?當然,在b上
!但是究竟是存在b的哪里呢?某個屬性中?不是的.
這是JavaScript語言的一個核心特性:一個函數可以持有外層作用域的變量,並且除了調用該函數以外沒有任何其他方法可以訪問到這些變量.
從現在開始,chrome的開發者工具可以讓閉包中的外部變量現形.在監控表達式(Watch Expressions)面板中查看函數實例b,展開它的屬性后,
應該會有一個稱為<function scope>的
子節點.所有被綁定的閉包變量都能在這里看到,這些變量就是在函數調用時可能會被用到的變量.
內部屬性
開發者工具還能顯示出另外一個東西,叫做內部屬性(internal property).
假設你的代碼中有個變量s,而且還執行了下面這樣的操作
:
s.substring(1, 4) // 返回'ell'
你覺得s肯定是個字符串值嗎? 這可不一定
.它也有可能是個字符串包裝對象.嘗試下面的監控表達式:
"hello"
Object("hello")
第一個表達式是一個普通的字符串字面量,第二個是一個功能完整(full-featured)的對象.令人費解的是,這兩個值幾乎有完全相同的表現.但是第二個表達式才真正的擁有自己的屬性,並且你也可以在它身上添加自定義的屬性.展開它的所有屬性你會看到,它不是一個完全常規的對象:它有一個內部屬性[[PrimitiveValue]] ,被包裝的字符串值就存儲在這個屬性里面.你不能在JavaScript代碼中訪問到這個內部屬性,但是你能在開發者工具的中看到它.
還有哪些值擁有內部屬性?那就是綁定函數(bound function).綁定函數也算是一種包裝對象,只不過被包裝的是個函數.嘗試執行下面的兩條語句:
function Sum(a, b) { return a + b; } var inc = Sum.bind(null, 1); // 將形參a綁定為1,this綁定為null
如果你把Sum和
inc放在監控表達式面板中對比一下,你會看到
,它們都是函數,但inc是一個不透明(non-transparent )的函數
:你看不到它的函數體內容,也不能看到它定義時的作用域.
這就是綁定函數的工作原理.在開發者工具中,你會看到[[TargetFunction]], [[BoundArgs]]以及[[BoundThis]]這三個內部屬性.它們都表明了inc是一個綁定函數
,以及一些更具體的信息:inc綁定的目標函數是Sum
,綁定了一個參數1,綁定的this值是n
ull
.