簡體   English   中英

subclass.prototype = new superclass()vs. subclass = new superclass()

[英]subclass.prototype = new superclass() vs. subclass = new superclass()

我一直在使用javascript實例化子類

object = new class ()

但我注意到有些人實例化使用

object.prototype = new class ()

問題:有什么區別? 對我來說,似乎后者更多地尊重繼承鏈,因為如果class ()有一堆“ this.variable = x ”語句,而對象是你想從它繼承而不是類的實例,那么你是准確地將這些變量分配給對象的原型而不是像前一種情況那樣分配給對象本身。 所以實際上它是這樣的?

object = new class () |vs.| subclass.prototype = new superclass ()

但是,在程序功能上兩者都是一樣的嗎?

旁邊的問題:我還有點不清楚new運營商實際上做了什么。 在我看來,只需創建一個空對象並分配它的proto屬性?

您問題中的代碼示例反映了一些誤解。 我們先來解決它們:

  • class是Javascript中的保留關鍵字。 您不能將class用作任何變量或函數的名稱。 這不是因為Javascript語言使用了該關鍵字,而是因為它計划在將來使用。
  • Javascript中沒有真正的類。 您可能已經看到過,通過使用Javascript中的可用繼承機制來模擬類繼承的不同嘗試,它基於原型對象,與鏈接實例共享屬性
  • 非常重要:此繼承機制中使用的prototype屬性是在函數上設置的, 而不是直接在對象上設置的

引用Douglas Crockford的第5章,繼承, JavaScript:好的部分

不是讓對象直接從其他對象繼承,而是插入不必要的間接級別,使得對象由構造函數生成。

(......)

使用新前綴使用構造函數調用模式調用函數時,這會修改函數的執行方式。

然后Douglas Crockford解釋了如何將新運算符實現為JavaScript函數。 這個函數使用了本書中定義的其他幾個函數,所以我在下面的(稍微)簡單的表格中重寫了它:

function createNew(constructor) {
  // a function to explain the new operator:
  //   var object = createNew(constructor);
  // is equivalent to
  //   var object = new constructor();
  //
  // param: constructor, a function
  // return: a new instance of the "constructor" kind of objects

  // step 1. create a new empty object instance
  //         linked to the prototype of provided constructor  
  var hiddenLink = function(){};
  hiddenLink.prototype = constructor.prototype;
  var instance = new hiddenLink(); // cheap trick here: using new to implement new

  // step 2. apply the constructor the new instance and get the result
  var result = constructor.apply(object); // make this a reference to instance within constructor

  // step 3. check the result, and choose whether to return it or the created instance
  if (typeof result === 'object') {
    return object;
  } else {
    return instance;
  } 
}

簡單來說,如果你調用new constructor() ,其中構造函數是一個函數,運算符創建一個新對象,其中包含從構造函數繼承屬性的鏈接,將構造函數應用於它,並返回構造函數返回的值,或者在構造函數返回不是對象的其他東西的情況下的新對象。

使用自定義構造函數創建新實例之前或之后的任何時候您都可以修改構造函數(函數)上的原型(對象)

function constructor(){}  // the most simple constructor function: does nothing
var before = new constructor();
var different = new constructor();
different.answer = "So long, and thanks for all the fish";

constructor.prototype = {};             // set an empty object to the prototype property
constructor.prototype.answer = 42;      // create a new property on prototype object
constructor.prototype.answer = Math.PI; // replace an existing property

var after = new constructor();

通過添加到使用此構造函數創建的所有對象的隱藏鏈接(請參閱createNew中的“cheap trick”),可以在所有這些實例上訪問原型對象的屬性,除非直接由對象上定義的屬性覆蓋。

before.answer === Math.PI; // true
after.answer === Math.PI;  // true
different.answer === "So long, and thanks for all the fish"; // true

使用這個新獲得的知識,您將如何創建一個新的“類”對象繼承數組的所有屬性,以及一個新方法empty()來刪除所有元素?

首先 ,Javascript中沒有類,所以為了創建一個新的“類”,我必須定義一個新的構造函數。 我們稱之為CustomArray,使用大寫C來遵循構造函數應該以大寫字母開頭的約定。

function CustomArray(){}

我現在可以創建自定義實例:

var myArray = new CustomArray(); 

其次 ,我希望使用CustomArray創建的實例繼承Array屬性:

myArray.prototype = new Array(); // WRONG EXAMPLE: we must set CustomArray.prototype
CustomArray.prototype = Array;   // WRONG EXAMPLE: prototype expects an object, Array is a function
CustomArray.prototype = new Array(); // OK, BUT: the simpler form [] should be used instead
CustomArray.prototype = [];

