简体   繁体   English

javascript原型继承混淆

[英]javascript prototypical inheritance confused

given the standard way of achieving inheritance like this 给出这样实现继承的标准方法

function BaseClass() {

}

function SubClass() {
    BaseClass.call(this);
}

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

why is necesary to do 为什么需要这样做

SubClass.prototype = Object.create(BaseClass.prototype);

and end up with something like 并最终得到类似的东西

function F(){}
F.prototype = BaseClass.prototype;
SubClass.prototype = new F();

instead of just doing 而不只是做

Subclass.prototype = BaseClass.prototype;

Assigning values to things in JavaScript really just copies a reference (unless working with primitive types). 在JavaScript中为事物赋值实际上只是复制引用 (除非使用基本类型)。 So when you do this: 所以当你这样做时:

Subclass.prototype = BaseClass.prototype;

What you're really doing is assigning the prototype of SubClass to the same location in memory as the prototype of BaseClass , therefore any prototype related changes you make to SubClass will also affect BaseClass . 真正在做的是将SubClass的原型分配到内存中与BaseClass原型相同的位置,因此你对SubClass所做的任何与原型相关的更改也会影响BaseClass Here's a little example: 这是一个小例子:

function BaseClass() {

}

function SubClass() {
    BaseClass.call(this);
}

SubClass.prototype = BaseClass.prototype;
SubClass.prototype.constructor = SubClass;

SubClass.prototype.subClassFunction = function(){
    console.log("Added this to SubClass");
}

var baseObj = new BaseClass();
baseObj.subClassFunction(); // => "Added this to SubClass"

That's why you want to use 这就是你想要使用的原因

SubClass.prototype = Object.create(BaseClass.prototype);

because it will create a new and unique object with the specified prototype instead. 因为它将使用指定的原型创建一个新的 唯一对象。

You can read more about how this function works here . 您可以在此处详细了解此功能的工作原理。

Object Reference! 对象参考!

Say BaseClass has a method toString : Say BaseClass有一个toString方法:

BaseClass.prototype.toString = function() {
  return 'foo'
}

But you redefine the toString method in SubClass : 但是你在SubClass重新定义了toString方法:

SubClass.prototype.toString = function() {
  return 'bar'
}

You'll expect: 你会期望:

var b = new BaseClass(), s = new SubClass()
b.toString() //=> 'foo'
s.toString() //=> 'bar'

But if you use assignment to create inheritance what you will get is: 但是如果你使用赋值创建继承,你会得到的是:

var b = new BaseClass(), s = new SubClass()
b.toString() //=> 'bar'
s.toString() //=> 'bar'

Because BaseClass.prototype and SubClass.prototype now reference the same object 因为BaseClass.prototypeSubClass.prototype现在引用相同的对象

The syntax of JavaScript can be somewhat confusing. JavaScript的语法可能有点令人困惑。 In JS, there isn't class inheritance but instance-based inheritance. 在JS中,没有类继承,而是基于实例的继承。 An instance's parent is also called its prototype. 实例的父级也称为其原型。

When you write instance.something or instance['something'] , the JS engine looks at the instance to see if it has a member called something . 当你编写instance.somethinginstance['something'] ,JS引擎会查看实例以查看它是否有一个名为something的成员。 If it doesn't, then it looks at the instance's prototype. 如果没有,那么它会查看实例的原型。 If that object doesn't have the member something , it looks at the prototype's prototype, again and again until it finds the property or reaches an instance which inherits from null . 如果该对象没有该成员的something ,它会一次又一次地查看原型的原型,直到它找到该属性或者到达一个继承自null的实例。 In that case it will simply return undefined . 在这种情况下,它将简单地返回undefined

Functions have a special property called prototype which is something else. 函数有一个叫做prototype的特殊属性,这是另一回事。 The function's .prototype is a simple object that has a constructor property which refers back to the function itself. 函数的.prototype是一个简单的对象,它有一个constructor属性,它返回函数本身。 When you create an object with the new keyword, that object's prototype will be set to the function's .prototype property. 使用new关键字创建对象时,该对象的原型将设置为函数的.prototype属性。 This is where the confusion comes from: the .prototype property of a constructor can be seen as the default prototype of all instances made with said constructor. 这就是混淆的地方:构造函数的.prototype属性可以看作是使用所述构造函数创建的所有实例的默认原型。

