자바 스크립트의 클래스에서 상속하는 방법은 무엇입니까?


99

PHP / Java에서 다음을 수행 할 수 있습니다.

class Sub extends Base
{
}

그리고 Super 클래스의 모든 공개 / 보호 된 메서드, 속성, 필드 등은 필요한 경우 재정의 할 수있는 Sub 클래스의 일부가됩니다.

Javascript에서 이에 상응하는 것은 무엇입니까?





그 crockford 방식이 여전히 작동합니까? ZParenizor.inherits (Parenizor);
Loren Shqipognja

답변:


80

나는 지금 이것을하는 방법을 변경했으며 생성자 함수와 그 prototype속성을 사용하지 않으려 고 노력 했지만 2010의 이전 답변은 여전히 ​​맨 아래에 있습니다. 나는 지금 선호한다 Object.create(). Object.create모든 최신 브라우저에서 사용할 수 있습니다.

나는 그주의해야한다 Object.create일반적으로 훨씬 느린 사용하는 것보다 new함수 생성자.

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

Object.create 사용의 큰 이점 중 하나는 defineProperties 인수 를 전달할 수 있다는 것입니다.이 인수는 클래스의 속성에 액세스하고 열거 할 수있는 방법에 대한 중요한 제어를 제공하며, 함수를 사용하여 인스턴스를 생성합니다. 인스턴스를 반환하는 대신 끝에 초기화를 수행 할 수 있습니다.

var Base = {};

function createBase() {
  return Object.create(Base, {
    doSomething: {
       value: function () {
         console.log("Doing something");
       },
    },
  });
}

var Sub = createBase();

