介紹下javascript中的最重要幾個概念執行環境、做用域、以及原型鏈。理解了這兩個概念才能寫出好的javascript代碼,特別是在理解閉包、寫插件或者使用使用框架快速上手。
javascript
一、執行環境(摘抄於javascript高級程序設計):
html
執行環境(execution context,爲簡單起見,有時也稱爲「環境」)是 JavaScript 中最爲重要的一個概念。執行環境定義了變量或函數是否有權訪問的其餘數據,決定了它們各自的行爲。每一個執行環境都有一個與之關聯的變量對象(variable object) ,環境中定義的全部變量和函數都保存在這個對象中。雖然咱們編寫的代碼沒法訪問這個對象,但解析器在處理數據時會在後臺使用它。前端
全局執行環境是最外圍的一個執行環境。根據 ECMAScript 實現所在的宿主環境不一樣,表示執行環境的對象也不同。在 Web 瀏覽器中,全局執行環境被認爲是 window 對象(第 7 章將詳細討論) ,所以全部全局變量和函數都是做爲 window 對象的屬性和方法建立的。某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬(全局執行環境直到應用程序退出——例如關閉網頁或瀏覽器——時纔會被銷燬) 。java
每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。ECMAScript 程序中的執行流正是由這個方便的機制控制着。 瀏覽器
二、做用域(摘抄於javascript高級程序設計):閉包
當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈(scope chain) 。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。若是這個環境是函數,則將其活動對象(activation object)做爲變量對象。活動對象在最開始時只包含一個變量,即 arguments 對象(這個對象在全局環境中是不存在的) 。做用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。框架
ps:在C中,每次執行一個函數,都會在內存的棧從新中劃定一個區域,加載實參、本地變量、返回地址等信息。這片新的內存棧就是執行環境,而執行鏈就是傳遞的指針(不是具體變量、並且每一個函數都是傳遞指針)。全部本棧幀能夠範圍本身的變量和前面棧幀的變量,直到全局變量(window對象)。而前面的棧幀不能訪問後面的棧幀變量。函數
三、案例:
測試
<!DOCTYPE html> <html> <head> <script> var one="One"; function firstFunction(){ var two="Two"; console.log("Two:--"+one); console.log("Two:--"+two); //console.log("Two:--"+three); function sencondeFunction(){ var three="three"; console.log("three:--"+one); console.log("three:--"+two); console.log("three:--"+three); } sencondeFunction(); } firstFunction(); </script> </head> </html>
正常
this
把註釋部分打開(前一個棧幀、訪問後一個棧幀。會出現錯誤)
2、原型鏈
摘抄於《javascript高級程序設計》的概念:ECMAScript 中描述了原型鏈的概念,並將原型鏈做爲實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。簡單回顧一下構造函數、原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。那麼,假如咱們讓原型對象等於另外一個類型的實例,結果會怎麼樣呢?顯然,此時的原型對象將包含一個指向另外一個原型的指針,相應地,另外一個原型中也包含着一個指向另外一個構造函數的指針。假如另外一個原型又是另外一個類型的實例,那麼上述關係依然成立,如此層層遞進,就構成了實例與原型的鏈條。這就是所謂原型鏈的基本概念
一、首先一張圖:(摘抄於知乎)
二、開始瞎掰
a、咱們的測試代碼
<!DOCTYPE html> <html> <head> <script> function firstFunction(){ this.two="two"; this.age="age"; } firstFunction.prototype.a="a"; var first=new firstFunction(); console.log(first); console.log(firstFunction.prototype); </script> </head> </html>
b、瀏覽器告訴咱們真像
聯繫上面的代碼,咱們先畫一張圖
__proto__:屬性能夠看做是原型鏈。
constructor:看做是java的字節碼文件,只是用戶定義的class對象,尚未在內存中實現。
prototype:則是class在堆中的實現,才具備使用價值。
接着將firstFunction.prototype中的constructor繼續展開(後面的主語):
從這個圖咱們看見,prototype原型的__proto__是Object對象。而後,constructor的__proto__是function()對象,而後咱們繼續做畫:
在圖上添加2條紅線後,咱們還須要展開 __ptoto__:function(),以下圖
從這咱們發現function的__proto__也是Object,再把咱們的畫補齊:
後面的__proto__就展開也沒有__proto__、constructor、prototype了,就不上圖了。這裏咱們發現這圖還有些變扭。根據《javascript高級程序設計》中描述:有prototype必然有對應的constructor,有constructor比然有對應的prototype。補全
全部的function的__proto__都是function.prototype。而全部prototype的__proto__都是object.prototype。最後object.prototype的__proto__指向的null