So when you add methods to a class by writing something like this: 因此,当您通过编写以下内容向类添加方法时:

MyClass.prototype.foo = function() {
  alert('foo');
};

...you're actually storing the function in the prototype of all MyClass instances. ...实际上,您正在将函数存储在所有MyClass实例的原型中。 When the JS engine looks at the instances of MyClass, it will look for the foo member which it won't find. 当JS引擎查看MyClass的实例时,它将查找它找不到的foo成员。 Then it will look at the instance's prototype which happens to be set to MyClass.prototype and it will find the foo member and fetch it. 然后它将查看实例的原型,该原型碰巧被设置为MyClass.prototype ,它将找到foo成员并获取它。

It's important to make the difference between an instance's prototype and a function's .prototype , but most people don't realize it. 重要的是在实例的原型和函数的.prototype之间做出区分,但是大多数人都没有意识到这一点。 When they speak of a class's prototype, they're talking about MyClass.prototype . 当他们谈到一个类的原型时,他们谈论的是MyClass.prototype The instance prototype is accessible via __proto__ in many browsers, but that's not a standard feature of JavaScript and shouldn't be used in your code. 在许多浏览器中,可以通过__proto__访问实例原型,但这不是JavaScript的标准功能,不应在代码中使用。

Now let's look at the code you're using to simulate class inheritance. 现在让我们看看你用来模拟类继承的代码。

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

Object.create(parent) can be seen as a function that does this: Object.create(parent)可以看作是执行此操作的函数:

return {
  __proto__ : parent
};

In other words, it creates a blank Object whose prototype is the passed object. 换句话说,它创建一个空白Object,其原型是传递的对象。 Since all SubClass instances will inherit from Subclass.prototype , replacing SubClass.prototype with an object that inherits from BaseClass.prototype makes sure all SubClass instances also inherit from BaseClass. 由于所有SubClass实例都将继承自Subclass.prototype ,因此将SubClass.prototype替换为继承自BaseClass.prototype的对象可确保所有SubClass实例也从BaseClass继承。

示例MyClass.prototype控制台检查

However, as I said earlier, the default .prototype attribute of a function is an empty object whose .constructor is set to the function itself. 但是,正如我之前所说,函数的默认.prototype属性是一个空对象,其.constructor设置为函数本身。 So by manually setting the .constructor again, we're perfectly mimicking default prototype behavior. 因此,通过再次手动设置.constructor ,我们完全模仿默认的原型行为。 If we don't do that, then instance.constructor will return the first defined .constructor property down the prototype chain, which will be BaseClass . 如果我们不这样做,那么instance.constructor将在原型链中返回第一个定义的.constructor属性,它将是BaseClass It doesn't really change anything in terms of behavior unless our code actually depends on the constructor property, but it's safer to have it. 除非我们的代码实际上依赖于constructor属性,否则它在行为方面并没有真正改变任何东西,但它更安全。

As a final note, like others mentioned before I could finally post this answer, you can't just do SubClass.prototype = BaseClass.prototype; 最后一点,就像我之前提到的那样,我最终可以发布这个答案,你不能只做SubClass.prototype = BaseClass.prototype; because then you wouldn't be able to add methods to the SubClass without adding them to the BaseClass. 因为那样你就无法在不将它们添加到BaseClass的情况下向SubClass添加方法。

Inheritance, the __proto__ : 继承,__ proto__

When an object SubClass inherits from another object BaseClass , in JavaScript that means that there is a special property SubClass.__proto__ = BaseClass . 当一个对象SubClass继承自另一个对象BaseClass ,在JavaScript中意味着有一个特殊属性SubClass.__proto__ = BaseClass

Code : 代码:

function BaseClass() {
}

function SubClass() {
}

var BaseClass = new BaseClass();
var SubClass = new SubClass();

BaseClass.a = 5;
SubClass.b = 10;

SubClass.__proto__ = BaseClass;

console.log(SubClass);

Output : 输出:

Here, BaseClass is inherited by SubClass and BaseClass variable is accessable through the SubClass . 这里, BaseClassSubClass继承, BaseClass variable is accessable through the SubClass

在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM