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

Posted

技术标签:

【中文标题】JavaScript 继承:当构造函数有参数时【英文标题】:JavaScript inheritance: when constructor has arguments 【发布时间】:2011-11-23 21:52:00 【问题描述】:

使用纯javascript做继承,这是我平时做的:

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

function B() 
B.prototype = new A;
B.prototype.constructor = B;

由于没有要传递给构造函数的参数,因此 new 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 继承。

有什么建议吗?

谢谢!

【问题讨论】:

【参考方案1】:

虽然这是一个老话题,但我想我还是会回应的。 两种方法:

虽然伪经典方式是最流行的,但它也有它的缺点,因为它需要在子构造函数中调用一次父构造函数,并且在继承原型时调用一次。此外,孩子的原型将包含父构造函数的所有属性,当调用子构造函数时,这些属性无论如何都会被覆盖。我个人的选择是原型继承

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;

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;

【讨论】:

我是 JS 新手,能否请您解释一下如何使用它以及调用构造函数的顺序?我将它与构造函数一起使用? @Chugaister,我不确定我是否完全理解你的问题。在上面的两个示例中,函数 A 和 B 都是构造函数。 B 是从 A 继承的构造函数。您可以使用这些构造函数来创建实例,如下所示: var a = new A(); var b = new B(); 这非常有用。感谢简单优雅的代码sn-ps进行对比。【参考方案2】:

考虑一下:

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 没有在 IE8 中实现,但是可以很容易地手动实现它:

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

这可以放在 ie8.js 文件中,该文件仅通过条件 cmets 为 IE8 及以下版本加载。

【讨论】:

@Grace 是的,Object.create 是 ES5。在此处查看有关浏览器中实现级别的信息:kangax.github.com/es5-compat-table 您可以在developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…找到更多关于对象创建的信息【参考方案3】:

问题是您不能轻易地为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的原型对象,你需要确保在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 中包含参数验证的完整内容,请参阅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.prototypeB.prototype(在另一个调用中)找到匹配的原型对象。 b.constructor 原型确实是指用于定义实例原型的函数。如果您想知道为什么它不指向protoCreator,这是因为它的原型在创建B.prototype 期间被A.prototype 覆盖。 the updated example 中显示的扩展定义将 constructor 属性修复为指向更合适(因为可能更符合预期)的功能。

对于日常使用,我建议完全放弃使用实例的constructor 属性的想法。请改用instanceof,因为它的结果更容易预测/预期。

【讨论】:

感谢您的回答!我想我有同样的问题要问你。 .我想知道为什么不使用调解器就不能直接执行此操作: function A() ; A.prototype.run = function () ;函数 B() ; B.prototype = A.prototype; B.prototype.constructor = B; 如果您设置B.prototype = A.prototype,您将失去向B 实例添加方法或属性的能力,而A 实例中不存在它们。您的分配将导致AB 的实例共享同一个原型——原型是实例的属性和方法的来源。 我之所以使用B.prototype.constructor = B,是因为B.prototype = new A之后,B的构造函数实际上指向了A,这样我就无法扩展B的构造函数了. 实际上你的扩展例子也说明了这个问题。【参考方案4】:

好吧,如果你想让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;
//..

【讨论】:

我认为副作用是 OP 所希望的。 "在某些情况下,我可能需要在 A 的构造函数中验证 x 和 y。" @patrickdw 可能是我理解错了,但是当那些验证抛出错误时我认为他有问题 “在某些极端情况下,我需要在检查 x 或 y 时抛出错误。那么,没有办法让 B 使用 new A 从 A 继承。”,我认为他正在寻找一种方法来避免在分配 B.prototype 对象时执行 A 构造函数... 是的,我想多了,我认为你是对的。虽然看起来A 中的验证可能需要应用于B 构造的对象,但这只是一个附带问题。为了避免将new A 分配给B.prototype 时出现验证问题,这是有道理的。 @CMS 谢谢你的回答。我想知道为什么我不能这样做:function A() ; A.prototype.run = function () ;函数 B() ; B.prototype = A.prototype; B.prototype.constructor = B; @GraceShao 不客气,如果您这样做 A.prototypeB.prototype 将引用同一个对象,您可以添加到 B.prototype 的任何特定属性将在 A.prototype 和两个构造函数的实例都将继承它们。创建一个继承自A.prototype 的对象可以解决这个问题。此外,您可能希望在B 中运行A 构造函数,也许是为了共享相同的初始化逻辑,如果两个构造函数共享相同的参数,您可以在B 中执行A.apply(this, arguments);,否则,获取必要的参数A,你可以使用A.call(this, arg1, arg2);

以上是关于JavaScript 继承:当构造函数有参数时的主要内容,如果未能解决你的问题,请参考以下文章

学习JavaScript 中的构造函数创建对象

34JavaScript面向对象(内置构造函数&相关方法|属性|运算符&继承&面向对象)

《JavaScript 高级程序设计》学习总结六

继承,虚继承机制

Python 3:当子构造函数的参数多于父构造函数时,从继承的方法返回新的子类实例

关于JavaScript的原型继承与原型链