Comment créer une classe de base abstraite en JavaScript?
est-il possible de simuler une classe de base abstraite en JavaScript? Quelle est la façon la plus élégante de le faire?
Dire, je veux faire quelque chose comme: -
var cat = new Animal('cat');
var dog = new Animal('dog');
cat.say();
dog.say();
il devrait sortie: 'bark',' meow '
16 réponses
une façon simple de créer une classe abstraite est celle-ci:
/**
@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!");
}
la Animal
"classe" et la méthode say
sont abstraites.
créer une instance provoquerait une erreur:
new Animal(); // throws
C'est ainsi que vous "héritez" d'elle:
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');
}
Dog
ressemble à ça.
et voici comment votre scénario se déroule:
var cat = new Cat();
var dog = new Dog();
cat.say();
dog.say();
Violon ici (regardez la sortie de la console).
voulez-vous dire quelque chose comme ceci:
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
vous pouvez vérifier la classe de base de Dean Edwards: http://dean.edwards.name/weblog/2006/03/base/
alternativement, il y a cet exemple / article de Douglas Crockford sur l'héritage classique en JavaScript: http://www.crockford.com/javascript/inheritance.html
est-il possible de simuler une classe de base abstraite en JavaScript?
certainement. Il y a environ un millier de façons d'implémenter des systèmes de classes/instances dans JavaScript. En voici un:
// 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 chat = new Animal('chat');
ce n'est pas vraiment une classe de base abstraite bien sûr. Vous voulez dire quelque chose comme:
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!
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!
classes JavaScript et héritage (ES6)
selon ES6, vous pouvez utiliser les classes JavaScript et l'héritage pour accomplir ce que vous avez besoin.
les classes JavaScript, introduites dans ECMAScript 2015,sont principalement du sucre syntaxique sur le prototype existant de JavaScript basé héritage.
référence: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
tout d'Abord, nous définissons notre classe abstraite. Cette classe ne peut pas être instanciée, mais peut être étendue. Nous pouvons également définir les fonctions qui doivent être implémentées dans toutes les classes qui étendent celle-ci.
/**
* 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");
}
}
Après cela, nous pouvons créer nos Classes concrètes. Ces classes hériteront toutes les fonctions et les comportements de la classe abstraite.
/**
* 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 {}
et les résultats...
// 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.
vous pouvez créer des classes abstraites en utilisant des prototypes d'objets, un exemple simple peut être le suivant:
var SampleInterface = {
addItem : function(item){}
}
Vous pouvez changer la méthode ci-dessus ou pas, c'est à vous lorsque vous la mettre en œuvre. Pour une observation détaillée, vous pouvez visiter ici .
Question est assez ancienne, mais j'ai créé quelque solution possible comment créer abstract "classe" et de bloquer la création d'objet ce type.
//our Abstract class
var Animal=function(){
this.name="Animal";
this.fullname=this.name;
//check if we have abstract paramater in prototype
if (Object.getPrototypeOf(this).hasOwnProperty("abstract")){
throw new Error("Can't instantiate abstract class!");
}
};
//very important - Animal prototype has property abstract
Animal.prototype.abstract=true;
Animal.prototype.hello=function(){
console.log("Hello from "+this.name);
};
Animal.prototype.fullHello=function(){
console.log("Hello from "+this.fullname);
};
//first inheritans
var Cat=function(){
Animal.call(this);//run constructor of animal
this.name="Cat";
this.fullname=this.fullname+" - "+this.name;
};
Cat.prototype=Object.create(Animal.prototype);
//second inheritans
var Tiger=function(){
Cat.call(this);//run constructor of animal
this.name="Tiger";
this.fullname=this.fullname+" - "+this.name;
};
Tiger.prototype=Object.create(Cat.prototype);
//cat can be used
console.log("WE CREATE CAT:");
var cat=new Cat();
cat.hello();
cat.fullHello();
//tiger can be used
console.log("WE CREATE TIGER:");
var tiger=new Tiger();
tiger.hello();
tiger.fullHello();
console.log("WE CREATE ANIMAL ( IT IS ABSTRACT ):");
//animal is abstract, cannot be used - see error in console
var animal=new Animal();
animal=animal.fullHello();
comme vous pouvez le voir dernier objet nous donner erreur, c'est parce que L'Animal dans prototype a la propriété abstract
. Pour être sûr Qu'il est Animal pas quelque chose qui a Animal.prototype
dans la chaîne prototype je fais:
Object.getPrototypeOf(this).hasOwnProperty("abstract")
donc je vérifie que mon prototype le plus proche objet possède la propriété abstract
, seul l'objet créé directement à partir du prototype Animal
aura cette condition sur true. La fonction hasOwnProperty
ne vérifie que les propriétés de l'objet courant pas ses prototypes, ce qui nous donne 100% de certitude que la propriété est déclarée ici pas dans la chaîne de prototypes.
tout objet descendant d'un objet hérite de la méthode hasOwnProperty . Cette méthode peut être utilisée pour déterminer si un objet a la propriété spécifiée comme propriété directe de cet objet; contrairement à l'opérateur in, cette méthode ne vérifie pas la chaîne prototype de l'objet. En savoir plus:
dans ma proposition nous n'avons pas à changer constructor
à chaque fois après Object.create
comme il est dans la meilleure réponse actuelle par @Jordão.
permet également de créer de nombreuses classes abstraites dans la hiérarchie, il suffit de créer la propriété abstract
dans prototype.
function Animal(type) {
if (type == "cat") {
this.__proto__ = Cat.prototype;
} else if (type == "dog") {
this.__proto__ = Dog.prototype;
} else if (type == "fish") {
this.__proto__ = Fish.prototype;
}
}
Animal.prototype.say = function() {
alert("This animal can't speak!");
}
function Cat() {
// init cat
}
Cat.prototype = new Animal();
Cat.prototype.say = function() {
alert("Meow!");
}
function Dog() {
// init dog
}
Dog.prototype = new Animal();
Dog.prototype.say = function() {
alert("Bark!");
}
function Fish() {
// init fish
}
Fish.prototype = new Animal();
var newAnimal = new Animal("dog");
newAnimal.say();
ce n'est pas garanti de fonctionner car __proto__
n'est pas une variable standard, mais il fonctionne au moins en Firefox et Safari.
si vous ne comprenez pas comment cela fonctionne, lisez à propos de la chaîne prototype.
Javascript peut avoir l'héritage, consultez L'URL ci-dessous:
http://www.webreference.com/js/column79 /
Andrew
une autre chose que vous pourriez vouloir appliquer est de vous assurer que votre classe abstraite n'est pas instanciée. Vous pouvez le faire en définissant une fonction qui agit comme un drapeau défini comme le constructeur de classe abstrait. Cela va ensuite essayer de construire le drapeau qui appellera son constructeur contenant l'exception à lancer. Exemple ci-dessous:
(function(){
var FLAG_ABSTRACT = function(__class){
throw "Error: Trying to instantiate an abstract class:"+__class
}
var Class = function (){
Class.prototype.constructor = new FLAG_ABSTRACT("Class");
}
//will throw exception
var foo = new Class();
}) ()
nous pouvons utiliser le dessin Factory
dans ce cas. Javascript utiliser prototype
pour hériter les membres du parent.
définit le constructeur de classe parent.
var Animal = function() {
this.type = 'animal';
return this;
}
Animal.prototype.tired = function() {
console.log('sleeping: zzzZZZ ~');
}
et puis créer la classe des enfants.
// These are the child classes
Animal.cat = function() {
this.type = 'cat';
this.says = function() {
console.log('says: meow');
}
}
définit alors le constructeur de classe enfants.
// Define the child class constructor -- Factory Design Pattern.
Animal.born = function(type) {
// Inherit all members and methods from parent class,
// and also keep its own members.
Animal[type].prototype = new Animal();
// Square bracket notation can deal with variable object.
creature = new Animal[type]();
return creature;
}
tester.
var timmy = Animal.born('cat');
console.log(timmy.type) // cat
timmy.says(); // meow
timmy.tired(); // zzzZZZ~
voici le CodePen link pour le codage d'exemple complet.
//Your Abstract class Animal
function Animal(type) {
this.say = type.say;
}
function catClass() {
this.say = function () {
console.log("I am a cat!")
}
}
function dogClass() {
this.say = function () {
console.log("I am a dog!")
}
}
var cat = new Animal(new catClass());
var dog = new Animal(new dogClass());
cat.say(); //I am a cat!
dog.say(); //I am a dog!
je pense que toutes ces réponses spécialement deux premières (par certains et jordão ) répondent clairement à la question avec le prototype conventionnel base js concept.
Maintenant, comme vous voulez que le constructeur de la classe animal se comporte selon le paramètre passé à la construction, je pense que c'est très similaire au comportement de base de Creational Patterns
par exemple modèle D'usine .
Ici, j'ai fait une petite approche pour que ça marche comme ça.
var Animal = function(type) {
this.type=type;
if(type=='dog')
{
return new Dog();
}
else if(type=="cat")
{
return new Cat();
}
};
Animal.prototype.whoAreYou=function()
{
console.log("I am a "+this.type);
}
Animal.prototype.say = function(){
console.log("Not implemented");
};
var Cat =function () {
Animal.call(this);
this.type="cat";
};
Cat.prototype=Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.say=function()
{
console.log("meow");
}
var Dog =function () {
Animal.call(this);
this.type="dog";
};
Dog.prototype=Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.say=function()
{
console.log("bark");
}
var animal=new Animal();
var dog = new Animal('dog');
var cat=new Animal('cat');
animal.whoAreYou(); //I am a undefined
animal.say(); //Not implemented
dog.whoAreYou(); //I am a dog
dog.say(); //bark
cat.whoAreYou(); //I am a cat
cat.say(); //meow
/****************************************/
/* version 1 */
/****************************************/
var Animal = function(params) {
this.say = function()
{
console.log(params);
}
};
var Cat = function() {
Animal.call(this, "moes");
};
var Dog = function() {
Animal.call(this, "vewa");
};
var cat = new Cat();
var dog = new Dog();
cat.say();
dog.say();
/****************************************/
/* version 2 */
/****************************************/
var Cat = function(params) {
this.say = function()
{
console.log(params);
}
};
var Dog = function(params) {
this.say = function()
{
console.log(params);
}
};
var Animal = function(type) {
var obj;
var factory = function()
{
switch(type)
{
case "cat":
obj = new Cat("bark");
break;
case "dog":
obj = new Dog("meow");
break;
}
}
var init = function()
{
factory();
return obj;
}
return init();
};
var cat = new Animal('cat');
var dog = new Animal('dog');
cat.say();
dog.say();
si vous voulez vous assurer que vos classes de base et leurs membres sont strictement abstraits voici une classe de base qui fait cela pour vous:
class AbstractBase{
constructor(){}
checkConstructor(c){
if(this.constructor!=c) return;
throw new Error(`Abstract class ${this.constructor.name} cannot be instantiated`);
}
throwAbstract(){
throw new Error(`${this.constructor.name} must implement abstract member`);}
}
class FooBase extends AbstractBase{
constructor(){
super();
this.checkConstructor(FooBase)}
doStuff(){this.throwAbstract();}
doOtherStuff(){this.throwAbstract();}
}
class FooBar extends FooBase{
constructor(){
super();}
doOtherStuff(){/*some code here*/;}
}
var fooBase = new FooBase(); //<- Error: Abstract class FooBase cannot be instantiated
var fooBar = new FooBar(); //<- OK
fooBar.doStuff(); //<- Error: FooBar must implement abstract member
fooBar.doOtherStuff(); //<- OK
Le mode Strict rend impossible la journalisation de l'appelant dans la méthode throwAbstract, mais l'erreur devrait se produire dans un environnement de débogage qui indiquerait la trace de la pile.