使用纯JavaScript进行继承,这就是我通常做的事情:
function A() {}
A.prototype.run = function () {};
function B() {}
B.prototype = new A;
B.prototype.constructor = B;
由于没有参数传递给构造函数,因此新的A没有什么可抱怨的。现在,如果构造函数有要传递的参数,我还没有想出一个很好的继承方法。例如,
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {}
B.prototype = new A;
B.prototype.constructor = B;
我可以传递一些任意值,如:
B.prototype = new A(null, null);
在某些情况下,我可能需要在A的构造函数中验证x和y。在某些极端情况下,我在检查x或y时需要抛出错误。然后,B无法使用新的A继承A.
有什么建议吗?
谢谢!
答案 0 :(得分:22)
好吧,如果你想让B.prototype
一个继承自A.prototype
的对象,而不执行A
构造函数,以避免所有可能的副作用,你可以使用一个虚拟构造函数这样做,例如:
function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
您可以创建一个函数来封装创建这个新对象的逻辑,例如:
function inherit(o) {
function F() {}; // Dummy constructor
F.prototype = o;
return new F();
}
//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
如果您定位现代浏览器,您可以将ECMAScript 5 Object.create
方法用于相同目的,例如:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
答案 1 :(得分:6)
问题在于您无法轻松地为B
创建原型对象,因为无法调用A
的构造函数。这是由于在执行new B
之前构造函数的参数未知。您需要一个虚拟构造函数来构造B
的原型,该原型链接到A
的原型。
B.prototype = (function(parent){
function protoCreator(){};
protoCreator.prototype = parent.prototype;
// Construct an object linking to A.prototype without calling constructor of A
return new protoCreator();
})(A);
设置B
原型对象后,需要确保在A
的构造函数中调用B
的构造函数。
function B(x, y) {
// Replace arguments by an array with A's arguments in case A and B differ in parameters
A.apply(this, arguments);
}
您现在应该可以通过调用B
来实例化new B(x, y)
。
有关A
中包含参数验证的完整内容,请参阅a jsFiddle。
在原始代码中,您正在设置B.prototype.constructor = B
。我不明白你为什么要这样做。 constructor
属性不会影响prototype
属性负责的继承层次结构。如果你想在constructor
属性中包含命名构造函数,你需要从上面扩展代码:
// Create child's prototype – Without calling A
B.prototype = (function(parent, child){
function protoCreator(){
this.constructor = child.prototype.constructor
};
protoCreator.prototype = parent.prototype;
return new protoCreator();
})(A, B);
使用B.prototype
的第一个定义,您将获得以下结果:
var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
使用extended version,您将获得:
var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
不同输出的原因是instanceof
跟进b
的整个原型链,并尝试为A.prototype
或B.prototype
找到匹配的原型对象(在另一个电话)。 b.constructor
原型确实引用了用于定义实例原型的函数。如果你想知道为什么它没有指向protoCreator
这是因为它的原型在创建A.prototype
期间被B.prototype
覆盖了。 the updated example中显示的扩展定义修复了此constructor
属性,指向更合适(因为可能更期望)的函数。
对于日常使用,我建议完全放弃使用实例的constructor
属性的想法。相反,请使用instanceof
,因为其结果更可预测/预期。
答案 2 :(得分:5)
考虑一下:
function B( x, y ) {
var b = Object.create( new A( x, y ) );
// augment b with properties or methods if you want to
return b;
}
然后
var b = new B( 12, 13 );
现在b
继承自A
的实例,后者继承自A.prototype
。
现场演示: http://jsfiddle.net/BfFkU/
Object.create
,但可以轻松地手动实现它:
if ( !Object.create ) {
Object.create = function ( o ) {
function F() {}
F.prototype = o;
return new F();
};
}
这可以放在ie8.js
文件中,该文件仅通过条件注释加载到IE8及以下版本。
答案 3 :(得分:4)
虽然这是一个古老的话题,但我认为无论如何我都会回应。 有两种方法可以做到:
尽管 Pseudo Classical 方式最受欢迎,但由于它需要在子构造函数中调用父构造函数一次并且在继承原型时需要调用一次,因此它有其缺点。此外,子进程的原型将包含父构造函数的所有属性,无论如何在调用子构造函数时都会被覆盖。我个人的选择是 Prototypal Inheritance 。
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = new A();
B.prototype.constructor = B;
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;