[英]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.prototype
和SubClass.prototype
现在引用相同的对象
JavaScript的语法可能有点令人困惑。 在JS中,没有类继承,而是基于实例的继承。 实例的父级也称为其原型。
当你编写instance.something
或instance['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继承。
但是,正如我之前所说,函数的默认.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);
输出:
这里, BaseClass
由SubClass
继承, BaseClass variable is accessable through the SubClass
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.