JavaScript继承[关闭]
Posted
技术标签:
【中文标题】JavaScript继承[关闭]【英文标题】:JavaScript Inheritance [closed] 【发布时间】:2011-11-21 03:46:55 【问题描述】:我正在尝试在 javascript 中实现继承。我想出了以下最少的代码来支持它。
function Base()
this.call = function(handler, args)
handler.call(this, args);
Base.extend = function(child, parent)
parent.apply(child);
child.base = new parent;
child.base.child = child;
专家,请让我知道这是否足够或我可能遗漏的任何其他重要问题。根据面临的类似问题,请提出其他更改建议。
这是完整的测试脚本:
function Base()
this.call = function(handler, args)
handler.call(this, args);
this.superalert = function()
alert('tst');
Base.extend = function(child, parent)
parent.apply(child);
child.base = new parent;
child.base.child = child;
function Child()
Base.extend(this, Base);
this.width = 20;
this.height = 15;
this.a = ['s',''];
this.alert = function()
alert(this.a.length);
alert(this.height);
function Child1()
Base.extend(this, Child);
this.depth = 'depth';
this.height = 'h';
this.alert = function()
alert(this.height); // display current object height
alert(this.a.length); // display parents array length
this.call(this.base.alert);
// explicit call to parent alert with current objects value
this.call(this.base.superalert);
// explicit call to grandparent, parent does not have method
this.base.alert(); // call parent without overriding values
var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);
【问题讨论】:
如果你需要继承,有很多很多库已经提供了这个。至少阅读它们以找出您的代码错误的地方。但为什么要重新发明?我想到的两个很棒的 javascript 继承库是 klass 和 selfish.js(我都用过,它们很棒。) 我使用过 Klass,但是在覆盖数组变量时存在一些问题。我会尝试自私的。但我的版本是简单的 4 行代码,但在大多数情况下都适用于我。我只是想知道我以后是否会被这种方法卡住。 您可能想查看this SO answer to a similar question;在所有伟大的技巧中,作者展示了如何在定义子类时删除对父构造函数的调用。 @hungryMind:如果您担心与您的代码有关的特定问题,为什么不编辑您的问题并准确告诉我们您害怕什么。因为您只是在询问您的代码是否正常,所以并没有给它太多的正义。你可能不会得到你正在寻找的答案。因此我建议你编辑你的 Q。 本题同题:***.com/questions/711209/… 【参考方案1】:要在ECMAScript 5 中实现javascript 继承,您可以定义对象的原型并使用Object.create
进行继承。您还可以根据需要添加/覆盖属性。
例子:
/**
* Transform base class
*/
function Transform()
this.type = "2d";
Transform.prototype.toString = function()
return "Transform";
/**
* Translation class.
*/
function Translation(x, y)
// Parent constructor
Transform.call(this);
// Public properties
this.x = x;
this.y = y;
// Inheritance
Translation.prototype = Object.create(Transform.prototype);
// Override
Translation.prototype.toString = function()
return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
/**
* Rotation class.
*/
function Rotation(angle)
// Parent constructor
Transform.call(this);
// Public properties
this.angle = angle;
// Inheritance
Rotation.prototype = Object.create(Transform.prototype);
// Override
Rotation.prototype.toString = function()
return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
【讨论】:
Translation.prototype = Object.create(new Transform()); ? Translation.prototype = Object.create(Transform.prototype); @4esn0k 是的,谢谢。 为什么不只是Translation.prototype = new Transform()
?另外,由于答案目前不起作用,您会编辑它吗?
@JörnZaefferer 看看这里:***.com/q/4166616/885464。 '答案目前不起作用'是什么意思?
您还应该明确设置子类的构造函数:Translation.prototype.constructor = Translation
。用于克隆对象(在大多数技术中)。【参考方案2】:
我认为 Crockfords 的解决方案过于复杂,John 的也是如此。获得 javascript 继承比他们似乎描述的要简单得多。考虑:
//Classes
function A()
B.call(this);
function B()
C.call(this);
this.bbb = function()
console.log("i was inherited from b!");
function C()
D.call(this);
function D()
E.call(this);
function E()
//instance property
this.id = Math.random()
//set up the inheritance chain (order matters)
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();
//Add custom functions to each
A.prototype.foo = function()
console.log("a");
;
B.prototype.bar = function()
console.log("b");
;
C.prototype.baz = function()
console.log("c");
;
D.prototype.wee = function()
console.log("d");
;
E.prototype.woo = function()
console.log("e");
;
//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);
var b = new B();
console.log(b.id)
我已经在my blog 上写了上述解决方案的完整描述。
【讨论】:
除了只支持所有公众会员 @rodrigo-silveira,不确定你的意思。如果你想要私有,你只需用 var x = "whatever" 声明它们,不是吗? 我认为@rodrigo-silveira 的意思是它不支持受保护的成员,这两种解决方案都不支持。 (根据定义,私有成员不能从子类访问,因此没有意义)。您必须使用this._myProtectedVariable = 5;
之类的东西来创建受保护的成员。
非常好的解决方案,只有(轻微的)缺点,构造函数执行了两次。一次 D.call(this),又一次:new D()。这通常不是什么大问题,但如果你确实想避免它,你可以像这样使用 Object.create:而不是 C.prototype = new D();你可以写 C.prototype = Object.create(D.prototype);例如:jsfiddle.net/9Dxkb/1
最后,一个有效的非混淆解释!我颠倒了你的逻辑,使 E 反向继承(E 最多),因为这对我来说很有意义。谢谢!【参考方案3】:
在玩 JS 对象时,我发现了一个更简约的解决方案 :-) 享受吧!
function extend(b,a,t,p) b.prototype = a; a.apply(t,p);
例子
function A()
this.info1 = function()
alert("A");
function B(p1,p2)
extend(B,A,this);
this.info2 = function()
alert("B"+p1+p2);
function C(p)
extend(C,B,this,["1","2"]);
this.info3 = function()
alert("C"+p);
var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc
【讨论】:
【参考方案4】:这里是最简单的,我希望是最简单的方式来理解 JS 中的继承。这个例子对 php 程序员最有帮助。
function Mother()
this.canSwim = function()
console.log('yes');
function Son();
Son.prototype = new Mother;
Son.prototype.canRun = function()
console.log('yes');
现在儿子有一个被覆盖的方法和一个新的
function Grandson()
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function()
console.log('yes');
;
Grandson.prototype.canSwim = function()
console.log('no');
现在孙子有两个被覆盖的方法和一个新的
var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no
【讨论】:
Why not to usenew
for inheritance
当然可以实现为 Object.create(new Son)
那会更糟。【参考方案5】:
为什么不使用对象而不是函数:
var Base =
superalert : function()
alert('tst');
;
var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function ()
alert(this.a.length);
alert(this.height);
var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function ()
alert(this.height);
alert(this.a.length);
this.childAlert();
this.superalert();
;
然后这样称呼它:
var child1 = Object.create(Child1);
child1.alert();
这种方法比函数更简洁。 我发现这个博客解释了为什么在 JS 中使用函数继承不是一种正确的方法:http://davidwalsh.name/javascript-objects-deconstruction
编辑
var Child也可以写成:
var Child = Object.create(Base,
width : value : 20,
height : value : 15, writable: true,
a : value : ['s', ''], writable: true,
childAlert : value : function ()
alert(this.a.length);
alert(this.height);
);
【讨论】:
【参考方案6】:这是我的解决方案,它基于Lorenzo Polidori'sanswer 中描述的标准原型继承方法。
首先,我从定义这些帮助方法开始,这使以后的事情更容易理解和可读:
Function.prototype.setSuperclass = function(target)
// Set a custom field for keeping track of the object's 'superclass'.
this._superclass = target;
// Set the internal [[Prototype]] of instances of this object to a new object
// which inherits from the superclass's prototype.
this.prototype = Object.create(this._superclass.prototype);
// Correct the constructor attribute of this class's prototype
this.prototype.constructor = this;
;
Function.prototype.getSuperclass = function(target)
// Easy way of finding out what a class inherits from
return this._superclass;
;
Function.prototype.callSuper = function(target, methodName, args)
// If methodName is ommitted, call the constructor.
if (arguments.length < 3)
return this.callSuperConstructor(arguments[0], arguments[1]);
// `args` is an empty array by default.
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
var method = superclass.prototype[methodName];
if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");
return method.apply(target, args);
;
Function.prototype.callSuperConstructor = function(target, args)
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
return superclass.apply(target, args);
;
现在,你不仅可以用SubClass.setSuperclass(ParentClass)
设置类的超类,还可以用SubClass.callSuper(this, 'functionName', [argument1, argument2...])
调用被覆盖的方法:
/**
* Transform base class
*/
function Transform()
this.type = "2d";
Transform.prototype.toString = function()
return "Transform";
/**
* Translation class.
*/
function Translation(x, y)
// Parent constructor
Translation.callSuper(this, arguments);
// Public properties
this.x = x;
this.y = y;
// Inheritance
Translation.setSuperclass(Transform);
// Override
Translation.prototype.toString = function()
return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
/**
* Rotation class.
*/
function Rotation(angle)
// Parent constructor
Rotation.callSuper(this, arguments);
// Public properties
this.angle = angle;
// Inheritance
Rotation.setSuperclass(Transform);
// Override
Rotation.prototype.toString = function()
return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
诚然,即使使用辅助函数,这里的语法也很尴尬。不过值得庆幸的是,在 ECMAScript 6 中添加了一些语法糖 (maximally minimal classes) 使事情变得更漂亮。例如:
/**
* Transform base class
*/
class Transform
constructor()
this.type = "2d";
toString()
return "Transform";
/**
* Translation class.
*/
class Translation extends Transform
constructor(x, y)
super(); // Parent constructor
// Public properties
this.x = x;
this.y = y;
toString()
return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
/**
* Rotation class.
*/
class Rotation extends Transform
constructor(angle)
// Parent constructor
super(...arguments);
// Public properties
this.angle = angle;
toString()
return super(...arguments) + this.type + " Rotation " + this.angle;
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
请注意,ECMAScript 6 目前仍处于草稿阶段,据我所知,尚未在任何主要的 Web 浏览器中实现。但是,如果您愿意,可以使用 Traceur compiler 之类的东西将 ECMAScript 6
编译成普通的基于 ECMAScript 5
的 JavaScript。可以看到上面使用 Traceur here 编译的例子。
【讨论】:
【参考方案7】:虽然我同意上述所有答案,但我认为 JavaScript 不需要面向对象(避免继承),而 an object-based approach 在大多数情况下就足够了。
我喜欢 Eloquent JavaScript 在面向对象编程上谈论 OO 时开始其 Chapter 8 的方式。与其解读实现继承的最佳方式,不如将更多的精力投入到学习 JavaScript 的函数方面,因此,我发现函数式编程的 Chapter 6 更有趣。
【讨论】:
【参考方案8】://This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:
this.somefunction = this.someFunction.override(function(args)
var result = this.inherited(args);
result += this.doSomethingElse();
return result;
);
//It is accomplished through this piece of code (courtesy of Poul Krogh):
/***************************************************************
function.override overrides a defined method with a new one,
while preserving the old method.
The old method is only accessible from the new one.
Use this.inherited() to access the old method.
***************************************************************/
Function.prototype.override = function(func)
var remember = this;
var f = function()
var save = this.inherited;
this.inherited = remember;
var result = func.apply(this, Array.prototype.slice.call(arguments));
this.inherited = save;
return result;
;
return f;
【讨论】:
【参考方案9】:这个简单的方法怎么样
function Body()
this.Eyes = 2;
this.Arms = 2;
this.Legs = 2;
this.Heart = 1;
this.Walk = function()alert(this.FirstName + ' Is Walking');
function BasePerson()
var BaseBody = new Body(this);
BaseBody.FirstName = '';
BaseBody.LastName = '';
BaseBody.Email = '';
BaseBody.IntroduceSelf = function () alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); ;
return BaseBody;
function Person(FirstName,LastName)
var PersonBuild = new BasePerson();
PersonBuild.FirstName = FirstName;
PersonBuild.LastName = LastName;
return PersonBuild;
var Person1 = new Person('Code', 'Master');
Person1.IntroduceSelf();
Person1.Walk();
【讨论】:
【参考方案10】:基本原型继承
在 JavaScript 中进行继承的一种简单但有效的方法是使用以下两行代码:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
这类似于这样做:
B.prototype = new A();
两者的主要区别在于A
的构造函数在使用Object.create
时没有运行,这样更直观,更类似于基于类的继承。
在创建B
的新实例时,您始终可以选择运行A
的构造函数,方法是将其添加到B
的构造函数中:
function B(arg1, arg2)
A(arg1, arg2); // This is optional
如果要将B
的所有参数传递给A
,也可以使用Function.prototype.apply()
:
function B()
A.apply(this, arguments); // This is optional
如果你想将另一个对象混入B
的构造函数链中,你可以将Object.create
与Object.assign
结合起来:
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
演示
function A(name)
this.name = name;
A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;
function B()
A.apply(this, arguments);
this.street = "Downing Street 10";
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function mixin()
mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;
mixin.prototype.getProperties = function()
return
name: this.name,
address: this.street,
year: this.year
;
;
function C()
B.apply(this, arguments);
this.year = "2018"
C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
创建自己的包装器
如果您不喜欢在整个代码中编写大致相同的两行代码,您可以编写一个基本的包装函数,如下所示:
function inheritance()
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length)
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++)
args[i] = args[i].prototype;
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
这个包装器是如何工作的:
-
如果你传递一个参数,它的原型将继承自
Object
。
如果您传递两个参数,第一个的原型将继承第二个的原型。
如果传递两个以上的参数,第一个的原型会继承第二个的原型,其他参数的原型会混入。
演示
function inheritance()
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length)
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++)
args[i] = args[i].prototype;
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
function A(name)
this.name = name;
inheritance(A);
function B()
A.apply(this, arguments);
this.street = "Downing Street 10";
inheritance(B, A);
function mixin()
inheritance(mixin);
mixin.prototype.getProperties = function()
return
name: this.name,
address: this.street,
year: this.year
;
;
function C()
B.apply(this, arguments);
this.year = "2018"
inheritance(C, B, mixin);
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
注意
Object.create
可以在所有现代浏览器中安全使用,包括 IE9+。 Object.assign
不适用于任何版本的 IE 或某些移动浏览器。如果您想使用它们并支持没有实现它们的浏览器,建议polyfill Object.create
和/或Object.assign
。
你可以找到 Object.create
here 的 polyfill
一个用于Object.assign
here。
【讨论】:
【参考方案11】://
// try this one:
//
// function ParentConstructor()
// function ChildConstructor()
//
// var
// SubClass = ChildConstructor.xtendz( ParentConstructor );
//
Function.prototype.xtendz = function ( SuperCtorFn )
return ( function( Super, _slice )
// 'freeze' host fn
var
baseFn = this,
SubClassCtorFn;
// define child ctor
SubClassCtorFn = function ( /* child_ctor_parameters..., parent_ctor_parameters[] */ )
// execute parent ctor fn on host object
// pass it last ( array ) argument as parameters
Super.apply( this, _slice.call( arguments, -1 )[0] );
// execute child ctor fn on host object
// pass remaining arguments as parameters
baseFn.apply( this, _slice.call( arguments, 0, -1 ) );
;
// establish proper prototype inheritance
// 'inherit' methods
SubClassCtorFn.prototype = new Super;
// (re)establish child ctor ( instead of Super ctor )
SubClassCtorFn.prototype.constructor = SubClassCtorFn;
// return built ctor
return SubClassCtorFn;
).call( this, SuperCtorFn, Array.prototype.slice );
;
// declare parent ctor
function Sup( x1, x2 )
this.parent_property_1 = x1;
this.parent_property_2 = x2;
// define some methods on parent
Sup.prototype.hello = function()
alert(' ~ h e l l o t h e r e ~ ');
;
// declare child ctor
function Sub( x1, x2 )
this.child_property_1 = x1;
this.child_property_2 = x2;
var
SubClass = Sub.xtendz(Sup), // get 'child class' ctor
obj;
// reserve last array argument for parent ctor
obj = new SubClass( 97, 98, [99, 100] );
obj.hello();
console.log( obj );
console.log('obj instanceof SubClass -> ', obj instanceof SubClass );
console.log('obj.constructor === SubClass -> ', obj.constructor === SubClass );
console.log('obj instanceof Sup -> ', obj instanceof Sup );
console.log('obj instanceof Object -> ', obj instanceof Object );
//
// Object parent_property_1: 99, parent_property_2: 100, child_property_1: 97, child_property_2: 98
// obj instanceof SubClass -> true
// obj.constructor === SubClass -> true
// obj instanceof Sup -> true
// obj instanceof Object -> true
//
【讨论】:
【参考方案12】:使用AWeb library 的最简单方法。官方样本:
/**
* A-class
*/
var ClassA = AWeb.class(
public :
/**
* A-class constructor
*/
constructor : function()
/* Private variable */
this.variable1 = "A";
this.calls = 0;
,
/**
* Function returns information about the object
*/
getInfo : function()
this.incCalls();
return "name=" + this.variable1 + ", calls=" + this.calls;
,
private :
/**
* Private function
*/
incCalls : function()
this.calls++;
);
/**
* B-class
*/
var ClassB = AWeb.class(
extends : ClassA,
public :
/**
* B-class constructor
*/
constructor : function()
this.super();
/* Private variable */
this.variable1 = "B";
,
/**
* Function returns extended information about the object
*/
getLongInfo : function()
return this.incCalls !== undefined ? "incCalls exists" : "incCalls undefined";
);
/**
* Main project function
*/
function main()
var a = new ClassA(),
b = new ClassB();
alert(
"a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
"a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo " + (b.getInfo ? "exists" : "undefined") + "\n" +
"b.getLongInfo " + (b.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo()=" + b.getInfo() + "\n" +
"b.getLongInfo()=" + b.getLongInfo()
);
【讨论】:
【参考方案13】:我找到了一个比扩展和原型设计更容易的解决方案。实际上,我不知道它的效率如何,尽管它看起来很干净而且很实用。
var A = function (p)
if (p == null) p = this;
p.a1 = 0;
this.a2 = 0;
var a3 = 0;
;
var B = function (p)
if (p == null) p = this;
p.b1 = new A(this);
this.b2 = new A(this);
var b3 = new A(this);
this b4 = new A();
;
var a = new A ();
var b = new B ();
结果:
a
a1 0
a2 0
b
a1 0
b1
a2 0
b2
a2 0
b4
a1 0
a2 0
实际例子:
var Point = function (p)
if (p == null) p = this;
var x = 0;
var y = 0;
p.getPoint = function () return [x,y]; ;
p.setPoint = function (_x,_y) x = _x; y = _y; ;
;
var Dimension = function (p)
if (p == null) p = this;
var w = 0;
var h = 0;
p.getDimension = function() return [w,h] ;
p.setDimension = function(_w,_h) w = _w; h = _h ;
;
var Rect = function (p)
if (p == null) p = this;
var dimension = new Dimension(this);
var location = new Point(this);
;
var rect = new Rect ();
rect.setDimension(w:30,h:40);
rect.setPoint(x:50,y:50);
【讨论】:
以上是关于JavaScript继承[关闭]的主要内容,如果未能解决你的问题,请参考以下文章