[英]Javascript prototype constructor explanation
我有以下代碼片段代表了此問題的答案:
您正在創建一個必須從Employee類繼承的名為Consultant的類。 Consultant類必須修改繼承的PayEmployee方法。 將來的顧問實例必須使用重寫的方法創建。
function Employee() {}
Employee.prototype.PayEmployee = function ( ){
alert(‘Hi there!’);
}
function Consultant () {
Employee.call(this); // X
}
Consultant.prototype = new Employee();
Consultant.prototype.constructor = Consultant; // X
Consultant.prototype.PayEmployee = function ()
{
alert(‘Pay Consultant’);
}
我真的無法解決這個問題。 為什么標有“ X”的行是必需的? 我不能只編寫沒有這些行的代碼,它會完全一樣嗎? 根據我的測試,這沒有什么區別:
有人可以向我解釋這兩個代碼之間的區別是什么,為什么我應該在另一個之上使用它?
提前致謝
我不能只編寫沒有這些行的代碼,它會完全一樣嗎?
不,在一般情況下您確實需要兩者。 是的,在某些特定情況下,您可能沒有一個或兩個都逃脫了。
首先,請注意該代碼中有一個常見錯誤。 這是不正確的:
Consultant.prototype = new Employee(); // INCORRECT
它應該是:
Consultant.prototype = Object.create(Employee.prototype);
new Employee
做兩件事:
它創建一個使用Employee.prototype
作為原型的對象
它運行在代碼Employee
,他們的工作是初始化實例Employee
通過設置屬性和對等this
但是我們還不想要第二部分。 Consultant.prototype
不是Employee
的實例 ,它是一個對象,這將成為通過創建的對象的原型new Consultant
(這將是一個實例Employee
,也是Consultant
)。 (這是JavaScript的構造函數和prototype
屬性偏離標准原型繼承的地方。您可以在JavaScript中進行標准原型繼承,但在這樣做時,請勿使用構造函數, prototype
屬性或new
。)
Object.create
只是完成這兩個任務中的第一個任務:使用Employee.prototype
作為原型創建一個對象。 因此,我們最終在Consultant.prototype
上使用了一個對象,該對象使用Employee.prototype
作為其原型,但沒有調用Employee
並讓它運行其按實例的初始化代碼。 (我們稍后在第二行中用X標記。)
但是為什么我們不希望Employee
按實例進行初始化呢? 同樣,因為Consultant.prototype
也不是Employee
實例,所以將其初始化為一個實例沒有任何意義。 一方面:如果Employee
需要用於初始化它返回的對象的參數,該怎么辦? 構造Consultant.prototype
時,我們將通過什么? 在這一點上,我們沒有任何特定於實例的信息來傳遞它。
因此,相反,我們只是在上面執行#1(通過Object.create
),然后離開#2(調用Employee
),直到構造實例為止,方法是從Consultant
調用Employee.call(this)
(在下文中進行更多介紹)。
標記行:
Employee.call(this);
該行對於使Employee
有機會對正在創建的實例進行初始化至關重要。 沒有它, Employee
就沒有機會做它的工作:初始化實例的Employee
部分。 例如,假設Employee
看起來像這樣:
function Employee() {
this.paySchedule = "biweekly";
}
沒有Employee.call(this);
行在Consultant
,通過new Consultant
創建的實例將缺少該屬性。
您的第二條標記行:
Consultant.prototype.constructor = Consultant;
我們需要這樣做,因為Consultant.prototype
的constructor
屬性應指向Consultant
,但如果不這樣做,它將指向Employee
。
它過去沒什么大不了的,因為盡管在規范中定義了constructor
以默認情況下以這種方式設置,但在使用它的規范中沒有任何內容。 在ES2015(又稱為“ ES6”)中發生了變化:現在,有時會使用constructor
屬性(例如,在promise中)。
這是說明Employee
具有參數的情況的示例。 我還切換了PayEmployee
到payEmployee
,以符合JavaScript中壓倒性的約定,即最初僅限制構造函數:
function Employee(name, title) { this.name = name; this.title = title; } Employee.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ")"); }; function Consultant(name) { Employee.call(this, name, "Consultant"); } Consultant.prototype = Object.create(Employee.prototype); Consultant.prototype.constructor = Consultant; Consultant.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); }; var e = new Employee("Joe Bloggs", "Engineer"); e.payEmployee(); var c = new Consultant("John Smith"); c.payEmployee();
最后,如果我沒有指出從ES2015開始,我們會為此感到失望,因為我們擁有此類的class
語法,可以在較舊的環境中根據需要進行轉換:
class Employee { constructor(name, title) { this.name = name; this.title = title; } payEmployee() { console.log("Time to pay " + this.name + " (" + this.title + ")"); } } class Consultant extends Employee { constructor(name) { super(name, "Consultant"); } payEmployee() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); } } const e = new Employee("Joe Bloggs", "Engineer"); e.payEmployee(); const c = new Consultant("John Smith"); c.payEmployee();
您詢問是否Consultant.prottype = new Employee();
總是錯的。 讓我們使用new Employee
來獲取代碼版本,並刪除用X標記的兩行,並向Employee添加一個看起來應該可以使用的功能,但最終會導致各種混亂:
function Employee() { this.staff = []; } Employee.prototype.payEmployee = function() { console.log("Paying employee"); }; function Consultant(name) { } Consultant.prototype = new Employee(); Consultant.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); }; var jill1 = new Employee(); jill1.staff.push("Bob"); jill1.staff.push("Jane"); var john1 = new Employee(); john1.staff.push("Russell"); john1.staff.push("Mohammed"); console.log("Jill's staff (1): " + jill1.staff.join(", ")); console.log("John's staff (1): " + john1.staff.join(", ")); // so far so good, but what if we use Consultant instead? var jill2 = new Consultant(); jill2.staff.push("Bob"); jill2.staff.push("Jane"); var john2 = new Consultant(); john2.staff.push("Russell"); john2.staff.push("Mohammed"); console.log("Jill's staff (2): " + jill2.staff.join(", ")); console.log("John's staff (2): " + john2.staff.join(", ")); // ??? how is it they both have all four staff?!
問題是staff
數組不是在每個實例上都在Consultant.prototype
,因此無論我們使用哪個實例,我們總是在訪問同一數組。
這就是為什么我們不使用旨在初始化實例的函數(例如,構造函數)來為構造函數的prototype
屬性初始化對象的原因。
我應該注意,在標准的原型繼承中,實際創建一個實例作為另一個實例的原型是正常的。 但是JavaScript的構造函數及其prototype
屬性不是標准的原型繼承,它們是基於類的OOP和原型OOP的混合體。 您可以使用JavaScript進行純原型OOP(很多可以這樣做),但是當您這樣做時,就不必使用構造函數, prototype
屬性或new
。 您通常使用工廠函數和Object.create
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.