Javascript 调用父类的非默认构造函数的继承
Javascript Inheritance calling parent's non default constructor
我知道class带原型的继承模式是基于设置构造函数的对象原型。
然而,我的意愿是有可能从派生的 class 构造函数调用父构造函数,其中包含一些在调用之前不可用的参数。
这在 Java/Python/PHP 中用 super(...)
或 Parent.__init__(...)
方法很好地完成了。
然而,在普通的 Javascript(不是 coffescript 或类似的)中,没有办法做到这一点(parent.apply(this,arguments)
可以,但不会设置原型)。
经过一些阅读我得到了这个解决方案,将 "inherits" 方法添加到 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 的考虑,我重写了代码以解决共享原型的问题。结果非常简单易用且优雅(恕我直言)。
一个缺点可能是扩展内置对象(如 Function 或 Array)的原型有可能与页面上 运行 的第三方脚本发生冲突。如果两个脚本试图覆盖同一个原型,您可能会得到意想不到的结果。
在浏览器兼容性方面,似乎最弱的 link 是 Object.create
,IE 8 及更早版本不支持。参见 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Browser_compatibility
您似乎正在寻找一种通用模式来在 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?
如果您正在寻找一种可靠的方法来在 JavaScript 中重新创建经典的面向对象,我会进一步研究改进 John 的原始代码(这个人知道他在说什么,这是一个很好的基础建立在)。
这是一个最小的例子,它实现了你想要的(从派生 class 的构造函数中调用父构造函数):
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);
这就是它的全部内容:以正在构造的对象作为上下文调用父构造函数,并设置原型链。
您发布的代码中存在一个严重缺陷。即,您使用派生 class 的 原型 作为上下文调用父构造函数,而不是正在构造的对象。这意味着父构造函数初始化的成员将在派生 class 的所有实例的 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"!
经过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
我知道class带原型的继承模式是基于设置构造函数的对象原型。 然而,我的意愿是有可能从派生的 class 构造函数调用父构造函数,其中包含一些在调用之前不可用的参数。
这在 Java/Python/PHP 中用 super(...)
或 Parent.__init__(...)
方法很好地完成了。
然而,在普通的 Javascript(不是 coffescript 或类似的)中,没有办法做到这一点(parent.apply(this,arguments)
可以,但不会设置原型)。
经过一些阅读我得到了这个解决方案,将 "inherits" 方法添加到 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 的考虑,我重写了代码以解决共享原型的问题。结果非常简单易用且优雅(恕我直言)。
一个缺点可能是扩展内置对象(如 Function 或 Array)的原型有可能与页面上 运行 的第三方脚本发生冲突。如果两个脚本试图覆盖同一个原型,您可能会得到意想不到的结果。
在浏览器兼容性方面,似乎最弱的 link 是 Object.create
,IE 8 及更早版本不支持。参见 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Browser_compatibility
您似乎正在寻找一种通用模式来在 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?
如果您正在寻找一种可靠的方法来在 JavaScript 中重新创建经典的面向对象,我会进一步研究改进 John 的原始代码(这个人知道他在说什么,这是一个很好的基础建立在)。
这是一个最小的例子,它实现了你想要的(从派生 class 的构造函数中调用父构造函数):
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);
这就是它的全部内容:以正在构造的对象作为上下文调用父构造函数,并设置原型链。
您发布的代码中存在一个严重缺陷。即,您使用派生 class 的 原型 作为上下文调用父构造函数,而不是正在构造的对象。这意味着父构造函数初始化的成员将在派生 class 的所有实例的 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"!
经过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