JavaScript的inheritance:当构造函数有参数

使用纯JavaScript来做inheritance,这是我通常做的:

function A() {} A.prototype.run = function () {}; function B() {} B.prototype = new A; B.prototype.constructor = B; 

由于没有任何参数传入构造函数,新的A没有什么可抱怨的。 现在,如果构造函数有parameter passing,我还没有想出inheritance的好方法。 例如,

 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的构造函数中validationx和y。在某些极端情况下,检查x或y时需要抛出错误。 那么B就没有办法使用新的A.

有什么build议么?

谢谢!

那么,如果你想使B.prototype成为一个inheritance自B.prototype的对象而不执行A构造函数,为了避免所有可能的副作用,你可以使用一个虚构的构造函数来完成它,例如:

 function tmp() {} tmp.prototype = A.prototype; B.prototype = new tmp(); B.prototype.constructor = B; 

你可以创build一个函数来封装这个新对象的创build逻辑,例如:

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

问题是你不能轻易地为B创build一个原型对象,因为不能调用A的构造函数。 这是由于在执行new B之前构造函数的参数是未知的。 你需要一个虚拟构造函数来构造一个连接到A的原型的B的原型。

 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的原型对象,就需要确保在B的构造函数中调用A的构造函数。

 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); } 

你现在应该可以通过调用new B(x, y)来实例化B

对于包含A参数validation的完整相同请参见jsFiddle 。

在你原来的代码中,你正在设置B.prototype.constructor = B 我不明白你为什么这样做。 constructor属性不会影响prototype属性负责的inheritance层次结构。 如果你想在constructor属性中包含指定的构造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 

随着扩展版本 ,你会得到:

 var b = new B(4, 6); b.constructor // B console.info(b instanceof A); // true console.info(b instanceof B); // true 

导致不同输出的原因是, instanceof跟随b的整个原型链,并尝试为A.prototypeB.prototype (在另一个调用中)find匹配的原型对象。 b.constructor原型是指用于定义实例原型的函数。 如果你想知道为什么它不指向protoCreator这是因为在创buildA.prototype期间,它的原型被A.prototype所覆盖。 如更新示例中所示的扩展定义将此constructor属性修复为指向更合适(因为可能更多期望)的函数。

对于日常使用,我build议放弃完全使用实例的constructor属性的想法。 相反,使用instanceof因为它的结果更可预测/预期。

考虑一下:

 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 ); 

现在bA一个实例inheritance而来, A又inheritance自A.prototype

现场演示: http : //jsfiddle.net/BfFkU/


Object.create没有在IE8中实现,但可以很容易地手动实现它:

 if ( !Object.create ) { Object.create = function ( o ) { function F() {} F.prototype = o; return new F(); }; } 

这可以放在一个ie8.js文件中,该文件仅通过条件注释为IE8加载。

虽然这是一个古老的话题,但我还是认为我会回应。 两种方式来做到这一点:

虽然伪古典方式是最stream行的,但它有其不利的一面,因为它需要在子构造函数中调用父构造函数一次,而在inheritance原型的时候需要一次。 此外,孩子的原型将包含父构造函数的所有属性,当调用子构造函数时,它将被覆盖。 我个人的select是原型inheritance

1.伪古典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; 

2.原型inheritance:

 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;