簡體   English   中英

Javascript原型構造函數說明

[英]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做兩件事:

  1. 它創建一個使用Employee.prototype作為原型的對象

  2. 它運行在代碼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.prototypeconstructor屬性應指向Consultant ,但如果不這樣做,它將指向Employee

它過去沒什么大不了的,因為盡管在規范中定義了constructor以默認情況下以這種方式設置,但在使用它的規范中沒有任何內容。 在ES2015(又稱為“ ES6”)中發生了變化:現在,有時會使用constructor屬性(例如,在promise中)。


這是說明Employee具有參數的情況的示例。 我還切換了PayEmployeepayEmployee ,以符合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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM