繁体   English   中英

javascript原型继承混淆

[英]javascript prototypical inheritance confused

给出这样实现继承的标准方法

function BaseClass() {

}

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

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

为什么需要这样做

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

并最终得到类似的东西

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

而不只是做

Subclass.prototype = BaseClass.prototype;

在JavaScript中为事物赋值实际上只是复制引用 (除非使用基本类型)。 所以当你这样做时:

Subclass.prototype = BaseClass.prototype;

真正在做的是将SubClass的原型分配到内存中与BaseClass原型相同的位置,因此你对SubClass所做的任何与原型相关的更改也会影响BaseClass 这是一个小例子:

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"

这就是你想要使用的原因

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

因为它将使用指定的原型创建一个新的 唯一对象。

您可以在此处详细了解此功能的工作原理。

对象参考!

Say BaseClass有一个toString方法:

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

但是你在SubClass重新定义了toString方法:

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

你会期望:

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

但是如果你使用赋值创建继承,你会得到的是:

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

因为BaseClass.prototypeSubClass.prototype现在引用相同的对象

JavaScript的语法可能有点令人困惑。 在JS中,没有类继承,而是基于实例的继承。 实例的父级也称为其原型。

当你编写instance.somethinginstance['something'] ,JS引擎会查看实例以查看它是否有一个名为something的成员。 如果没有,那么它会查看实例的原型。 如果该对象没有该成员的something ,它会一次又一次地查看原型的原型,直到它找到该属性或者到达一个继承自null的实例。 在这种情况下,它将简单地返回undefined

函数有一个叫做prototype的特殊属性,这是另一回事。 函数的.prototype是一个简单的对象,它有一个constructor属性,它返回函数本身。 使用new关键字创建对象时,该对象的原型将设置为函数的.prototype属性。 这就是混淆的地方:构造函数的.prototype属性可以看作是使用所述构造函数创建的所有实例的默认原型。

因此,当您通过编写以下内容向类添加方法时:

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

...实际上,您正在将函数存储在所有MyClass实例的原型中。 当JS引擎查看MyClass的实例时,它将查找它找不到的foo成员。 然后它将查看实例的原型,该原型碰巧被设置为MyClass.prototype ,它将找到foo成员并获取它。

重要的是在实例的原型和函数的.prototype之间做出区分,但是大多数人都没有意识到这一点。 当他们谈到一个类的原型时,他们谈论的是MyClass.prototype 在许多浏览器中,可以通过__proto__访问实例原型,但这不是JavaScript的标准功能,不应在代码中使用。

现在让我们看看你用来模拟类继承的代码。

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

Object.create(parent)可以看作是执行此操作的函数:

return {
  __proto__ : parent
};

换句话说,它创建一个空白Object,其原型是传递的对象。 由于所有SubClass实例都将继承自Subclass.prototype ,因此将SubClass.prototype替换为继承自BaseClass.prototype的对象可确保所有SubClass实例也从BaseClass继承。

示例MyClass.prototype控制台检查

但是,正如我之前所说,函数的默认.prototype属性是一个空对象,其.constructor设置为函数本身。 因此,通过再次手动设置.constructor ,我们完全模仿默认的原型行为。 如果我们不这样做,那么instance.constructor将在原型链中返回第一个定义的.constructor属性,它将是BaseClass 除非我们的代码实际上依赖于constructor属性,否则它在行为方面并没有真正改变任何东西,但它更安全。

最后一点,就像我之前提到的那样,我最终可以发布这个答案,你不能只做SubClass.prototype = BaseClass.prototype; 因为那样你就无法在不将它们添加到BaseClass的情况下向SubClass添加方法。

继承,__ proto__

当一个对象SubClass继承自另一个对象BaseClass ,在JavaScript中意味着有一个特殊属性SubClass.__proto__ = BaseClass

代码:

function BaseClass() {
}

function SubClass() {
}

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

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

SubClass.__proto__ = BaseClass;

console.log(SubClass);

输出:

这里, BaseClassSubClass继承, BaseClass variable is accessable through the SubClass

在此输入图像描述

暂无
暂无

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

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