構造函數、原型對象、實例
1、每個函數都有一個prototype屬性,該屬性指向函數的原型對象,原型對象的用途是可以讓所有對象實例共享它所包含的屬性和方法。
2、默認情況下,每個原型對象都會自動獲得一個constructor屬性,該屬性指向prototype屬性所在的函數。constructor屬性最初是用來標識對象類型的,但檢測對象類型,還是instanceof更可靠些(因爲對象的constructor可被改寫,指向的函數不同於原型對象construtor指向的函數):
person1 instanceof Person // 檢測Person.prototype是否在person1的原型鏈上,在就返回true。
3、當調用構造函數創建一個對象實例後,該實例的內部將包含一個指針[[Prototype]](內部屬性),指向構造函數的原型對象。Firefox、Safari和Chrome等瀏覽器在每個對象上都加了一個__proto__,來訪問[[Prototype]],而在其他實現中,這個屬性則完全不可見。對象實例和構造函數沒有直接關係。可以通過isPrototypeOf()來確定是否爲對象的原型對象:
Person.prototype.isPrototypeOf(person1);
ES5中增加了新方法Object.getPrototypeOf(),返回[[Prototype]]的值(IE 9+,Firefox 3.5+,Safari 5+,Opera 12+,Chrome):
Object.getPrototypeOf(person1) === Person.prototype // true
4、讀取對象某個屬性時,會先搜索該對象實例,如果找到同名屬性,返回該屬性值;如果沒有找到,繼續搜索該對象的原型對象,這是多個對象實例共享原型對象屬性和方法的基本原理。對象實例中的屬性會屏蔽原型對象中的同名屬性,使用delete操作符可以刪除實例屬性,從而能重新訪問原型中的屬性。
function Person() {}
Person.prototype.name = 'Nicholas';
Person.prototype.sayName = function () {
alert(this.name);
}
let person1 = new Person();
let person2 = new Person();
person1.name = 'Greg';
alert(person1.name); // 'Greg'
alert(person2.name); // 'Nicholas'
delete person1.name;
alert(person1.name); // 'Nicholas
hasOwnProperty():若屬性爲實例屬性,返回true:
person1.hasOwnProperty('name') // 如果person1實例本身有name屬性,返回true
in操作符:無論該屬性在實例中還是原型中,只要能通過對象訪問,就返回true。
'name' in person1 // 檢測person1是否有name屬性,無論是實例還是原型屬性
for-in: 返回的是所有能通過對象訪問的、可枚舉的屬性,包括實例屬性和原型屬性。屏蔽了原型中不可枚舉屬性的實例屬性也會被返回,因爲按規定,開發人員定義的屬性都是可枚舉的(IE8及更早版本中例外)。
Object.keys():返回對象上所有可枚舉的實例屬性(IE 9+,Firefox 4+,Safari 5+,Opera 12+ 和 Chrome)。
Object.getOwnPropertyNames():返回對象上所有實例屬性,包括不可枚舉的(IE 9+,Firefox 4+,Safari 5+,Opera 12+ 和 Chrome)。
用new創建實例時經歷的步驟
- 創建一個空的js對象,即{};
- 將該空對象[[Prototype]]屬性鏈接到構造函數的原型對象 ;
- 將步驟1新創建的對象作爲
this
的上下文 ; - 執行構造函數內的代碼(爲這個新對象添加屬性);
- 如果該函數沒有返回對象,則返回
this
。
用代碼示意,記函數A爲構造函數, new A() 實際做了如下事情:
let obj = {};
obj.__proto__ = A.prototype;
return A.call(obj) || obj;
1、new的過程中並不涉及constructor屬性。改變A.prototype.constructor,也不影響new A()的執行過程:
function A () { console.log('A'); }
function B () { console.log('B'); }
B.prototype.constructor = A;
let b = new B(); // 'B'
console.log(b.__proto__ === B.prototype); // true
console.log(b.construtor === A); // true
2、new A也可以創建實例,和new A()的區別是new A不可以傳入參數。