【问题标题】:Javascript Inheritance calling parent's non default constructorJavascript继承调用父级的非默认构造函数
【发布时间】:2016-01-18 11:21:55
【问题描述】:

我知道原型继承的经典模式是基于设置构造函数的对象原型。 但是我愿意有可能从派生类构造函数中调用父构造函数,其中一些参数在调用之前不可用。

这在 Java/Python/PHP 中使用 super(...)Parent.__init__(...) 方法可以很好地完成。

但是在纯 Javascript(不是 coffescript 或类似的)中没有办法做到这一点(parent.apply(this,arguments) 可以,但它不会设置原型)。

经过一些阅读,我得到了这个解决方案,将“继承”方法添加到 Function 的原型中。 这只是将 super 的定义添加到派生函数中,以便根据一些参数初始化原型。

Function.prototype.inherits=function(parent)
{
    var ctor=this;
    var p=Object.create(parent);
    ctor.super=function()
    {
        parent.apply(p,arguments);
    }
    ctor.prototype=p;
}


//testing

function A(x)
{
    this.x=x;
}

function B(y)
{
    B.super(y*2);//Here "super" is available and I can pass parameters. 
    this.y=y;
}
B.inherits(A);//here I define the inheritance


a=new A(3);
b=new B(5);


console.log(a);//returns { x: 3 }
console.log(b);//returns { y: 5 }
console.log(b.x);//returns 10


console.log(a instanceof A);//returns true
console.log(b instanceof B);//returns true

通过这种方式,我得到了我所期望的行为。 我的问题是:这个解决方案有什么缺点?同一个问题有更有效的解决方案吗?此解决方案是否跨浏览器?

PS:我自己发明的:)

编辑:为了避免与其他库发生冲突,我可以定义一个像这样实现相同目标的独立函数。

function inherits(klass,parent)
{
    var p=Object.create(parent);
    klass.super=function()
    {
        parent.apply(p,arguments);
    }
    klass.prototype=p;
}

并且在定义B之后的测试中简单地调用

inherits(B,A);

编辑 2: 在 Moolamaduck 考虑之后,我重写了代码以解决共享原型的问题。结果非常简单易用且优雅(恕我直言)。
https://stackoverflow.com/a/33270707/76081

【问题讨论】:

  • 一个缺点是您正在修改内置对象,这通常被认为(有充分理由)是不好的做法。修改函数/数组/对象等的原型是一个糟糕的(或至少是脆弱的)想法。在效率方面,如果您的对象创建是您的瓶颈,那么您几乎可以肯定做错了。跨浏览器?除非你需要支持旧的 IE,否则没问题。
  • 效率以什么指标衡量?运行?代码复杂性/可维护性?
  • 另外,您对转译器有什么强烈反对吗? Coffeescript、babel 和 traceur 都可以处理 super 就好了。还是更多的是出于教育目的?
  • 是的,速度和内存的效率。
  • 它非常正确,除了不完整之外没有其他重要缺陷。例如,它处理子类化Array 的效果如何? Date 怎么样? RegExp?如果您只想继承您自己的用户定义的构造函数,那么我认为您会发现您的版本易于维护,并且具有上述警告的充足性能。

标签: javascript inheritance prototype super default-constructor


【解决方案1】:

这是一个实现您想要的最小示例(从派生类的构造函数中调用父构造函数):

var Shape = function(sides) {
  this.sides = sides;
};

var Square = function(size) {
  /* Just call the parent constructor function with `this` as context. */
  Shape.call(this, 4);
  this.size = size;
};

/* Set up the prototype chain. Use a shim for `Object.create` if you want. */
Square.prototype = Object.create(Shape.prototype);

这就是它的全部内容:调用父构造函数并将正在构造的对象作为上下文,并设置原型链。

您发布的代码存在一个严重缺陷。即,您使用派生类的 prototype 作为上下文调用父构造函数,而不是正在构造的对象。这意味着父构造函数初始化的成员将在派生类的所有实例的prototype上更新,因此所有实例都将被更新。这不是你想要的。

为了说明问题:

