簡體   English   中英

對象的Javascript原型繼承和鏈接構造

[英]Javascript Prototype Inheritance and Chaining Constructs for an Object

在閱讀“使用對對象的鏈構造函數的調用”部分的https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call時

function Product(name, price) {
  this.name = name;
  this.price = price;

  if (price < 0)
    throw RangeError('Cannot create product "' + name + '" with a negative price');
  return this;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}
Food.prototype = Object.create(Product.prototype);

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}
Toy.prototype = Object.create(Product.prototype);

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

我已經讀過一個對象的原型實際上是一個指向構造函數的屬性存儲位置的對象本身。

function Food(name, price)它通過Product.call(this)繼承構造函數的Product屬性。 什么是Food.prototype = Object.create(Product.prototype); 在干嘛 是否在Food中添加了另一個原型(如果可能甚至有2個原型)? 還是將它與Food的原型值附加到Food的原型(它已經繼承了,所以對我來說沒有意義)?

問題的實質可以概括為“為什么要根本設置原型”? 換句話說,為什么這不足以調用基類構造函數而不設置原型?

訣竅是您可能無法設置子類函數的原型! 沒有它,您的瑣碎示例就可以很好地工作。 注釋掉該行,您會發現該代碼在有和沒有此行的情況下均有效:

Food.prototype = Object.create(Product.prototype);

這是因為所有對象屬性都是在構造函數中設置的,並且通過鏈接構造函數(稱為基類構造函數),可以在子類實例上設置所有屬性。

但是考慮一下:

function Base() {
   this.BaseClassMethod = function() {}
}

function Base() {
}

Base.prototype.BaseClassMethod = function() {}

如您所見,除了方法之外,沒有其他字段。 語義上,兩個代碼片段都定義了一個構造函數,該函數創建具有唯一BaseClassMethod方法的實例。 但是,在第一個代碼段中,為該類的每個已創建實例創建了一個新的額外函數,而在第二個代碼段中,方法定義是共享的。

這個非常簡單的示例表明,可以通過具有不同配置文件的不同方式來實現相同的語義-在上述示例中,至少是不同的內存占用空間。

兩者之間的另一個區別是,后者可以在將來的某個位置重新定義函數,新版本會影響以前創建的所有實例,而在第一個代碼段中,您可以做的就是在特定實例中更改定義,但不容易“在所有先前創建的實例中”。

這是否是您的目標是另外一個故事。 您可以在這兩種將方法分配給類的方式之間自由選擇。

問題的答案就在這里:如果不設置原型鏈,則僅從子類構造函數調用基類構造函數時, 將不會繼承基類原型(后一片段)中定義的方法。

換句話說,同時調用基類構造函數和設置原型鏈,可以使您的繼承獨立於基類中定義方法的方式。

讓我們讓代碼說明一切。 Object.create基本上是這樣做的:

Object.create = function (o) {
  //Object.create equals an anonymous function that accepts one parameter, 'o'.

    function F() {};
    //Create a new function called 'F' which is just an empty object.

    F.prototype = o;
    //the prototype of the 'F' function should point to the
    //parameter of the anonymous function.

    return new F();
    //create a new constructor function based off of the 'F' function.
  };

希望這可以幫助。 干杯

Food.prototype = Object.create(Product.prototype)就像其他語言的extends一樣。 Product.call(this)就像super 使用幫助程序並遵循以下約定,有助於查看這種關系:

Function.prototype.inherits = function(parent) {
  this.prototype = Object.create(parent.prototype); // inherit parent's prototype
  this.prototype.constructor = this; // point to the right constructor
};

// A "class"
var Product = (function(){
  // constructor
  function Product(name, price) {
    this.name = name;
    this.price = price;
  }
  return Product;
}());

var Food = (function(_super){
  Food.inherits(Product); // inherit Product's prototype methods
  function Food(name, price) {
    // call "super" to inherit instance properties
    _super.call(this, name, price);
    this.category = 'food';
  }
}(Product)); // the parent class AKA "super" 

它不是100%等效的,但是與其他語言相比,它應該使您大致了解JS如何繼承。 如您所見,它看起來非常相似。

在函數Food(name,price)中,它通過Product.call(this)繼承構造函數的Product屬性。

並不是的。 它將Product的構造函數應用於新的Food實例,並通過例如負價檢查執行其代碼。

“副產品”是構造函數在對象上創建特定於實例的屬性,是的。

什么是Food.prototype = Object.create(Product.prototype); 在干嘛 是否在Food中添加了另一個原型(如果可能甚至有2個原型)?

究竟。 它正在鏈接原型。 new Food創建的Food實例將繼承Food.prototype屬性(包括方法),該屬性(通過該語句)將從Product.prototype繼承屬性。

由於原型對象尚無任何方法,因此您目前無法看到這種行為。 添加一些(也許是“輸出”方法?)並檢查結果。

簡單的簡約繼承庫:(最小2kb) https://github.com/haroldiedema/joii

基本上,您可以執行以下(以及更多操作):

// First (bottom level)
var Person = new Class(function() {
    this.name = "Unknown Person";
});

// Employee, extend on Person & apply the Role property.
var Employee = new Class({ extends: Person }, function() {
    this.name = 'Unknown Employee';
    this.role = 'Employee';

    this.getValue = function() {
        return "Hello World";
    }
});

// 3rd level, extend on Employee. Modify existing properties.
var Manager = new Class({ extends: Employee }, function() {

    // Overwrite the value of 'role'.
    this.role = this.role + ': Manager';

    // Class constructor to apply the given 'name' value.
    this.__construct = function(name) {
        this.name = name;
    }

    // Parent inheritance & override
    this.getValue = function() {
        return this.parent.getValue().toUpperCase();
    }
});

// And to use the final result:
var myManager = new Manager("John Smith");
console.log( myManager.name ); // John Smith
console.log( myManager.role ); // Manager

console.log( myManager.getValue() ); // HELLO WORLD

暫無
暫無

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

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