ใน PHP / Java สามารถทำได้:
class Sub extends Base
{
}
และโดยอัตโนมัติเมธอดสาธารณะ / ที่มีการป้องกันคุณสมบัติฟิลด์และอื่น ๆ ของคลาส Super จะกลายเป็นส่วนหนึ่งของคลาสย่อยซึ่งสามารถแทนที่ได้หากจำเป็น
อะไรที่เทียบเท่ากับสิ่งนั้นใน Javascript?
ใน PHP / Java สามารถทำได้:
class Sub extends Base
{
}
และโดยอัตโนมัติเมธอดสาธารณะ / ที่มีการป้องกันคุณสมบัติฟิลด์และอื่น ๆ ของคลาส Super จะกลายเป็นส่วนหนึ่งของคลาสย่อยซึ่งสามารถแทนที่ได้หากจำเป็น
อะไรที่เทียบเท่ากับสิ่งนั้นใน 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
ประโยชน์ที่สำคัญอย่างหนึ่งของการใช้ Object.create คือความสามารถในการส่งผ่านอาร์กิวเมนต์definePropertiesซึ่งช่วยให้คุณสามารถควบคุมคุณสมบัติในคลาสได้อย่างมีนัยสำคัญว่าจะเข้าถึงและแจกแจงคุณสมบัติในชั้นเรียนได้อย่างไรและฉันยังใช้ฟังก์ชันเพื่อสร้างอินสแตนซ์ซึ่งทำหน้าที่เป็น ตัวสร้างในลักษณะที่คุณสามารถเริ่มต้นในตอนท้ายแทนที่จะส่งคืนอินสแตนซ์
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
นี่คือคำตอบเดิมของฉันจากปี 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 คุณไม่มีคลาสแต่คุณสามารถรับการสืบทอดและการนำพฤติกรรมกลับมาใช้ใหม่ได้หลายวิธี:
การสืบทอดแบบหลอกคลาสสิก (ผ่านการสร้างต้นแบบ):
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
ตัวSuper
สร้างให้กับthis
วัตถุภายในSub
มันจะเพิ่มคุณสมบัติที่กำหนดให้this
บนSuper
อินสแตนซ์ใหม่โดยตรงเช่นsubInstance
มีคุณสมบัติmember1
และmember2
โดยตรง (subInstance.hasOwnProperty('member1') == true;
)
ในตัวอย่างแรกคุณสมบัติเหล่านั้นเข้าถึงได้ผ่านห่วงโซ่ต้นแบบซึ่งมีอยู่ภายใน[[Prototype]]
วัตถุ
การถ่ายทอดทางพันธุกรรมหรือตัวสร้างพลัง:
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 Crockford
อินสแตนซ์ออบเจ็กต์สืบทอดมาจากอินสแตนซ์ออบเจ็กต์อื่นนั่นแหล่ะ
เทคนิคนี้จะดีกว่าง่าย "เสริมวัตถุ" เพราะคุณสมบัติสืบทอดไม่ได้คัดลอกมาทุกกรณีวัตถุใหม่ตั้งแต่ฐานวัตถุถูกตั้งค่าเป็น[[Prototype]]
ของขยายวัตถุในตัวอย่างข้างต้นsubInstance
มีร่างกายเพียงmember3
สถานที่ให้บริการ
Object.create()
หรือclone()
ฟังก์ชันที่กำหนดเอง(เช่น mercurial.intuxication.org/hg/js-hacks/raw-file/tip/clone.js ) เพื่อสืบทอดโดยตรงจากวัตถุต้นแบบ ดูความคิดเห็นเพื่อ stackoverflow.com/questions/1404559/…สำหรับคำอธิบาย
Object.create
วิธีการ :)
member1
ตัวแปรเดียวกันซึ่งไม่เป็นที่ต้องการเลย แน่นอนว่าพวกเขาสามารถเขียนซ้ำได้ แต่นั่นก็ไม่สมเหตุสมผล github.com/dotnetwise/Javascript-FastClassเป็นสารละลายน้ำตาลที่ดีกว่า
Sub.prototype = new Super();
. จะเกิดอะไรขึ้นถ้าทั้งสองคลาสจะไม่ถูกใช้ในระหว่างการเรียกใช้สคริปต์ ดูเหมือนปัญหาด้านประสิทธิภาพ เหตุใดฉันจึงต้องสร้างคลาสหลักหากไม่ได้ใช้คลาสย่อยจริง ช่วยอธิบายให้ละเอียดหน่อยได้ไหม? นี่คือการสาธิตง่ายๆของปัญหา: jsfiddle.net/slavafomin/ZeVL2ขอบคุณ!
ด้วยเวอร์ชันล่าสุดของมาตรฐาน ECMAScript (ES6)คุณสามารถใช้คำclass
สำคัญ
โปรดทราบว่าการกำหนดชั้นไม่ได้เป็นปกติobject
; ด้วยเหตุนี้จึงไม่มีเครื่องหมายจุลภาคระหว่างสมาชิกชั้นเรียน ในการสร้างอินสแตนซ์ของคลาสคุณต้องใช้new
คีย์เวิร์ด ในการสืบทอดจากคลาสพื้นฐานให้ใช้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'
ในการสืบทอดจากคลาสพื้นฐานให้ใช้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()
ที่จะเรียกสมาชิกอีกคนหนึ่งใช้ตัวอย่างเช่นการใช้ชั้นเรียนยังมีอีกมากมาย หากคุณต้องการเจาะลึกเรื่องนี้ขอแนะนำ“ Classes in ECMAScript 6 ” โดย Dr. Axel Rauschmayer *
class
และextends
เป็นน้ำตาลไวยากรณ์ (มีประโยชน์มาก) สำหรับห่วงโซ่ต้นแบบ: stackoverflow.com/a/23877420/895245
ใน JavaScript ไม่มี "การสืบทอดคลาส" มีเพียง "การสืบทอดต้นแบบ" ดังนั้นคุณอย่าสร้างคลาส "รถบรรทุก" แล้วทำเครื่องหมายเป็นคลาสย่อยของ "รถยนต์" ให้คุณสร้างวัตถุ "แจ็ค" และบอกว่ามันใช้ "จอห์น" เป็นต้นแบบ ถ้าจอห์นรู้ว่า "4 + 4" นั้นเท่าไหร่แจ็คก็รู้เช่นกัน
ฉันขอแนะนำให้คุณอ่านบทความของ Douglas Crockford เกี่ยวกับการสืบทอดต้นแบบที่นี่: http://javascript.crockford.com/prototypal.htmlนอกจากนี้เขายังแสดงให้เห็นว่าคุณสามารถทำให้ JavaScript มีการสืบทอด "เหมือนกัน" เหมือนในภาษา OO อื่น ๆ ได้อย่างไรจากนั้นจึงอธิบายว่าสิ่งนี้ จริงๆแล้วหมายถึงการทำลาย javaScript ด้วยวิธีที่ไม่ได้ตั้งใจจะใช้
ฉันพบว่าคำพูดนี้ให้ความกระจ่างมากที่สุด:
โดยพื้นฐานแล้ว"คลาส" ของ JavaScript เป็นเพียงวัตถุฟังก์ชันที่ทำหน้าที่เป็นตัวสร้างบวกกับวัตถุต้นแบบที่แนบมา ( ที่มา: คุรุแคทซ์ )
ฉันต้องการใช้ก่อสร้างมากกว่าวัตถุดังนั้นฉันบางส่วนกับ "มรดกหลอกคลาสสิก" วิธีการอธิบายไว้ที่นี่โดย CMS นี่คือตัวอย่างของการสืบทอดหลายรายการกับโซ่ต้นแบบ :
// 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 แตกต่างจาก Java และ PHP เล็กน้อยเนื่องจากไม่มีคลาสจริงๆ แต่มีวัตถุต้นแบบที่ให้วิธีการและตัวแปรสมาชิก คุณสามารถโยงต้นแบบเหล่านั้นเพื่อจัดเตรียมการสืบทอดวัตถุ รูปแบบที่พบมากที่สุดที่ผมพบเมื่อค้นคว้าคำถามนี้ได้อธิบายไว้ในMozilla พัฒนาเครือข่าย ฉันได้อัปเดตตัวอย่างเพื่อรวมการโทรไปยังเมธอด superclass และเพื่อแสดงบันทึกในข้อความแจ้งเตือน:
// 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 เป็นภาษาต้นแบบ คุณจะสังเกตได้ว่าคุณไม่เคยประกาศ "คลาส" ใน Javascript; คุณเป็นเพียงกำหนดสถานะและวิธีการของวัตถุ ในการสร้างมรดกคุณต้องใช้วัตถุบางอย่างและสร้างต้นแบบ ต้นแบบถูกขยายด้วยฟังก์ชันใหม่
คุณสามารถใช้.inheritWith
และ.fastClass
ไลบรารีห้องสมุดเร็วกว่าไลบรารียอดนิยมส่วนใหญ่และบางครั้งก็เร็วกว่าเวอร์ชันเนทีฟด้วยซ้ำ
ใช้งานง่ายมาก:
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
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
หลังจากอ่านหลาย ๆ โพสต์ฉันได้หาวิธีแก้ปัญหานี้ ( 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 ดังที่แสดงไว้ที่นี่:
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";
}
};
})();
จาก ES2015 นั่นคือวิธีการสืบทอดใน JavaScript
class Sub extends Base {
}
function Base() {
this.doSomething = function () {
}
}
function Sub() {
Base.call(this); // inherit Base's method(s) to this instance of Sub
}
var sub = new Sub();
sub.doSomething();
Javascript ไม่มีคลาส คลาสในจาวาสคริปต์เป็นเพียงการสร้างน้ำตาลสังเคราะห์ที่ด้านบนของรูปแบบการสืบทอดต้นแบบที่จาวาสคริปต์ คุณสามารถใช้ JSclass
เพื่อบังคับใช้การสืบทอดต้นแบบได้ แต่สิ่งสำคัญคือต้องตระหนักว่าคุณยังคงใช้ฟังก์ชันตัวสร้างภายใต้ประทุน
แนวคิดเหล่านี้ยังใช้เมื่อคุณขยายจากes6
'คลาส' โดยใช้คีย์เวิร์ดขยาย นี่เป็นการสร้างลิงก์เพิ่มเติมในห่วงโซ่ต้นแบบ __proto__
class Animal {
makeSound () {
console.log('animalSound');
}
}
class Dog extends Animal {
makeSound () {
console.log('Woof');
}
}
console.log(typeof Dog) // classes in JS are just constructor functions under the hood
const dog = new Dog();
console.log(dog.__proto__ === Dog.prototype);
// First link in the prototype chain is Dog.prototype
console.log(dog.__proto__.__proto__ === Animal.prototype);
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal
Object.create()
ยังเป็นวิธีสร้างการสืบทอดใน JS ในจาวาสคริปต์ Object.create()
เป็นฟังก์ชันที่สร้างวัตถุใหม่โดยใช้วัตถุที่มีอยู่เป็นอาร์กิวเมนต์ มันจะกำหนดวัตถุที่ได้รับเป็นอาร์กิวเมนต์ให้กับ__proto__
คุณสมบัติของวัตถุที่สร้างขึ้นใหม่ สิ่งสำคัญอีกครั้งที่ต้องตระหนักว่าเราผูกพันกับกระบวนทัศน์การสืบทอดต้นแบบที่ JS คาดเดา
const Dog = {
fluffy: true,
bark: () => {
console.log('woof im a relatively cute dog or something else??');
}
};
const dog = Object.create(Dog);
dog.bark();
คุณไม่สามารถสืบทอดจากคลาสใน JavaScript เนื่องจากไม่มีคลาสใน JavaScript