Function.prototype.inherits=function(parent)
{
    var ctor=this;
    var p=Object.create(parent);
    ctor.super=function()
    {
        parent.apply(p,arguments);
    }
    ctor.prototype=p;
}


function A(x)
{
    this.x=x;
}

function B(y)
{
    B.super(y*2);
    this.y=y;
}

B.inherits(A);

var b1 = new B(1);
var b2 = new B(2);
alert(b1.x); // displays "4" instead of "2"!

【讨论】:

【解决方案2】:

一个缺点可能是扩展内置对象(如 Function 或 Array)的原型可能会与页面上运行的第三方脚本发生冲突。如果两个脚本试图覆盖同一个原型,你会得到意想不到的结果。

在浏览器兼容性方面,似乎最薄弱的环节是Object.create,IE 8 及更早版本不支持该链接。见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Browser_compatibility

【讨论】:

  • Object.create 对于 polyfill 来说是微不足道的。
  • 这并不意味着它是。我指的是真空中的 OP 代码。
  • 谢谢。谁在乎 IE8 :) 关于第三方脚本的冲突,请参阅编辑
  • 我个人不知道,但考虑到 IE 8 比 IE 9 和 10 拥有更大的市场份额,它仍然值得考虑。来源:gs.statcounter.com/…
  • 你是对的......可能有一种方法可以修改它并使其与 IE8 一起工作......针对移动设备和最近的浏览器,我认为 IE8 对于现代 webapps 来说可能已经过时了.
【解决方案3】:

您似乎正在寻找一种通用模式,以便在 JavaScript 中重新创建经典的面向对象范例(包括使用 super)。

几年前,John Resig(jQuery 的创建者)提出了一组函数,可以让您在 Javascript 中模仿这一点。

http://ejohn.org/blog/simple-javascript-inheritance/

然而,由于几个原因(包括使用arguments.callee 属性),现在认为已弃用,但仍可作为一个良好的基础。

已经提出了许多替代 arguments.callee 属性的改进建议,包括:

Is John Resig's Javascript inheritance snippet deprecated?

https://codereview.stackexchange.com/questions/30018/improving-on-john-resigs-simple-javascript-inheritance-avoiding-new

如果您正在寻找一种在 JavaScript 中重新创建经典面向对象的可靠方法,我会进一步研究对 John 原始代码的改进(他知道他在说什么,这是一个很好的基础)。

【讨论】:

  • 是的。这正是我想做的。感谢您的参考。
  • @alexroat - 没问题,我自己也研究过类似的事情!
【解决方案4】:

经过 Moolamaduck 的考虑,我重写了代码以解决共享原型的问题。 结果非常简单易用且优雅(恕我直言)。

这是它,正在测试:

function inherits(ctor,parent)
{
    ctor.prototype=Object.create(parent.prototype);
    ctor._super=function()
    {
        parent.apply(this,arguments);
    }
}


//testing

function A(x)
{
    this.x=x;
};

function B(y)
{
    B._super.call(this,y*2);//Here "_super" is available (after calling inherits) and I can pass parameters to the parent constructor.
    this.y=y;
};
inherits(B,A);// Here we call inherits, after this parent will be available in the B constructor


a=new A(3);
b=new B(5);


console.log(a);//returns A {x: 3}
console.log(b);//returns B {x: 10, y: 5}
console.log(b.x);//returns 2*5=10

console.log(a instanceof A);//returns true
console.log(b instanceof B);//returns true

//post instantiation method definition and inheritance
A.prototype.test=function(){console.log(this.x+1);};
a.test();//returns 3+1=4
b.test();//returns 2*5+1=11



var b1 = new B(1);
var b2 = new B(2);


console.log(b1.x);//returns 2
console.log(b2.x);//returns 4

【讨论】:

  • 你也可以把ctor._super=function() { ... }写成ctor._super = parent;
猜你喜欢
  • 1970-01-01
  • 2012-07-26
  • 1970-01-01
  • 2016-03-24
  • 2016-04-07
  • 2011-05-20
  • 1970-01-01
  • 2017-10-04
  • 2015-07-09
相关资源
最近更新 更多