在JavaScript中是否有可能模拟抽象基类?最优雅的方法是什么?
比如说,我想做这样的事情:
var cat = new Animal('cat');
var dog = new Animal('dog');
cat.say();
dog.say();
它应该输出:
meow
bark
在JavaScript中是否有可能模拟抽象基类?最优雅的方法是什么?
比如说,我想做这样的事情:
var cat = new Animal('cat');
var dog = new Animal('dog');
cat.say();
dog.say();
它应该输出:
meow
bark
根据ES6,您可以使用JavaScript类和继承来实现所需的功能。
JavaScript类是在ECMAScript 2015中引入的,主要是对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.
console.log(this.constructor.name + " eating")
来输出“猫正在吃”,“狗正在吃”或“马正在吃”。 - Felix Eve一种简单的方法是这样的:
/**
@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
类和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');
}
狗
看起来就像它。
以下是您的情景:
var cat = new Cat();
var dog = new Dog();
cat.say();
dog.say();
请在这里尝试(查看控制台输出)。
你的意思是这样的吗:
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
whattosay
),这个答案清楚地询问了提问者是否正在寻找所提议的答案。它并没有声称给出JavaScript中抽象类的解决方案。提问者没有回复我或其他任何人,所以我不知道它是否对他有用。如果一个五年前针对别人问题的回答对您没起作用,我很抱歉。 - some你可能想要查看 Dean Edwards 的 Base Class:http://dean.edwards.name/weblog/2006/03/base/
或者,可以参考 Douglas Crockford 的这篇关于 JavaScript 经典继承的例子和文章:http://www.crockford.com/javascript/inheritance.html
在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 = new Animal('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!
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!
你可能还想强制确保抽象类不会被实例化。你可以通过定义一个函数来实现,该函数作为抽象类构造函数的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();
})()
这个问题很旧了,但我想提供一些可能的解决方案,来创建抽象“类”并阻止该类型对象的创建。
//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();
您可以看到,最后一个对象给我们报错了,这是因为原型中的Animal具有abstract
属性。 为了确保它是Animal而不是在原型链中具有Animal.prototype
的其他对象,我执行以下操作:
Object.getPrototypeOf(this).hasOwnProperty("abstract")
abstract
属性,只有直接从Animal
原型创建的对象才会满足此条件。函数hasOwnProperty
仅检查当前对象的属性,而不检查其原型,因此这可以确保该属性在此处声明而不在原型链中。
在我的建议中,我们不必像@Jordão提供的当前最佳答案中那样在每次从Object继承的每个对象都继承了hasOwnProperty方法。此方法可用于确定对象是否将指定属性作为该对象的直接属性;与in运算符不同,此方法不会沿着对象的原型链向下检查。更多信息:
Object.create
之后更改constructor
。abstract
属性即可。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中可以工作。
如果您不明白它的工作原理,请了解有关原型链的知识。
var SampleInterface = {
addItem : function(item){}
}
当你实现它时,你可以选择更改上述方法或不更改。如果您想进行详细观察,可以访问这里。