如何从JavaScript中的类inheritance?
在PHP / Java中,可以这样做:
class Sub extends Base { }
而且Super类的所有公共/受保护的方法,属性,字段等自动成为Sub类的一部分,如果需要的话可以被覆盖。
在Javascript中相当于什么?
我已经改变了我现在做这个,我尽量避免使用构造函数和它的prototype
属性,但我从2010年的旧答案仍然是在底部。 我现在更喜欢Object.create()
。 Object.create
在所有现代浏览器中都可用。
我应该注意到Object.create
通常要比使用new
构造函数慢得多。
//The prototype is just an object when you use `Object.create()` var Base = {}; //This is how you create an instance: var baseInstance = Object.create(Base); //If you want to inherit from "Base": var subInstance = Object.create(Object.create(Base)); //Detect if subInstance is an instance of Base: console.log(Base.isPrototypeOf(subInstance)); //True
的jsfiddle
使用Object.create的一个很大的好处是能够传入一个defineProperties参数,这个参数可以显着的控制如何访问和枚举类的属性,我也使用函数来创build实例,构造函数的方式,因为你可以在最后进行初始化,而不是仅仅返回实例。
var Base = {}; function createBase() { return Object.create(Base, { doSomething: { value: function () { console.log("Doing something"); }, }, }); } var Sub = createBase(); function createSub() { return Object.create(Sub, { doSomethingElse: { value: function () { console.log("Doing something else"); }, }, }); } var subInstance = createSub(); subInstance.doSomething(); //Logs "Doing something" subInstance.doSomethingElse(); //Logs "Doing something else" console.log(Base.isPrototypeOf(subInstance)); //Logs "true" console.log(Sub.isPrototypeOf(subInstance)); //Logs "true
的jsfiddle
这是我2010年的原始答案:
function Base ( ) { this.color = "blue"; } function Sub ( ) { } Sub.prototype = new Base( ); Sub.prototype.showColor = function ( ) { console.log( this.color ); } var instance = new Sub ( ); instance.showColor( ); //"blue"
在JavaScript中你没有类,但是你可以通过许多方式获得inheritance和行为的重用:
伪古典inheritance(通过原型):
function Super () { this.member1 = 'superMember1'; } Super.prototype.member2 = 'superMember2'; function Sub() { this.member3 = 'subMember3'; //... } Sub.prototype = new Super();
应与new
运营商一起使用:
var subInstance = new Sub();
函数应用程序或“构造器链接”:
function Super () { this.member1 = 'superMember1'; this.member2 = 'superMember2'; } function Sub() { Super.apply(this, arguments); this.member3 = 'subMember3'; }
这种方法也应该与new
运营商一起使用:
var subInstance = new Sub();
与第一个例子不同的是,当我们apply
Sub
构造函数应用于Sub
的this
对象时,它直接在新实例上添加了分配给this
对象的属性,例如subInstance
直接包含属性member1
和member2
( subInstance.hasOwnProperty('member1') == true;
)。
在第一个例子中,这些属性是通过原型链达到的,它们存在于一个内部[[Prototype]]
对象中。
寄生inheritance或权力构造者:
function createSuper() { var obj = { member1: 'superMember1', member2: 'superMember2' }; return obj; } function createSub() { var obj = createSuper(); obj.member3 = 'subMember3'; return obj; }
这种方法基本上是基于“对象扩充”,你不需要使用new
操作符,正如你所看到的, this
关键字不涉及。
var subInstance = createSub();
ECMAScript 5th Ed。 Object.create
方法:
// Check if native implementation available if (typeof Object.create !== 'function') { Object.create = function (o) { function F() {} // empty constructor F.prototype = o; // set base object as prototype return new F(); // return empty object with right [[Prototype]] }; } var superInstance = { member1: 'superMember1', member2: 'superMember2' }; var subInstance = Object.create(superInstance); subInstance.member3 = 'subMember3';
上述方法是Crockford提出的原型inheritance技术。
对象实例inheritance自其他对象实例,就是这样。
这个技术可以比简单的“对象扩充”更好,因为inheritance的属性不会复制到所有新的对象实例上,因为基础对象被设置为扩展对象的[[Prototype]]
,在上面的例子中subInstance
包含物理上只有member3
属性。
对于那些在2015年或之后到达此页面的用户
使用最新版本的ECMAScript标准(ES6) ,您可以使用de keywork extends
请注意,类定义不是一个常规的object
因此,类成员之间没有逗号。 要创build一个类的实例,您必须使用new
关键字。 要从基类inheritance,请使用extends
:
class Vehicle { constructor(name) { this.name = name; this.kind = 'vehicle'; } getName() { return this.name; } } // Create an instance var myVehicle = new Vehicle('rocky'); myVehicle.getName(); // => 'rocky'
要从基类inheritance,请使用extends
:
class Car extends Vehicle { constructor(name) { super(name); this.kind = 'car' } } var myCar = new Car('bumpy'); myCar.getName(); // => 'bumpy' myCar instanceof Car; // => true myCar instanceof Vehicle; // => true
从派生类中,可以使用任何构造函数或方法的super来访问其基类:
- 要调用父构造函数,请使用
super().
- 要调用另一个成员,请使用
super.getName()
。
还有更多的使用类。 如果您想更深入地研究这个问题,我推荐Dr. Axel Rauschmayer的“ ECMAScript 6中的类 ”。
资源
那么,在JavaScript中没有“类inheritance”,只有“原型inheritance”。 所以你不要上课“卡车”,然后将其标记为“汽车”的一个子类。 相反,你做一个对象“杰克”,并说它使用“约翰”作为原型。 如果约翰知道多less“4 + 4”,那么杰克也知道。
我build议你阅读道格拉斯·克罗克福德关于原型inheritance的文章: http : //javascript.crockford.com/prototypal.html他还展示了如何使JavaScript与其他OO语言具有“看起来相似”的inheritance,然后解释这个实际上是指以一种不打算使用的方式来打破javaScript。
我觉得这句话是最有启发性的:
实质上,一个JavaScript “类”只是一个Function对象,作为一个构造函数加上一个附加的原型对象。 ( 来源:Guru Katz )
我喜欢使用构造函数而不是对象,所以我偏向于CMS所描述的“伪古典inheritance”方法。 下面是一个原型链多重inheritance的例子:
// Lifeform "Class" (Constructor function, No prototype) function Lifeform () { this.isLifeform = true; } // Animal "Class" (Constructor function + prototype for inheritance) function Animal () { this.isAnimal = true; } Animal.prototype = new Lifeform(); // Mammal "Class" (Constructor function + prototype for inheritance) function Mammal () { this.isMammal = true; } Mammal.prototype = new Animal(); // Cat "Class" (Constructor function + prototype for inheritance) function Cat (species) { this.isCat = true; this.species = species } Cat.prototype = new Mammal(); // Make an instance object of the Cat "Class" var tiger = new Cat("tiger"); console.log(tiger); // The console outputs a Cat object with all the properties from all "classes" console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform); // Outputs: true true true true // You can see that all of these "is" properties are available in this object // We can check to see which properties are really part of the instance object console.log( "tiger hasOwnProperty: " ,tiger.hasOwnProperty("isLifeform") // false ,tiger.hasOwnProperty("isAnimal") // false ,tiger.hasOwnProperty("isMammal") // false ,tiger.hasOwnProperty("isCat") // true ); // New properties can be added to the prototypes of any // of the "classes" above and they will be usable by the instance Lifeform.prototype.A = 1; Animal.prototype.B = 2; Mammal.prototype.C = 3; Cat.prototype.D = 4; console.log(tiger.A, tiger.B, tiger.C, tiger.D); // Console outputs: 1 2 3 4 // Look at the instance object again console.log(tiger); // You'll see it now has the "D" property // The others are accessible but not visible (console issue?) // In the Chrome console you should be able to drill down the __proto__ chain // You can also look down the proto chain with Object.getPrototypeOf // (Equivalent to tiger.__proto__) console.log( Object.getPrototypeOf(tiger) ); // Mammal console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal // Etc. to get to Lifeform
这里是MDN的另一个很好的资源 ,这里是一个jsfiddle,所以你可以尝试一下 。
JavaScript的inheritance有点不同于Java和PHP,因为它并没有真正的类。 相反,它有提供方法和成员variables的原型对象。 你可以链接这些原型来提供对象inheritance。 在研究这个问题时,我发现的最常见的模式在Mozilla开发者networking上描述。 我已经更新了他们的示例,以包含对超类方法的调用并在警报消息中显示日志:
// Shape - superclass function Shape() { this.x = 0; this.y = 0; } // superclass method Shape.prototype.move = function(x, y) { this.x += x; this.y += y; log += 'Shape moved.\n'; }; // Rectangle - subclass function Rectangle() { Shape.call(this); // call super constructor. } // subclass extends superclass Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle; // Override method Rectangle.prototype.move = function(x, y) { Shape.prototype.move.call(this, x, y); // call superclass method log += 'Rectangle moved.\n'; } var log = ""; var rect = new Rectangle(); log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true rect.move(1, 1); // Outputs, 'Shape moved.' alert(log);
你不能(古典意义上的)。 Javascript是一个原型语言。 你会发现你永远不会在Javascript中声明一个“class” 你只需要定义一个对象的状态和方法。 为了产生inheritance,你需要一些对象并对它进行原型devise。 原型扩展了新的function。
您可以使用.inheritWith
和.fastClass
库 。 它比大多数stream行的库更快,有时甚至比本地版本更快。
非常容易使用:
function Super() { this.member1 = "superMember";//instance member }.define({ //define methods on Super's prototype method1: function() { console.log('super'); } //prototype member }.defineStatic({ //define static methods directly on Super function staticMethod1: function() { console.log('static method on Super'); } }); var Sub = Super.inheritWith(function(base, baseCtor) { return { constructor: function() {//the Sub constructor that will be returned to variable Sub this.member3 = 'subMember3'; //instance member on Sub baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments }, method1: function() { console.log('sub'); base.method1.apply(this, arguments); //call the base class' method1 function } }
用法
var s = new Sub(); s.method1(); //prints: //sub //super
读了很多post后,我想出了这个解决scheme( jsfiddle在这里 )。 大多数时候我不需要更复杂的东西
var Class = function(definition) { var base = definition.extend || null; var construct = definition.construct || definition.extend || function() {}; var newClass = function() { this._base_ = base; construct.apply(this, arguments); } if (definition.name) newClass._name_ = definition.name; if (definition.extend) { var f = function() {} f.prototype = definition.extend.prototype; newClass.prototype = new f(); newClass.prototype.constructor = newClass; newClass._extend_ = definition.extend; newClass._base_ = definition.extend.prototype; } if (definition.statics) for (var n in definition.statics) newClass[n] = definition.statics[n]; if (definition.members) for (var n in definition.members) newClass.prototype[n] = definition.members[n]; return newClass; } var Animal = Class({ construct: function() { }, members: { speak: function() { console.log("nuf said"); }, isA: function() { return "animal"; } } }); var Dog = Class({ extend: Animal, construct: function(name) { this._base_(); this.name = name; }, statics: { Home: "House", Food: "Meat", Speak: "Barks" }, members: { name: "", speak: function() { console.log( "ouaf !"); }, isA: function(advice) { return advice + " dog -> " + Dog._base_.isA.call(this); } } }); var Yorkshire = Class({ extend: Dog, construct: function(name,gender) { this._base_(name); this.gender = gender; }, members: { speak: function() { console.log( "ouin !"); }, isA: function(advice) { return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice); } } }); var Bulldog = function() { return _class_ = Class({ extend: Dog, construct: function(name) { this._base_(name); }, members: { speak: function() { console.log( "OUAF !"); }, isA: function(advice) { return "bulldog -> " + _class_._base_.isA.call(this,advice); } } })}(); var animal = new Animal("Maciste"); console.log(animal.isA()); animal.speak(); var dog = new Dog("Sultan"); console.log(dog.isA("good")); dog.speak(); var yorkshire = new Yorkshire("Golgoth","Male"); console.log(yorkshire.isA("bad")); yorkshire.speak(); var bulldog = new Bulldog("Mike"); console.log(bulldog.isA("nice")); bulldog.speak();
感谢CMS的回答,经过原型和Object.create的一段时间的摆弄之后,我能够使用apply来为我的inheritance提供一个整洁的解决scheme,如下所示:
var myNamespace = myNamespace || (function() { return { BaseClass: function(){ this.someBaseProperty = "someBaseProperty"; this.someProperty = "BaseClass"; this.someFunc = null; }, DerivedClass:function(someFunc){ myNamespace.BaseClass.apply(this, arguments); this.someFunc = someFunc; this.someProperty = "DerivedClass"; }, MoreDerivedClass:function(someFunc){ myNamespace.DerivedClass.apply(this, arguments); this.someFunc = someFunc; this.someProperty = "MoreDerivedClass"; } }; })();
您不能从JavaScript中inheritance类,因为JavaScript中没有类。
function Person(attr){ this.name = (attr && attr.name)? attr.name : undefined; this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined; this.printName = function(){ console.log(this.name); } this.printBirthYear = function(){ console.log(this.birthYear); } this.print = function(){ console.log(this.name + '(' +this.birthYear+ ')'); } } function PersonExt(attr){ Person.call(this, attr); this.print = function(){ console.log(this.name+ '-' + this.birthYear); } this.newPrint = function(){ console.log('New method'); } } PersonExt.prototype = new Person(); // Init object and call methods var p = new Person({name: 'Mr. A', birthYear: 2007}); // Parent method p.print() // Mr. A(2007) p.printName() // Mr. A var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007}); // Overwriten method pExt.print() // Mr. A-2007 // Extended method pExt.newPrint() // New method // Parent method pExt.printName() // Mr. A
从ES2015开始,这正是您在JavaScript中inheritance的方式
class Sub extends Base { }
function Base() { this.doSomething = function () { } } function Sub() { Base.call(this); } var sub = new Sub(); sub.doSomething();