function createSub() {
  return Object.create(Sub, {
    doSomethingElse: {
      value: function () {
        console.log("Doing something else");
      },
    },
  }); 
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

이것은 2010의 원래 답변입니다.

function Base ( ) {
  this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
 console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"

5
sub.prototype.constructor 값은 어떻습니까? 하위 값으로 설정해야한다고 생각합니다.
막시무스

예약 된 키워드 ( 'super')를 클래스 이름으로 사용하는 것 외에도 예제를 실행할 수 없었습니다. jsbin.com/ixiyet/8/edit
MOnsDaR

@MOnsDaR 나는 자료로 개명
비욘

내가 반환 하는 alert()것을보기 위해 사용 하는 경우 나는 instance.showColor()여전히 얻는다 undefined. jsbin.com/uqalin/1
MOnsDaR

1
@MOnsDaR 그것은 콘솔 로그이기 때문에 경고를 표시하기 위해 아무것도 반환하지 않습니다. showColor에 return 문이 있습니까?
Bjorn

190

JavaScript에는 클래스가 없지만 여러 가지 방법으로 상속 및 동작 재사용을 얻을 수 있습니다.

유사 클래식 상속 (프로토 타이핑을 통해) :

function Super () {
  this.member1 = 'superMember1';
}
Super.prototype.member2 = 'superMember2';

function Sub() {
  this.member3 = 'subMember3';
  //...
}
Sub.prototype = new Super();

new연산자 와 함께 사용해야합니다 .

var subInstance = new Sub();

함수 애플리케이션 또는 "생성자 체인":

function Super () {
  this.member1 = 'superMember1';
  this.member2 = 'superMember2';
}


function Sub() {
  Super.apply(this, arguments);
  this.member3 = 'subMember3';
}

이 접근 방식은 new운영자 와 함께 사용해야합니다 .

var subInstance = new Sub();

첫 번째 예제와의 차이점 applySuper생성자가 this내부 객체에 Sub할당 될 때 thison에 할당 된 속성을 Super새 인스턴스에 직접 추가한다는 것입니다 ( 예 : subInstance속성 포함 member1member2직접 ( subInstance.hasOwnProperty('member1') == true;)).

첫 번째 예에서 이러한 속성은 프로토 타입 체인을 통해 도달 하며 내부 [[Prototype]]개체 에 존재 합니다.

기생 상속 또는 전력 생성자 :

function createSuper() {
  var obj = {
    member1: 'superMember1',
    member2: 'superMember2'
  };

  return obj;
}

function createSub() {
  var obj = createSuper();
  obj.member3 = 'subMember3';
  return obj;
}

이 접근 방식은 기본적으로 "객체 확대"를 기반으로하며 new연산자 를 사용할 필요가 없으며 보시 this다시피 키워드가 관련되지 않습니다.

var subInstance = createSub();

ECMAScript 5th Ed. Object.create방법:

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var superInstance = {
  member1: 'superMember1',
  member2: 'superMember2'
};

var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';

위의 방법은 Crockford가 제안한 프로토 타입 상속 기법 입니다.

개체 인스턴스는 다른 개체 인스턴스에서 상속됩니다.

이 기술은 상속 된 속성이 모든 새 개체 인스턴스에 복사되지 않기 때문에 단순한 "개체 확대"보다 나을 수 있습니다 . 위의 예 에서는 속성 만 물리적으로 만 포함되어 있으므로 기본 개체가 확장 개체 [[Prototype]]의 로 설정되어 있기 때문 입니다 .subInstancemember3


3
상속을 위해 인스턴스를 사용하지 마십시오- 프로토 타입 객체에서 직접 상속 하려면 ES5 Object.create()또는 사용자 정의 clone()함수 (예 : mercurial.intuxication.org/hg/js-hacks/raw-file/tip/clone.js )를 사용하십시오. 설명은 stackoverflow.com/questions/1404559/…에 대한 주석을 참조하십시오
Christoph

감사합니다 @Christoph, 나는 Object.create방법 을 언급하려고했다 :)
CMS

1
Sub의 프로토 타입에 Super의 인스턴스 멤버가 있기 때문에 이것은 적절한 상속이 아닙니다. 따라서 모든 Sub의 인스턴스는 동일한 member1변수 를 공유하므로 전혀 바람직하지 않습니다. 물론 그들은 그것을 다시 쓸 수 있지만 그것은 의미가 없습니다. github.com/dotnetwise/Javascript-FastClass 는 더 나은 설탕 솔루션입니다.
Adaptabi

안녕하세요 @CMS, 설명해 주시겠습니까? 첫 번째 예제에서 하위 클래스에 대한 상속을 설정하기 위해 부모 클래스의 인스턴스를 만들어야하는 이유는 무엇입니까? 저는이 줄에 대해 이야기하고 있습니다 : Sub.prototype = new Super();. 스크립트 실행 중에 두 클래스가 모두 사용되지 않으면 어떻게됩니까? 성능 문제인 것 같습니다. 자식 클래스가 실제로 사용되지 않는데 왜 부모 클래스를 만들어야합니까? 자세히 설명해 주시겠습니까? 다음은 문제에 대한 간단한 데모입니다. jsfiddle.net/slavafomin/ZeVL2 감사합니다!
Slava Fomin II

마지막 예제를 제외한 모든 예제에는 Super에 대한 "class"와 Sub에 대한 "class"가 있으며 Sub 인스턴스를 만듭니다. Object.create 예제와 유사한 예제를 추가 할 수 있습니까?
Luke

49

2019 년 이후에이 페이지를 방문하신 분들께

ECMAScript 표준 (ES6) 의 최신 버전 에서는 키워드를 사용할 수 있습니다 class.

클래스 정의는 일반이 아닙니다 object. 따라서 클래스 멤버 사이에는 쉼표가 없습니다. 클래스의 인스턴스를 만들려면 new키워드 를 사용해야합니다 . 기본 클래스에서 상속하려면 다음을 사용하십시오 extends.

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
var myVehicle = new Vehicle('rocky');
myVehicle.getName(); // => 'rocky'

기본 클래스에서 상속하려면 다음을 사용하십시오 extends.

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

var myCar = new Car('bumpy');

myCar.getName(); // => 'bumpy'
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true

파생 클래스에서 생성자 또는 메서드의 super를 사용하여 기본 클래스에 액세스 할 수 있습니다.

  • 부모 생성자를 호출하려면 super().
  • 다른 회원에게 전화를 걸려면 예를 들어 super.getName().

클래스 사용에 더 많은 것이 있습니다. 이 주제에 대해 더 자세히 알고 싶다면 Axel Rauschmayer 박사의 “ Classes in ECMAScript 6 ”을 추천합니다. *

출처


1
후드, classextends프로토 타입 체인 (매우 유용) 신택스는 다음 stackoverflow.com/a/23877420/895245
치로 틸리郝海东冠状病六四事件法轮功

정보 'instance.name'여기 'mycar.name'은 클래스 이름을 반환합니다. 이것은 ES6 및 ESnext의 기본 동작입니다. mycar.name는 '자동차'반환됩니다 여기에 있습니다
Shiljo 폴슨

7

자, JavaScript에는 "클래스 상속"이없고 "프로토 타입 상속"만 있습니다. 따라서 클래스 "트럭"을 만들고 "자동차"의 하위 클래스로 표시하지 않습니다. 대신 개체 "Jack"을 만들고 "John"을 프로토 타입으로 사용한다고 말합니다. John이 "4 + 4"가 얼마인지 안다면 Jack도 알고 있습니다.

http://javascript.crockford.com/prototypal.html http://javascript.crockford.com/prototypal.html 여기에서 Douglas Crockford의 프로토 타입 상속에 대한 기사를 읽어 보시기 바랍니다. 그는 또한 JavaScript가 다른 OO 언어에서와 같이 "유사한"상속을 갖도록 만드는 방법을 보여준 다음이를 설명합니다. 실제로는 사용되지 않는 방식으로 자바 스크립트를 깨는 것을 의미합니다.


Jack의 프로토 타입이 John이라고 가정 해 보겠습니다. 런타임 동안 John에 속성 / 동작을 추가했습니다. Jack에게서 그 속성 / 행동을 얻을 수 있습니까?
Ram Bavireddi

당신은 확실히 할 것입니다. 예를 들어, 사람들이 일반적으로 모든 문자열 객체에 "trim ()"메서드를 추가하는 방법입니다 (내장되지 않음). 여기에서 예제를 참조하십시오. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
naivists

6

나는이 인용문이 가장 깨달음을 얻는다.

본질적으로 JavaScript "클래스" 는 생성자 역할을하는 Function 객체와 첨부 된 프로토 타입 객체입니다. ( 출처 : Guru Katz )

저는 객체보다는 생성자를 사용하는 것을 좋아하기 때문에 여기에서 CMS가 설명 하는 "의사-고전적 상속"방법에 부분적 입니다. 다음은 프로토 타입 체인을 사용한 다중 상속 의 예입니다 .

// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
    this.isLifeform = true;
}

// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
    this.isAnimal = true;
}
Animal.prototype = new Lifeform();

// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
    this.isMammal = true;
}
Mammal.prototype = new Animal();

// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true

// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
    ,tiger.hasOwnProperty("isLifeform") // false
    ,tiger.hasOwnProperty("isAnimal")   // false
    ,tiger.hasOwnProperty("isMammal")   // false
    ,tiger.hasOwnProperty("isCat")      // true
);

// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A    = 1;
Animal.prototype.B      = 2;
Mammal.prototype.C      = 3;
Cat.prototype.D         = 4;

console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4

// Look at the instance object again
console.log(tiger);
// You'll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) );  // Mammal 
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform

여기에 MDN의 또 다른 좋은 리소스가 있으며 여기 에 jsfiddle이 있으므로 사용해 볼 수 있습니다 .


4

Javascript 상속은 실제로 클래스가 없기 때문에 Java 및 PHP와 약간 다릅니다. 대신 메서드와 멤버 변수를 제공하는 프로토 타입 개체가 있습니다. 이러한 프로토 타입을 연결하여 개체 상속을 제공 할 수 있습니다. 이 질문을 조사 할 때 내가 찾은 가장 일반적인 패턴은 Mozilla 개발자 네트워크 에 설명되어 있습니다. 수퍼 클래스 메서드에 대한 호출을 포함하고 경고 메시지에 로그를 표시하도록 예제를 업데이트했습니다.

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);

