JavaScript继承:当构造函数有参数时

Gra*_*hao 46 javascript inheritance prototype-programming

使用纯JavaScript进行继承,这是我通常做的事情:

function A() {}
A.prototype.run = function () {};

function B() {}
B.prototype = new A;
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)

由于没有参数传递给构造函数,新的A没有什么可抱怨的.现在,如果构造函数有要传递的参数,我还没有想出一个很好的继承方法.例如,

function A(x, y) {}
A.prototype.run = function () {};

function B(x, y) {}
B.prototype = new A;
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)

我可以传递一些任意值,如:

B.prototype = new A(null, null);
Run Code Online (Sandbox Code Playgroud)

在某些情况下,我可能需要在A的构造函数中验证x和y.在某些极端情况下,我在检查x或y时需要抛出错误.然后,B无法使用新的A从A继承.

有什么建议?

谢谢!

CMS*_*CMS 23

好吧,如果你想创建B.prototype一个继承自的对象A.prototype,而不执行A构造函数,以避免所有可能的副作用,你可以使用虚拟构造函数来做到这一点,例如:

function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)

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

function inherit(o) {
  function F() {}; // Dummy constructor
  F.prototype = o; 
  return new F(); 
}

//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)

如果您定位现代浏览器,则可以将ECMAScript 5 Object.create方法用于相同目的,例如:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
Run Code Online (Sandbox Code Playgroud)

  • @GraceShao你是受欢迎的,如果你这样做`A.prototype`和`B.prototype`将引用相同的对象,你可以添加到`B.prototype`的任何特定属性将在`A.prototype`和两个构造函数的实例都将继承它们.创建一个继承自`A.prototype`的对象可以解决问题.此外,您可能希望在`B`中运行`A`构造函数,也许共享相同的初始化逻辑,如果两个构造函数共享相同的参数,您可以执行`A.apply(this,arguments);``B.`否则,得到`A`的必要参数,你可以使用`A.call(this,arg1,arg2);` (3认同)

Aug*_*ing 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);
Run Code Online (Sandbox Code Playgroud)

一旦你有了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);
}
Run Code Online (Sandbox Code Playgroud)

您现在应该能够B通过调用实例化new B(x, y).

有关包含参数验证的完整相同内容,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);
Run Code Online (Sandbox Code Playgroud)

使用您的第一个定义B.prototype会得到以下结果:

var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
Run Code Online (Sandbox Code Playgroud)

使用扩展版本,您将获得:

var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
Run Code Online (Sandbox Code Playgroud)

不同输出的原因是instanceof跟随整个原型链b并试图找到匹配的原型对象A.prototypeB.prototype(在另一个调用中).该b.constructor原型确实是指用于定义实例原型的功能.如果你想知道为什么它没有指出protoCreator这是因为它的原型A.prototype在创建过程中被覆盖了B.prototype.更新后的示例中显示的扩展定义将此constructor属性修复为指向更合适(因为可能更期望)的函数.

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


Šim*_*das 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;
}
Run Code Online (Sandbox Code Playgroud)

然后

var b = new B( 12, 13 );
Run Code Online (Sandbox Code Playgroud)

现在b继承自一个实例A,而实例继承自A.prototype.

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


Object.create 在IE8中没有实现,但可以轻松地手动实现它:

if ( !Object.create ) {
    Object.create = function ( o ) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
Run Code Online (Sandbox Code Playgroud)

这可以放在一个ie8.js文件中,该文件仅通过条件注释加载到IE8及以下版本.


小智 5

尽管这是一个老话题,但我还是想回应。有两种方法:

尽管Pseudo Classical方法是最流行的方法,但它有缺点,因为它需要在子构造函数中调用一次父构造函数,而在继承原型时调用一次。此外,子级原型将包含父级构造函数的所有属性,无论如何,当调用子级构造函数时,这些属性都会被覆盖。我个人的选择是“ 原型继承”

1.伪古典继承:

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;
Run Code Online (Sandbox Code Playgroud)

2.原型继承:

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;
Run Code Online (Sandbox Code Playgroud)