javascript函數執行過程:javascript
1. 爲函數建立一個執行環境前端
2. 複製函數的 [[scopes]] 屬性中的對象構建起執行環境的做用鏈域java
3. 建立函數活動對象並推入執行環境做用鏈域的前端閉包
4. 執行代碼函數
5. 銷燬執行環境和活動對象(閉包狀況下活動對象仍被引用沒被銷燬)this
用例子來講明:spa
function Person(name) { this.getName = function() { return name; }; this.setName = function(value) { name = value; }; } var person = new Person("Candy"); alert(person.getName()); //"Candy" person.setName("Greg"); alert(person.getName()); //"Greg"
以調用Person()構造函數、setName() 函數爲例,函數執行過程當中,各對象的關係以下:對象
(1)未調用前,只存在全局變量對象blog
全局變量中定義了Person() 構造函數,Person做爲全局變量對象的一個屬性,[[scopes]] 保存着Person() 構造函數的做用鏈域,由於Person() 構造函數是定義在全局變量對象裏面的ip
Person爲何會有 [[scopes]] 屬性???
在建立函數時,會建立一個預先包含全局變量對象的做用鏈域,這個做用鏈域被保存在內部的 [[scopes]] 屬性中
(2)new Person() 建立對象,調用以後,存在全局變量對象,Person對象
用new調用構造函數會通過如下4個步驟:
1. 建立一個新對象
2. 將構造函數的做用域賦給新對象(所以this就指向這個新對象——person,這也是變量name爲何不是對象屬性的緣由)
3. 執行構造函數中的代碼(爲新對象添加屬性)
4. 返回新對象
重點說第3個過程,執行過程如圖:
在調用(執行)函數時,會爲函數建立一個執行環境,而後經過複製函數的 [[scopes]] 屬性中的對象構建起執行環境的做用鏈域。此後,又有一個活動對象(Person的活動對象)被建立並推入執行環境做用鏈域的前端,因此圖中的做用鏈域有2層。
調用完畢後,返回Person對象
Person執行環境在函數執行完畢後銷燬,Person活動對象沒有被銷燬
爲何Person活動對象沒有被銷燬???
這種狀況屬於閉包:Person活動對象仍然被getName、setName的 [[scopes]] 引用,即仍在做用鏈域中,因此沒有被銷燬
注意:
Person() 構造函數中定義了getName()、setName() 兩個函數,還有函數的參數name
new Person() 建立的 Person對象 的屬性只有getName()、setName(),沒有name屬性,name不是用this聲明的,不是Person對象的屬性
(3)調用setName(),存在全局變量對象,Person對象
建立setName() 的執行環境,而後經過複製函數的 [[scopes]] 屬性中的對象構建起執行環境的做用鏈域。此後,又有一個活動對象(setName的活動對象)被建立並推入執行環境做用鏈域的前端,因此圖中的做用鏈域有3層。
函數執行完畢後:
setName執行環境和setName活動對象都被銷燬,由於setName活動對象沒有被引用
- 其餘:
經過這個例子,也很好理解爲何person.name是undefined,由於person對象沒有name這個屬性,getName、setName能訪問name屬性是由於它們經過做用鏈域訪問到了Person活動對象中的name屬性
PS:用構造函數建立的對象是獨立的