oop - Hvordan lager jeg en abstrakt baseklasse i JavaScript?
Er det mulig å simulere abstrakt basisklasse i JavaScript? Hva er den mest elegante måten å gjøre det på?
Si, jeg vil gjøre noe som:
var cat = new Animal('cat');
var dog = new Animal('dog');
cat.say();
dog.say();
Det skal gi ut:
meow
bark
Svar #1
JavaScript-klasser og arv (ES6)
I henhold til ES6 kan du bruke JavaScript-klasser og arv for å oppnå det du trenger.
JavaScript-klasser, introdusert i ECMAScript 2015, er primært syntaktisk sukker over JavaScripts eksisterende prototypebaserte arv.
Referanse: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Først definerer vi vår abstrakte klasse. Denne klassen kan ikke instantieres, men kan utvides. Vi kan også definere funksjoner som må implementeres i alle klasser som utvider denne.
/**
* Abstract Class Animal.
*
* @class Animal
*/
class Animal {
constructor() {
if (this.constructor == Animal) {
throw new Error("Abstract classes can't be instantiated.");
}
}
say() {
throw new Error("Method 'say()' must be implemented.");
}
eat() {
console.log("eating");
}
}
Etter det kan vi lage våre konkrete klasser. Disse klassene vil arve alle funksjoner og oppførsel fra den abstrakte klassen.
/**
* Dog.
*
* @class Dog
* @extends {Animal}
*/
class Dog extends Animal {
say() {
console.log("bark");
}
}
/**
* Cat.
*
* @class Cat
* @extends {Animal}
*/
class Cat extends Animal {
say() {
console.log("meow");
}
}
/**
* Horse.
*
* @class Horse
* @extends {Animal}
*/
class Horse extends Animal {}
Og resultatene...
// RESULTS
new Dog().eat(); // eating
new Cat().eat(); // eating
new Horse().eat(); // eating
new Dog().say(); // bark
new Cat().say(); // meow
new Horse().say(); // Error: Method say() must be implemented.
new Animal(); // Error: Abstract classes can't be instantiated.
Svar #2
En enkel måte å lage en abstrakt klasse på er dette:
/**
@constructor
@abstract
*/
var Animal = function() {
if (this.constructor === Animal) {
throw new Error("Can't instantiate abstract class!");
}
// Animal initialization...
};
/**
@abstract
*/
Animal.prototype.say = function() {
throw new Error("Abstract method!");
}
Animal-"klassen" og say-metoden er abstrakte.
Å opprette en instans vil kaste en feil:
new Animal(); // throws
Slik «arver» du fra det:
var Cat = function() {
Animal.apply(this, arguments);
// Cat initialization...
};
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.say = function() {
console.log('meow');
}
Hund ser akkurat slik ut.
Og slik utspiller scenariet ditt seg:
var cat = new Cat();
var dog = new Dog();
cat.say();
dog.say();
Fjernspill her (se på konsollutdataene).
Svar #3
Mener du noe sånt som dette:
function Animal() {
//Initialization for all Animals
}
//Function and properties shared by all instances of Animal
Animal.prototype.init=function(name){
this.name=name;
}
Animal.prototype.say=function(){
alert(this.name + " who is a " + this.type + " says " + this.whattosay);
}
Animal.prototype.type="unknown";
function Cat(name) {
this.init(name);
//Make a cat somewhat unique
var s="";
for (var i=Math.ceil(Math.random()*7); i>=0; --i) s+="e";
this.whattosay="Me" + s +"ow";
}
//Function and properties shared by all instances of Cat
Cat.prototype=new Animal();
Cat.prototype.type="cat";
Cat.prototype.whattosay="meow";
function Dog() {
//Call init with same arguments as Dog was called with
this.init.apply(this,arguments);
}
Dog.prototype=new Animal();
Dog.prototype.type="Dog";
Dog.prototype.whattosay="bark";
//Override say.
Dog.prototype.say = function() {
this.openMouth();
//Call the original with the exact same arguments
Animal.prototype.say.apply(this,arguments);
//or with other arguments
//Animal.prototype.say.call(this,"some","other","arguments");
this.closeMouth();
}
Dog.prototype.openMouth=function() {
//Code
}
Dog.prototype.closeMouth=function() {
//Code
}
var dog = new Dog("Fido");
var cat1 = new Cat("Dash");
var cat2 = new Cat("Dot");
dog.say(); // Fido the Dog says bark
cat1.say(); //Dash the Cat says M[e]+ow
cat2.say(); //Dot the Cat says M[e]+ow
alert(cat instanceof Cat) // True
alert(cat instanceof Dog) // False
alert(cat instanceof Animal) // True
Svar #4
Du vil kanskje sjekke ut Dean Edwards' Base Class: http://dean.edwards.name/weblog/2006/03/base/
Alternativt finnes dette eksemplet/artikkelen av Douglas Crockford om klassisk arv i JavaScript: http://www.crockford.com/javascript/inheritance.html
Svar #5
Er det mulig å simulere abstrakt baseklasse i JavaScript?
Selvfølgelig. Det finnes omtrent tusen måter å implementere klasse-/instanssystemer i JavaScript på. Her er en:
// Classes magic. Define a new class with var C= Object.subclass(isabstract),
// add class members to C.prototype,
// provide optional C.prototype._init() method to initialise from constructor args,
// call base class methods using Base.prototype.call(this, ...).
//
Function.prototype.subclass= function(isabstract) {
if (isabstract) {
var c= new Function(
'if (arguments[0]!==Function.prototype.subclass.FLAG) throw(\'Abstract class may not be constructed\'); '
);
} else {
var c= new Function(
'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
'if (arguments[0]!==Function.prototype.subclass.FLAG && this._init) this._init.apply(this, arguments); '
);
}
if (this!==Object)
c.prototype= new this(Function.prototype.subclass.FLAG);
return c;
}
Function.prototype.subclass.FLAG= new Object();
var cat=new Animal('katt');
Det er selvfølgelig ikke egentlig en abstrakt baseklasse. Mener du noe sånt som:
var Animal= Object.subclass(true); // is abstract
Animal.prototype.say= function() {
window.alert(this._noise);
};
// concrete classes
var Cat= Animal.subclass();
Cat.prototype._noise= 'meow';
var Dog= Animal.subclass();
Dog.prototype._noise= 'bark';
// usage
var mycat= new Cat();
mycat.say(); // meow!
var mygiraffe= new Animal(); // error!
Svar #6
Animal = function () { throw "abstract class!" }
Animal.prototype.name = "This animal";
Animal.prototype.sound = "...";
Animal.prototype.say = function() {
console.log( this.name + " says: " + this.sound );
}
Cat = function () {
this.name = "Cat";
this.sound = "meow";
}
Dog = function() {
this.name = "Dog";
this.sound = "woof";
}
Cat.prototype = Object.create(Animal.prototype);
Dog.prototype = Object.create(Animal.prototype);
new Cat().say(); //Cat says: meow
new Dog().say(); //Dog says: woof
new Animal().say(); //Uncaught abstract class!