第三 ,我希望使用CustomArray創建的所有實例都具有empty()方法:

function empty(){
    // empty this array by setting its length to 0
    // function to be called in the context (this) of an array
    this.length = 0;
}
CustomArray.prototype.empty = empty; // set the function named empty to the property "empty" of the prototype

最后 ,我可以用更簡潔的方式重寫整個示例:

function CustomArray(){}
CustomArray.prototype = [];
CustomArray.prototype.empty = function(){ this.length = 0; }

我現在可以創建一個自定義數組,設置幾個值並清空它:

var myArray = new CustomArray();
myArray[0] = "abc";
myArray[1] = "def";
myArray[2] = "ghi";
myArray.empty();

問題是 :上面的代碼不能按預期工作。 為什么? 因為與常規數組不同,我們的自定義數組中的設置值不會自動增加數組的length屬性。 同樣,調用empty()只會將自定義數組的length屬性設置為0,它不會刪除其中的所有值。

此外,我們無法使用數組文字語法來初始化我們的自定義數組:

var myArray = ["abc","def","ghi"]; // this creates a regular array

總而言之,了解Javascript繼承如何工作非常重要,但是您可能經常發現它沒有預期的那么有用,並且有更簡單的方法可以實現相同的結果,例如使用構建器函數而不是構造函數。 我們可以通過使用構建器函數customizeArray來擴展常規數組來解決問題:

function customizeArray(array){
  array.empty = function(){
    this.length = 0;
  }; 
}
var myArray = ["abc","def","ghi"];
customizeArray(myArray);
myArray.empty();

此代碼按預期工作,因為在這種情況下myArray是一個常規數組,使用名為empty的新方法進行擴展。 與使用原型相比,這種方法的主要優點和缺點是它只修改了所選實例,如果你同時處理大量類似對象,設置這個額外屬性會比設置一個共享使用更多內存共同原型上的財產。

為避免這種情況,您可能想直接修改Array原型:

Array.prototype.empty = function(){
  this.length = 0;
};
var myArray = ["abc","def","ghi"];
myArray.empty();

它可以工作,但我建議反對它:你將自定義屬性附加到每個數組實例,包括那些花哨的庫,插件,框架,廣告和分析腳本,頁面上的所有代碼創建的所有數據。 毋庸置疑,它可能會在您無法修復的地方破壞某些東西。

編輯:關於kangax博客的有趣帖子作為后續內容: “ECMAScript 5如何仍然不允許子類化數組”

不同的是,當你這樣做時:

var subclass = new superclass();

您正在創建superclass的實例。 subclass只是變量。 您沒有創建子類(即,使subclass繼承superclass )。 在后一個例子中,假設子類是一個函數,你說的是子類的所有新實例都應該繼承(即。子類) superclass

所以:

function superclass() {this.stuff="stuff";}
function subclass() {}
subclass.prototype = new superclass();
alert(new subclass().this); // pops up "stuff"

是典型的繼承。

對於new運算符,它用於創建內置對象和用戶定義類型的實例。 用戶定義的類型只是一個函數。

編輯:當我在上面寫道,子類繼承了使用原型繼承的超類型時,我的意思是所有子類的新實例都從超類的一個特定實例繼承,而不是從superclass類型/函數本身繼承。

閱讀mozilla doc后,共享Javascript繼承的快速演示

function Employee (name, dept) {
    this.name = name || "";
    this.dept = dept || "";
}

function Programmer (name, projs) {
    Employee.call(this, name, "programming");
    this.projects = projs || [];
}
Programmer.prototype = new Employee;

// demo dynamic inheritance
Employee.prototype.leave = 10;

var johnny = new Programmer("Johnny", ["C#","Java"]);
alert("name: " + johnny.name + "\n"
      + "dept: " + johnny.dept + "\n"
      + "projects: " + johnny.projects + "\n"
      + "leave: " + johnny.leave);

var mary = new Programmer("Mary", ["Javascript","Java"]);
alert("name: " + mary.name + "\n"
      + "dept: " + mary.dept + "\n"
      + "projects: " + mary.projects + "\n"
      + "leave: " + mary.leave);

alert("changing leave of all staff to 8");
Employee.prototype.leave = 8;
alert("Johnny leave: " + johnny.leave); // 8
alert("Mary leave: " + mary.leave); // 8

alert("cannot batch move staff to another department");
Employee.prototype.dept = "sales";
alert("Johnny dept: " + johnny.dept); // programming
alert("Mary dept: " + mary.dept); // programming

在jsfiddle 演示中使用jsfiddle中的更多調試進行演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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