简体   繁体   English

对象的Javascript原型继承和链接构造

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

When reading https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call for the section "Using call to chain constructors 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);

I have read that a object's prototype is actually a object itself that points to the constructor's properties memory locations. 我已经读过一个对象的原型实际上是一个指向构造函数的属性存储位置的对象本身。

In function Food(name, price) it inherits the constructor's Product's properties with Product.call(this) . function Food(name, price)它通过Product.call(this)继承构造函数的Product属性。 What is Food.prototype = Object.create(Product.prototype); 什么是Food.prototype = Object.create(Product.prototype); doing? 在干嘛 Is it adding another prototype to Food(if that is even possible to have 2 prototypes)? 是否在Food中添加了另一个原型(如果可能甚至有2个原型)? Or is it appending to Food's prototype with the same prototype values of Product(which it already inherited so doesn't make sense to me ether)? 还是将它与Food的原型值附加到Food的原型(它已经继承了,所以对我来说没有意义)?

The essence of the problem cam be summarized as "why would you need to set prototypes at all"? 问题的实质可以概括为“为什么要根本设置原型”? In other words, why this is not sufficient to call the base class constructor and not to set the prototype? 换句话说,为什么这不足以调用基类构造函数而不设置原型?

The trick is that you could possibly not set the child class function's prototype! 诀窍是您可能无法设置子类函数的原型! Your trivial example would work fine without it. 没有它,您的琐碎示例就可以很好地工作。 Comment out the line and you will find that the code works with and without this line: 注释掉该行,您会发现该代码在有和没有此行的情况下均有效:

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

This is because all your object properties are set in constructors and by chaining constructors (calling the base class constructor) you have all the properties set on child class instances. 这是因为所有对象属性都是在构造函数中设置的,并且通过链接构造函数(称为基类构造函数),可以在子类实例上设置所有属性。

Consider however this: 但是考虑一下:

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

vs

function Base() {
}

Base.prototype.BaseClassMethod = function() {}

As you can see, there are no fields but a method. 如您所见,除了方法之外,没有其他字段。 Semantically, both snippets define a constructor function that creates instances having the sole BaseClassMethod method. 语义上,两个代码片段都定义了一个构造函数,该函数创建具有唯一BaseClassMethod方法的实例。 However, in the first snippet there is a new extra function created for each created instance of the class, while in the second snippet the method definition is shared. 但是,在第一个代码段中,为该类的每个已创建实例创建了一个新的额外函数,而在第二个代码段中,方法定义是共享的。

This very simple example shows that the same semantics can be achieved in different ways that have different profiles - in the above example is was at least the different memory footprint. 这个非常简单的示例表明,可以通过具有不同配置文件的不同方式来实现相同的语义-在上述示例中,至少是不同的内存占用空间。

Yet another difference between these two is that in the latter, the function can be redefined somewhere in the future and the new version affects all previously created instances while in the first snippet what you can do is to change the definition in specific instances but not easily "in all previously created instances". 两者之间的另一个区别是,后者可以在将来的某个位置重新定义函数,新版本会影响以前创建的所有实例,而在第一个代码段中,您可以做的就是在特定实例中更改定义,但不容易“在所有先前创建的实例中”。

Whether or not this is your goal is another story. 这是否是您的目标是另外一个故事。 You can freely choose between these two ways of assigning methods to classes. 您可以在这两种将方法分配给类的方式之间自由选择。

And there lies the answer to your question: without setting the prototype chain, methods defined in base class prototype (the latter snippet) would not be inherited if you only called the base class constructor from the child class constructor. 问题的答案就在这里:如果不设置原型链,则仅从子类构造函数调用基类构造函数时, 将不会继承基类原型(后一片段)中定义的方法。

In other words, calling both the base class constructor and setting the prototype chain makes your inheritance independent on the way methods are defined in the base class. 换句话说,同时调用基类构造函数和设置原型链,可以使您的继承独立于基类中定义方法的方式。

Let's let code speak for itself. 让我们让代码说明一切。 Object.create basically does this: 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.
  };

Hope this helps. 希望这可以帮助。 Cheers 干杯

Food.prototype = Object.create(Product.prototype) is like extends in other languages. Food.prototype = Object.create(Product.prototype)就像其他语言的extends一样。 Product.call(this) is like super . Product.call(this)就像super With a helper and following conventions it helps to see this relation: 使用帮助程序并遵循以下约定,有助于查看这种关系:

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" 

It is not 100% equivalent, but this should give you a general idea of how inheritance works in JS compared to other languages. 它不是100%等效的,但是与其他语言相比,它应该使您大致了解JS如何继承。 It can look pretty similar as you see. 如您所见,它看起来非常相似。

In function Food(name, price) it inherits the constructor's Product's properties with Product.call(this). 在函数Food(name,price)中,它通过Product.call(this)继承构造函数的Product属性。

Not really. 并不是的。 It applies the Product's constructor to the new Food instance, executing its code with eg the negative price check. 它将Product的构造函数应用于新的Food实例,并通过例如负价检查执行其代码。

A "byproduct" is that the constructor creates instance-specific properties on the object, yes. “副产品”是构造函数在对象上创建特定于实例的属性,是的。

What is Food.prototype = Object.create(Product.prototype); 什么是Food.prototype = Object.create(Product.prototype); doing? 在干嘛 Is it adding another prototype to Food(if that is even possible to have 2 prototypes)? 是否在Food中添加了另一个原型(如果可能甚至有2个原型)?

Exactly. 究竟。 It is chaining the prototypes. 它正在链接原型。 The instances of Food that are created by new Food will inherit properties (including methods) from Food.prototype , which will (by that statement) inherit properties from Product.prototype . new Food创建的Food实例将继承Food.prototype属性(包括方法),该属性(通过该语句)将从Product.prototype继承属性。

You can't see much of this behaviour currently as your prototype objects do not have any methods yet. 由于原型对象尚无任何方法,因此您目前无法看到这种行为。 Add some (maybe an "output" method?) and check the results. 添加一些(也许是“输出”方法?)并检查结果。

Simple minimalistic inheritance library: (2kb minified) https://github.com/haroldiedema/joii 简单的简约继承库:(最小2kb) https://github.com/haroldiedema/joii

It basically allows you to do the following (and more): 基本上,您可以执行以下(以及更多操作):

// 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