Ich versuche, die Vererbung in Javascript zu implementieren. Ich habe mir den folgenden minimalen Code ausgedacht, um ihn zu unterstützen.
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;
}
Experten, lassen Sie mich bitte wissen, ob dies ausreicht oder ein anderes wichtiges Problem, das ich möglicherweise übersehen habe. Aufgrund ähnlicher Probleme schlagen Sie bitte andere Änderungen vor.
Hier ist das vollständige Testskript:
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
inheritance
hungrigMind
quelle
quelle
Antworten:
Um die Javascript-Vererbung in ECMAScript 5 zu implementieren , können Sie den Prototyp eines Objekts definieren und
Object.create
zum Erben verwenden. Sie können Eigenschaften auch beliebig hinzufügen / überschreiben.Beispiel:
/** * 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
quelle
Translation.prototype = new Transform()
? Würden Sie die Antwort bearbeiten, da sie derzeit nicht funktioniert?Translation.prototype.constructor = Translation
. Nützlich zum Klonen des Objekts (in den meisten Techniken).Ich denke, Crockfords Lösung ist zu kompliziert, genau wie Johns. Es ist viel einfacher, eine Javascript-Vererbung zu erhalten, als beide zu beschreiben scheinen. Erwägen:
//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)
Ich habe eine vollständige Beschreibung der oben genannten Lösung in meinem Blog geschrieben .
quelle
this._myProtectedVariable = 5;
, um geschützte Mitglieder zu erstellen.Während ich mit JS-Objekten spielte, fand ich eine minimalistischere Lösung :-) Viel Spaß!
function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }
Beispiel
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
quelle
Hier ist der einfachste und ich hoffe, der einfachste Weg, die Vererbung in JS zu verstehen. Am hilfreichsten ist dieses Beispiel für PHP-Programmierer.
function Mother(){ this.canSwim = function(){ console.log('yes'); } } function Son(){}; Son.prototype = new Mother; Son.prototype.canRun = function(){ console.log('yes'); }
Jetzt hat der Sohn eine überschriebene Methode und eine neue
function Grandson(){} Grandson.prototype = new Son; Grandson.prototype.canPlayPiano = function(){ console.log('yes'); }; Grandson.prototype.canSwim = function(){ console.log('no'); }
Jetzt hat der Enkel zwei überschriebene Methoden und eine neue
var g = new Grandson; g.canRun(); // => yes g.canPlayPiano(); // => yes g.canSwim(); // => no
quelle
new
für die Vererbung verwendenWarum nicht Objekte anstelle von Funktionen verwenden:
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(); };
Und nenne es so:
var child1 = Object.create(Child1); child1.alert();
Dieser Ansatz ist viel sauberer als bei Funktionen. Ich fand diesen Blog, in dem erklärt wurde, warum die Vererbung mit Funktionen in JS nicht richtig ist: http://davidwalsh.name/javascript-objects-deconstruction
BEARBEITEN
var Child kann auch geschrieben werden als:
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); }} });
quelle
Hier ist meine Lösung, die auf der in Lorenzo Polidoris Antwort beschriebenen standardmäßigen prototypischen Vererbungsmethode basiert .
Zunächst definiere ich diese Hilfsmethoden, die die Dinge später verständlicher und lesbarer machen:
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); };
Jetzt können Sie nicht nur die Oberklasse einer Klasse mit festlegen
SubClass.setSuperclass(ParentClass)
, sondern auch überschriebene Methoden aufrufen mitSubClass.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
Zugegeben, selbst mit den Hilfsfunktionen ist die Syntax hier ziemlich umständlich. Zum Glück wurde in ECMAScript 6 etwas syntaktischer Zucker ( maximal minimale Klassen ) hinzugefügt, um die Dinge viel schöner zu machen. Z.B:
/** * 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
Beachten Sie, dass sich ECMAScript 6 zu diesem Zeitpunkt noch im Entwurfsstadium befindet und meines Wissens in keinem größeren Webbrowser implementiert ist. Wenn Sie möchten, können Sie jedoch den Traceur-Compiler verwenden , um
ECMAScript 6
das einfacheECMAScript 5
JavaScript auf alter Basis zu kompilieren . Das obige Beispiel, das mit Traceur kompiliert wurde, finden Sie hier .quelle
Obwohl ich mit allen oben genannten Antworten einverstanden bin, bin ich der Meinung, dass JavaScript nicht objektorientiert sein muss (Vererbung vermeiden), sondern dass in den meisten Fällen ein objektbasierter Ansatz ausreichen sollte.
Ich mag die Art und Weise, wie Eloquent JavaScript sein Kapitel 8 über objektorientierte Programmierung beginnt und über OO spricht. Anstatt den besten Weg zur Implementierung der Vererbung zu entschlüsseln, sollte mehr Energie aufgewendet werden, um funktionale Aspekte von JavaScript zu lernen. Daher fand ich Kapitel 6 über funktionale Programmierung interessanter.
quelle
//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; }
quelle
Wie wäre es mit diesem einfachen Ansatz
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();
quelle
Grundlegende prototypische Vererbung
Eine einfache, aber effektive Methode zur Vererbung in JavaScript ist die Verwendung des folgenden Zweiliners:
B.prototype = Object.create(A.prototype); B.prototype.constructor = B;
Das ist ähnlich wie folgt:
B.prototype = new A();
Der Hauptunterschied zwischen beiden besteht darin, dass der Konstruktor von
A
bei Verwendung nicht ausgeführt wirdObject.create
, was intuitiver und der klassenbasierten Vererbung ähnlicher ist.Sie können jederzeit festlegen, dass der Konstruktor von optional ausgeführt werden soll,
A
wenn Sie eine neue Instanz von erstellenB
, indem Sie ihn dem Konstruktor hinzufügen vonB
:function B(arg1, arg2) { A(arg1, arg2); // This is optional }
Wenn Sie alle Argumente von
B
an übergeben möchtenA
, können Sie auch Folgendes verwendenFunction.prototype.apply()
:function B() { A.apply(this, arguments); // This is optional }
Wenn Sie ein anderes Objekt in den Konstruktor Kette mixin wollen
B
, können Sie kombinierenObject.create
mitObject.assign
:B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype); B.prototype.constructor = B;
Demo
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());
Erstellen Sie Ihren eigenen Wrapper
Wenn Sie nicht gerne ungefähr denselben Zweizeiler in Ihren Code schreiben, können Sie eine grundlegende Wrapper-Funktion wie die folgende schreiben:
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; } }
So funktioniert dieser Wrapper:
Object
.Demo
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());
Hinweis
Object.create
kann sicher in jedem modernen Browser verwendet werden, einschließlich IE9 +.Object.assign
funktioniert weder in einer IE-Version noch in einigen mobilen Browsern. Es wird empfohlen, PolyfillObject.create
und / oderObject.assign
wenn Sie sie verwenden und Browser unterstützen möchten, die sie nicht implementieren.Eine Polyfüllung finden Sie
Object.create
hier und eineObject.assign
hier .quelle
// // 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 //
quelle
Der einfachste Weg, die AWeb-Bibliothek zu verwenden . Offizielles Beispiel:
/** * 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() ); }
quelle
Ich fand eine Lösung viel einfacher als Dinge zu erweitern und Prototypen zu erstellen. Eigentlich weiß ich nicht, wie effizient das ist, obwohl es sauber und funktional aussieht.
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 ();
Ergebnis:
a a1 0 a2 0 b a1 0 b1 a2 0 b2 a2 0 b4 a1 0 a2 0
praktisches Beispiel:
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});
quelle