개인적으로 Javascript의 상속이 어색하다고 생각하지만 이것이 내가 찾은 최고의 버전입니다.


3

당신은 할 수 없습니다 (고전적인 의미에서). 자바 스크립트는 프로토 타입 언어입니다. Javascript에서 "클래스"를 선언하지 않음을 알 수 있습니다. 객체의 상태와 메서드를 정의하기 만하면됩니다. 상속을 생성하려면 개체를 가져와 프로토 타입을 만듭니다. 프로토 타입이 새로운 기능으로 확장되었습니다.


1

.inheritWith.fastClass 라이브러리 를 사용할 수 있습니다 . 가장 인기있는 라이브러리보다 빠르며 때로는 기본 버전보다 빠릅니다.

사용하기 매우 쉬움 :

function Super() {
   this.member1 = "superMember";//instance member
}.define({ //define methods on Super's prototype
   method1: function() { console.log('super'); } //prototype member
}.defineStatic({ //define static methods directly on Super function 
   staticMethod1: function() { console.log('static method on Super'); }
});

var Sub = Super.inheritWith(function(base, baseCtor) {
   return {
      constructor: function() {//the Sub constructor that will be returned to variable Sub
         this.member3 = 'subMember3'; //instance member on Sub
         baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
      },
      method1: function() { 
         console.log('sub'); 
         base.method1.apply(this, arguments); //call the base class' method1 function
      }
}

용법

var s = new Sub();
s.method1(); //prints:
//sub 
//super

1
function Person(attr){
  this.name = (attr && attr.name)? attr.name : undefined;
  this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;

  this.printName = function(){
    console.log(this.name);
  }
  this.printBirthYear = function(){
    console.log(this.birthYear);
  }
  this.print = function(){
    console.log(this.name + '(' +this.birthYear+ ')');
  }
}

function PersonExt(attr){
  Person.call(this, attr);

  this.print = function(){
    console.log(this.name+ '-' + this.birthYear);
  }
  this.newPrint = function(){
    console.log('New method');
  }
}
PersonExt.prototype = new Person();

// Init object and call methods
var p = new Person({name: 'Mr. A', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A

var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A

1

많은 게시물을 읽은 후이 솔루션을 생각해 냈습니다 ( jsfiddle here ). 대부분의 경우 더 정교한 것이 필요하지 않습니다.

var Class = function(definition) {
    var base = definition.extend || null;
    var construct = definition.construct || definition.extend || function() {};

    var newClass = function() { 
        this._base_ = base;        
        construct.apply(this, arguments);
    }

    if (definition.name) 
        newClass._name_ = definition.name;

    if (definition.extend) {
        var f = function() {}       
        f.prototype = definition.extend.prototype;      
        newClass.prototype = new f();   
        newClass.prototype.constructor = newClass;
        newClass._extend_ = definition.extend;      
        newClass._base_ = definition.extend.prototype;         
    }

    if (definition.statics) 
        for (var n in definition.statics) newClass[n] = definition.statics[n];          

    if (definition.members) 
        for (var n in definition.members) newClass.prototype[n] = definition.members[n];    

    return newClass;
}


var Animal = Class({

    construct: function() {        
    },

    members: {

        speak: function() {
            console.log("nuf said");                        
        },

        isA: function() {        
            return "animal";           
        }        
    }
});


var Dog = Class({  extend: Animal,

    construct: function(name) {  
        this._base_();        
        this.name = name;
    },

    statics: {
        Home: "House",
        Food: "Meat",
        Speak: "Barks"
    },

    members: {
        name: "",

        speak: function() {
            console.log( "ouaf !");         
        },

        isA: function(advice) {
           return advice + " dog -> " + Dog._base_.isA.call(this);           
        }        
    }
});


var Yorkshire = Class({ extend: Dog,

    construct: function(name,gender) {
        this._base_(name);      
        this.gender = gender;
    },

    members: {
        speak: function() {
            console.log( "ouin !");           
        },

        isA: function(advice) {         
           return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);       
        }        
    }
});


var Bulldog = function() { return _class_ = Class({ extend: Dog,

    construct: function(name) {
        this._base_(name);      
    },

    members: {
        speak: function() {
            console.log( "OUAF !");           
        },

        isA: function(advice) {         
           return "bulldog -> " + _class_._base_.isA.call(this,advice);       
        }        
    }
})}();


var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();

var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();

var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();

var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("nice"));
bulldog.speak();

1

CMS의 답변 덕분에 잠시 동안 프로토 타입과 Object.create를 조작 한 후 여기에 표시된대로 apply를 사용하여 상속에 대한 깔끔한 솔루션을 찾을 수있었습니다.

var myNamespace = myNamespace || (function() {
    return {

        BaseClass: function(){
            this.someBaseProperty = "someBaseProperty";
            this.someProperty = "BaseClass";
            this.someFunc = null;
        },

        DerivedClass:function(someFunc){
            myNamespace.BaseClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "DerivedClass";
        },

        MoreDerivedClass:function(someFunc){
            myNamespace.DerivedClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "MoreDerivedClass";
        }
    };
})();


1
function Base() {
    this.doSomething = function () {
    }
}

function Sub() {
    Base.call(this); // inherit Base's method(s) to this instance of Sub
}

var sub = new Sub();
sub.doSomething();

2
단순히 코드를 게시하지 말고 그것이하는 일과 질문에 대한 답을 설명하십시오.
Patrick Hund

1

ES6 수업 :

Javascript에는 클래스가 없습니다. 자바 스크립트의 클래스는 자바 스크립트의 프로토 타입 상속 패턴 위에 구축 된 구문 적 설탕 일뿐 입니다. JS class를 사용하여 프로토 타입 상속을 적용 할 수 있지만 실제로 여전히 내부에서 생성자 함수를 사용하고 있음을 인식하는 것이 중요합니다.

이러한 개념 es6은 extends 키워드를 사용하여 '클래스' 에서 확장 할 때도 적용됩니다 . 이것은 프로토 타입 체인에 추가 링크를 생성합니다. 그만큼__proto__

예:

class Animal {
  makeSound () {
    console.log('animalSound');
  }
}

class Dog extends Animal {
   makeSound () {
    console.log('Woof');
  }
}


console.log(typeof Dog)  // classes in JS are just constructor functions under the hood

const dog = new Dog();

console.log(dog.__proto__ === Dog.prototype);   
// First link in the prototype chain is Dog.prototype

console.log(dog.__proto__.__proto__ === Animal.prototype);  
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal

Object.create ()

Object.create()또한 자바 스크립트에서 JS에서 상속을 만드는 방법입니다. Object.create()는 새 객체를 생성하는 함수이며은 기존 객체를 인수로 사용합니다. __proto__새로 생성 된 객체 의 속성에 인자로받은 객체를 할당 합니다. 다시 말하지만 우리가 JS가 구현하는 프로토 타입 상속 패러다임에 묶여 있음을 깨닫는 것이 중요합니다.

예:

const Dog = {
  fluffy: true,
  bark: () => {
      console.log('woof im a relatively cute dog or something else??');
  }
};

const dog = Object.create(Dog);

dog.bark();


0

JavaScript에는 클래스가 없기 때문에 JavaScript의 클래스에서 상속 할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.