如何在 JavaScript 中创建抽象基类?

是否可以用 JavaScript 模拟抽象基类? 最优雅的方法是什么?

比如,我想做这样的事情:-

var cat = new Animal('cat');
var dog = new Animal('dog');


cat.say();
dog.say();

它应该输出: “树皮”,“喵”

164899 次浏览

Javascript 可以继承,查看下面的 URL:

Http://www.webreference.com/js/column79/

安德鲁

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();

因为 __proto__不是标准变量,所以不能保证它能正常工作,但至少在 Firefox 和 Safari 中能正常工作。

如果你不了解它是如何工作的,那么就阅读原型链吧。

是否可以用 JavaScript 模拟抽象基类?

当然可以。在 JavaScript 中实现类/实例系统的方法大约有一千种。以下是其中之一:

// 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 = 新动物(‘ cat’) ;

当然,这并不是一个抽象的基类,你的意思是:

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!

你的意思是这样的:

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

你可能想看看爱德华兹院长的基本级别: http://dean.edwards.name/weblog/2006/03/base/

此外,还有一个关于经典继承的例子/文章,道格拉斯·克罗克福特是 JavaScript: http://www.crockford.com/javascript/inheritance.html

您可能希望强制执行的另一件事是确保您的抽象类没有被实例化。您可以通过定义一个作为抽象类构造函数设置的 FLAG 函数来实现这一点。然后,这将尝试构造 FLAG,它将调用其包含异常的构造函数来抛出异常。例如:

(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();


})()

您可以使用对象原型创建抽象类,一个简单的例子如下:

var SampleInterface = {
addItem : function(item){}
}

您可以更改上述方法或不更改,它取决于您何时实现它。对于一个详细的观察,您可能想要访问 给你

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!

创建抽象类的一个简单方法是:

/**
@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“ class”和 say方法是抽象的。

创建一个实例将抛出一个错误:

new Animal(); // throws

这就是你如何从中“继承”的:

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看起来很像。

这就是你的剧本:

var cat = new Cat();
var dog = new Dog();


cat.say();
dog.say();

Fiddle 给你(查看控制台输出)。

我认为所有这些特别回答了前两个问题(通过 一些Jordão) ,用传统的基于原型的 JavaScript 概念清楚地回答了这个问题。

现在,由于您希望动物类构造函数按照传递给构造函数的参数行事,因此我认为这非常类似于 Creational Patterns的基本行为,例如 工厂模式

在这里,我做了一个小方法,使其工作的方式。

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();

问题是相当老,但我创建了一些可能的解决方案,如何创建抽象“类”和块创建对象的类型。

//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();

正如你可以看到最后一个对象给我们的错误,这是因为动物在原型具有属性 abstract。为了确保它是动物,而不是在原型链中有 Animal.prototype的东西,我做:

Object.getPrototypeOf(this).hasOwnProperty("abstract")

因此,我检查我最接近的原型对象具有 abstract属性,只有直接从 Animal原型创建的对象将具有这个条件为 true。函数 hasOwnProperty只检查当前对象的属性,而不检查它的原型,因此我们可以100% 确定属性是在这里声明的,而不是在原型链中。

每个从 Object 派生出来的对象都继承了 HasOwnProperty方法。此方法可用于确定对象是否具有指定的属性作为该对象的直接属性; 与 in 操作符不同,此方法不检查对象的原型链。更多信息:

在我的建议,我们不必改变 constructor每次后,Object.create喜欢它是目前最好的答案@Jord ão。

解决方案还允许在层次结构中创建多个抽象类,只需在原型中创建 abstract属性即可。

在这种情况下,我们可以使用 Factory设计模式。

定义父类构造函数。

var Animal = function() {
this.type = 'animal';
return this;
}
Animal.prototype.tired = function() {
console.log('sleeping: zzzZZZ ~');
}

然后创建儿童课程。

// These are the child classes
Animal.cat = function() {
this.type = 'cat';
this.says = function() {
console.log('says: meow');
}
}

然后定义子类构造函数。

// 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;
}

测试一下。

var timmy = Animal.born('cat');
console.log(timmy.type) // cat
timmy.says(); // meow
timmy.tired(); // zzzZZZ~

下面是完整示例编码的 密码链接

//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!

JavaScript 类和继承(ES6)

根据 ES6,您可以使用 JavaScript 类和继承来完成您需要的任务。

在 ECMAScript 2015中引入的 JavaScript 类主要是基于 JavaScript 现有的基于原型的继承的语法糖。

参考资料: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

首先,我们定义我们的抽象类。这个类不能被实例化,但是可以被扩展。 我们还可以定义必须在扩展此函数的所有类中实现的函数。

/**
* 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");
}
}

然后,我们可以创建具体的类,这些类将从抽象类继承所有的函数和行为。

/**
* 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 {}

结果是..。

// 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.

如果你想确保你的基类和它们的成员是严格抽象的,这里有一个基类可以帮你做到这一点:

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

严格模式使得调用方不可能记录在 throw 抽象方法中,但是错误应该发生在显示堆栈跟踪的调试环境中。

"use strict";






function Abstract (...arg){
    

// create abstract constructor
if(  this.constructor.name === 'Object' || this.constructor === Abstract ) throw { ErrorType : "can't call abstract class with new !" , }
 



// ceate abstract method
Object.defineProperty( this , 'config' , {
value : function(){
console.log('config parent')
}
});




// or other
return this ;
};
 



class Home extends Abstract{
name = '';
constructor(...arg){
super(...arg) ;
}


config(){
// this method not working
console.log('config child')
}
}


let y = new Home( "home" , 'dasd');
y.config();