在前端面試中,原型和原型鏈始終是一個避不開的問題,今天就弄明白!
原型和原型鏈
- 對象的創建方式
- 工廠模式
- 構造函數模式
- 原型模式
- 原型和原型鏈
- 實踐
對象的創建方式
原型和原型鏈都是關于對象的內容,先來看一下JavaScript中對象的構建方式。
工廠模式
function createPerson(name, age, job) { let o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { console.log(this.name); }; return o;
}
let person1 = createPerson("Nicholas", 29, "Software Engineer");
let person2 = createPerson("Greg", 27, "Doctor");
構造函數模式
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); };
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
要創建 Person 的實例,應使用 new 操作符。以這種方式調用構造函數會執行如下操作。
(1) 在內存中創建一個新對象。
(2) 這個新對象內部的[[Prototype]]特性被賦值為構造函數的 prototype 屬性。
(3) 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)。
(4) 執行構造函數內部的代碼(給新對象添加屬性)。
(5) 如果構造函數返回非空對象,則返回該對象;否則,返回剛創建的新對象。
原型模式
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // "Nicholas"
let person2 = new Person();
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true
無論何時,只要創建一個函數,就會按照特定的規則為這個函數創建一個 prototype 屬性(指向
原型對象)。默認情況下,所有原型對象自動獲得一個名為 constructor 的屬性,指回與之關聯的構
造函數。對前面的例子而言,Person.prototype.constructor 指向 Person。然后,因構造函數而
異,可能會給原型對象添加其他屬性和方法。
原型和原型鏈
- Person.prototype.constructor == Person // 準則1:原型對象(即Person.prototype)的constructor指向構造函數本身
- person01.proto == Person.prototype // 準則2:實例(即person01)的__proto__和原型對象指向同一個地方
構造函數、原型和實例的關系:每個構造函數都有一個原型對象,原型有一個屬性指回構造函數,而實例有一個內部指針指向原型。如果原型是另一個類型的實例呢?那就意味著這個原型本身有一個內部指針指向另一個原型,相應地另一個原型也有一個指針指向另一個構造函數。這樣就在實例和原型之間構造了一條原型鏈。
實例的隱式原型指向構建該實例的類的顯式原型。
驗證一下這句話:
p.proto===person.prototype
person.prototype.proto===Object.prototype
Object.prototype.proto===null
注意有的瀏覽器可能已經廢除了__proto__屬性,改用Object.getPrototypeOf()方法來獲取對象的原型。
原型鏈例子:
示意圖:
注意:如果通過對象自變量的方式添加新方法,會導致原型鏈失效