原型對象概念
無論什么時候,只要創建一個新函數,就會根據一組特定的規則為該函數創建一個prototype屬性,這個屬性指向函數的原型對象。在默認情況下,所有原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性包含一個指向 prototype 屬性所在函數的指針。而通過這個構造函數,可以繼續為原型對象添加其他屬性和方法。創建了自定義的構造函數后,其原型對象默認只會取得 constructor 屬性;至於其他方法,則都從 Object 繼承而來。當調用構造函數創建一個新實例后,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。ECMA-262第5版管這個指針叫 [[Prototype]] 。腳本中沒有標准的方式訪問 [[Prototype]],但Firefox、Safari和Chrome在每個對象上都支持一個屬性__proto__;而在其他實現中,這個屬性對腳本是完全不可見的。不過,要明確的真正重要的一點就是,這個連接存在於示例和構造函數的原型對象之間,而不是存在於實例和構造函數之間。
這段話基本概述了構造函數、原型、示例之間的關系,下圖表示更清晰
通過new創建對象經歷4個步驟
1、創建一個新對象;[var o = new Object();]
2、將構造函數的作用域賦給新對象(因此this指向了這個新對象);[Person.apply(o)] [Person原來的this指向的是window]
3、執行構造函數中的代碼(為這個新對象添加屬性);
4、返回新對象。
通過代碼還原new的步驟:
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { alert(this.name); } } function createPerson(P) { var o = new Object(); var args = Array.prototype.slice.call(arguments, 1); o.__proto__ = P.prototype; P.prototype.constructor = P; P.apply(o, args); return o; }
new()過程中的原型鏈維護
new總是因為建立原型繼承樹而存在的,如果沒有new過程參與,則當
obj = new MyObjecEx()時,我們無法通過instanceof運算:obj instanceof MyObject 來了解obj在繼承樹上的關系。但是事實上這一過程並不需要MyObject的參與。因為instanceof只檢查prototype鏈,並不檢查函數本身。
new新對象的創建,就是不斷地為this賦值而已,只不過new會為產生的對象維護<obj>.constructor屬性。