developer tip

JavaScript에서 추상 기본 클래스를 어떻게 만듭니 까?

copycodes 2020. 8. 20. 18:59
반응형

JavaScript에서 추상 기본 클래스를 어떻게 만듭니 까?


JavaScript에서 추상 기본 클래스를 시뮬레이션 할 수 있습니까? 가장 우아한 방법은 무엇입니까?

다음과 같이하고 싶습니다.-

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

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

출력되어야합니다 : 'bark', 'meow'


추상 클래스를 만드는 간단한 방법은 다음과 같습니다.

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

Dog 똑같이 보입니다.

그리고 이것이 당신의 시나리오가 진행되는 방식입니다.

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

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.

Dean Edwards의 기본 클래스를 확인하십시오 : http://dean.edwards.name/weblog/2006/03/base/

또는 JavaScript의 고전적 상속에 대한 Douglas Crockford의 예제 / 기사 : 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! 

객체 프로토 타입을 사용하여 추상 클래스를 만들 수 있습니다. 간단한 예제는 다음과 같습니다.

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

위의 방법을 변경할 수 있는지 여부는 구현할 때 귀하에게 달려 있습니다. 자세한 관찰은 여기 를 방문 하십시오 .


질문은 꽤 오래되었지만 추상 "클래스"를 만들고 해당 유형의 객체 생성을 차단하는 방법에 대한 몇 가지 가능한 솔루션을 만들었습니다.

//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이 property를 가지고 있기 때문 abstract입니다. Animal.prototype프로토 타입 체인에 있는 것이 아닌 동물인지 확인하기 위해 다음을 수행합니다.

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

그래서 가장 가까운 프로토 타입 객체에 abstract속성 이 있는지 확인합니다. 프로토 타입 에서 직접 생성 된 객체 만이 Animal조건을 true로 설정합니다. 함수 hasOwnProperty는 프로토 타입이 아닌 현재 객체의 속성 만 확인하므로 여기에서 프로토 타입 체인이 아닌 속성이 선언되었는지 100 % 확신 할 수 있습니다.

Object의 자손 인 모든 객체는 hasOwnProperty 메서드를 상속합니다 . 이 메서드는 개체에 해당 개체의 직접 속성으로 지정된 속성이 있는지 여부를 확인하는 데 사용할 수 있습니다. in 연산자와 달리이 메서드는 개체의 프로토 타입 체인을 확인하지 않습니다. 그것에 대해 더 알아보기 :

내 제안에서 우리 는 @ Jordão의 현재 베스트 답변처럼 constructor매번 변경할 필요가 없습니다 Object.create.

솔루션은 또한 계층 구조에서 많은 추상 클래스를 생성 할 수있게하므로 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에서 작동합니다.

작동 방식을 이해하지 못한다면 프로토 타입 체인에 대해 읽어보십시오.


Javascript는 상속을 가질 수 있습니다. 아래 URL을 확인하십시오.

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

앤드류


강제 할 수있는 또 다른 것은 추상 클래스가 인스턴스화되지 않았는지 확인하는 것입니다. Abstract 클래스 생성자로 설정된 FLAG 함수로 작동하는 함수를 정의하여이를 수행 할 수 있습니다. 그러면 throw 될 예외를 포함하는 생성자를 호출하는 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();

}) ()


Factory이 경우 디자인 패턴을 사용할 수 있습니다 . Javascript를 사용 prototype하여 부모의 구성원을 상속합니다.

부모 클래스 생성자를 정의합니다.

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!

나는 All That 답변 특히 처음 두 ( some and jordão )가 기존 프로토 타입 기본 JS 개념으로 질문에 명확하게 대답 한다고 생각 합니다.
이제 동물 클래스 생성자가 구성에 전달 된 매개 변수에 따라 동작하기를 원하므로 이것은 Creational Patterns예를 들어 Factory Pattern 의 기본 동작과 매우 유사하다고 생각합니다 .

여기에서는 그렇게 작동하도록 약간의 접근 방식을 만들었습니다.

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

기본 클래스와 해당 멤버가 엄격하게 추상적인지 확인하려면 다음을 수행하는 기본 클래스가 있습니다.

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

Strict 모드에서는 throwAbstract 메서드에서 호출자를 기록 할 수 없지만 스택 추적을 표시하는 디버그 환경에서 오류가 발생해야합니다.

참고 URL : https://stackoverflow.com/questions/597769/how-do-i-create-an-abstract-base-class-in-javascript

반응형