在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
http://www.webreference.com/js/column79/
安德鲁
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~
这是完整示例代码的Codepen链接。
//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!
class Animal {
constructor() {
if(new.target===Animal)
throw new Error('Cannot be instantiated')
}
//Non-abstract method
makeSound()
{
console.log(this.sound);
}
}
class Cat extends Animal {
constructor(sound) {
super();
this.sound = sound;
}
}
class Dog extends Animal {
constructor(sound) {
super();
this.sound = sound;
}
}
let cat1 = new Cat('Meow')
cat1.makeSound();
let dog1 = new Dog('Bark')
dog1.makeSound();
let genericAnimal = new Animal(); //Throws Error
//ES6 - Abstract class with Abstract and Non-Abstract methods
class Animal {
constructor() {
if(new.target===Animal)
throw new Error('Abstract Class cannot be instantiated')
}
//abstract method
makeSound()
{
throw new Error('Abstract Method cannot be called')
}
//non-abstract method
displayType()
{
console.log(this.name," instanceof Animal",this instanceof Animal);
}
}
class Cat extends Animal {
constructor(name,sound) {
super();
this.name = name;
this.sound = sound;
}
//abstract method defined in child class implementation
makeSound()
{
console.log("Cat ",this.name, " is making ", this.sound);
}
}
class Dog extends Animal {
constructor(name,sound) {
super();
this.name = name;
this.sound = sound;
}
}
//Dog.prototype.constructor = Dog;
let cat1 = new Cat('Bella','Meow')
cat1.makeSound();
cat1.displayType();
let dog1 = new Dog('Jimmy','Bark')
dog1.displayType();
dog1.makeSound(); //throws error
//let genericAnimal = new Animal(); //throws error
如果您想确保基类及其成员严格抽象,这里有一个为您实现此功能的基类:
```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
严格模式使在 throwAbstract 方法中记录调用者不可能,但错误应该发生在能显示堆栈跟踪的调试环境中。
/****************************************/
/* 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();
创建型模式
的基本行为,例如工厂模式。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
Animal
构造函数可以被视为一种反模式,超类不应该知道它的子类。(违反了Liskov原则和开闭原则) Link for reference: http://programmers.stackexchange.com/questions/219543/should-a-class-know-about-its-subclasses - Len"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();