ES2015 (ES6)`class` 구문은 어떤 이점을 제공합니까?


87

ES6 수업에 대해 많은 질문이 있습니다.

class구문 사용의 이점은 무엇입니까 ? 공개 / 비공개 / 정적이 ES7의 일부가 될 것이라고 읽었습니다. 그게 이유인가요?

또한 class다른 종류의 OOP입니까 아니면 여전히 JavaScript의 프로토 타입 상속입니까? 을 사용하여 수정할 수 있습니까 .prototype? 또는 동일한 객체이지만 선언하는 두 가지 방법입니다.

속도 이점이 있습니까? 큰 앱과 같은 큰 애플리케이션이 있다면 유지 관리 / 이해가 더 쉬울까요?


내 의견 일뿐입니다. 최신 구문은 이해하기 훨씬 쉬우 며 사전 경험이 많이 필요하지 않습니다 (특히 대부분의 사람들이 OOP 언어에서 왔기 때문에). 그러나 프로토 타입 객체 모델은 알만한 가치가 있으며 새로운 구문을 사용하는 것을 결코 배울 수 없으며, 이미 알고 있지 않는 한 프로토 타입 코드 작업이 더 어려울 것입니다. 나는 이전 구문 모든 내 자신의 코드를 작성 선택할 것 그래서 나는 그 선택이있을 때
자크 스미스

답변:


120

새로운 class구문를 들면, 지금은 주로 문법 설탕. (하지만 좋은 종류의 설탕이 있습니다.) ES2015-ES2020 class에는 생성자 함수 Reflect.construct(서브 클래 싱 ErrorArray¹ 포함)로 할 수없는 작업이 없습니다 . (그것은 이다 아마 당신이 할 수있는 ES2021에 몇 가지가있을 것입니다 class당신이 그렇지 않으면 할 수없는 : private 필드 , 개인 방법정적 필드 / 개인 정적 방법 .)

또한 class다른 종류의 OOP입니까 아니면 여전히 JavaScript의 프로토 타입 상속입니까?

생성자 함수 ( , 등)를 사용 하는 것을 좋아 한다면 더 깔끔하고 편리한 구문으로 우리가 항상 가지고 있었던 것과 동일한 프로토 타입 상속 new Foo입니다. (특히 ES5 이전 버전에서는 할 수 없었던 Arrayor Error에서 파생 된 경우입니다 . 이제 Reflect.construct[ spec , MDN ]으로는 가능하지만 이전 ES5 스타일에서는 불가능합니다.)

을 사용하여 수정할 수 있습니까 .prototype?

예, prototype클래스를 만든 후에도 클래스 생성자 에서 개체를 수정할 수 있습니다 . 예를 들어, 이것은 완벽하게 합법적입니다.

class Foo {
    constructor(name) {
        this.name = name;
    }
    
    test1() {
        console.log("test1: name = " + this.name);
    }
}
Foo.prototype.test2 = function() {
    console.log("test2: name = " + this.name);
};

속도 이점이 있습니까?

이것에 대한 특정 관용구를 제공함으로써, 나는 그것의 가정 가능한 엔진이 더 나은 작업 최적화를 할 수있을 수 있음. 그러나 그들은 이미 최적화에 매우 능숙합니다. 나는 큰 차이를 기대하지 않습니다.

ES2015 (ES6) class구문은 어떤 이점을 제공합니까?

간단히 말해서 : 처음에 생성자 함수를 사용하지 않는 경우 선호 Object.create하거나 유사한 class것은 유용하지 않습니다.

생성자 함수를 사용하는 경우 다음과 같은 이점이 있습니다 class.

  • 구문이 더 간단하고 오류가 덜 발생합니다.

  • 그건 훨씬 더 쉽게 (다시, 더 적은 오류가 발생하기 쉬운) 이전보다 새로운 구문을 사용하여 상속 계층 구조를 설정합니다.

  • classnew생성자 함수와 함께 사용하지 못하는 일반적인 오류로부터 사용자를 보호합니다 (생성자에 this대해 유효한 개체가 아닌 경우 생성자가 예외를 throw하도록 함 ).

  • 새 구문을 사용하면 부모 프로토 타입 버전의 메서드를 호출하는 것이 이전 ( 또는 super.method()대신) 보다 훨씬 간단합니다 .ParentConstructor.prototype.method.call(this)Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)

다음은 계층 구조에 대한 구문 비교입니다.

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

class Manager extends Employee {
    constructor(first, last, position, department) {
        super(first, last, position);
        this.department = department;
    }

    personMethod() {
        const result = super.personMethod();
        // ...use `result` for something...
        return result;
    }

    managerMethod() {
        // ...
    }
}

예:

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    // ...
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.employeeMethod = function() {
    // ...
};

var Manager = function(first, last, position, department) {
    if (!(this instanceof Manager)) {
        throw new Error("Manager is a constructor function, use new with it");
    }
    Employee.call(this, first, last, position);
    this.department = department;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.personMethod = function() {
    var result = Employee.prototype.personMethod.call(this);
    // ...use `result` for something...
    return result;
};
Manager.prototype.managerMethod = function() {
    // ...
};

라이브 예 :

보시다시피, 틀리기 쉽고 재 입력하기 지루한 반복적이고 장황한 내용이 많이 있습니다 (이것이 제가 스크립트를 작성 했던 이유 입니다.


¹ "ES2015-ES2018 class에는 생성자 함수 및 Reflect.construct(하위 클래스 Error및 포함 Array)로 할 수없는 작업이 없습니다. "

예:


9
이것이 공정한 비교인지 모르겠습니다. 엉망진창없이 비슷한 물체를 얻을 수 있습니다. 그것은 고전적인 상속을 근사화하기보다는 프로토 타입 체인을 통한 객체 링크의 JS 방식을 사용합니다. 나는 이것을 보여주기 위해 당신의 것을 기반으로 간단한 예를 보여주는 요점 을 만들었습니다 .
Jason Cust 2015

8
@JasonCust : 예, 공정한 비교입니다. 왜냐하면 ES6이 class. 자바 스크립트는 당신이 무슨 일을하는지 인하지 않으려면 사용 생성자 함수에하지 않아도 유연 충분하지만, 그건 다른 에서 class-의 요점은 class자바 스크립트의 단순화 의사 고전 상속입니다.
TJ Crowder

3
@JasonCust : 예, 다른 방식입니다. 나는 그것을 "더 많은 원주민"이라고 부르지 않을 것입니다 (나는 Kyle이 알고 있지만 그것은 Kyle입니다). 생성자 함수는 JavaScript의 기본입니다. 모든 내장 유형을 살펴보십시오 (반 생성자 파벌이 어떻게 든 Symbol엉망이 된 것을 제외하고 ). 그러나 다시 한 번 좋은 점은 사용하지 않으려면 사용할 필요가 없다는 것입니다. 내 작업에서 생성자 함수가 여러 곳에서 의미가 있고 new명확성 을 향상시키는 의미를 발견했습니다 . 및 Object.create차종은 ESP, 다른 많은 곳에서 감지. 외관 상황. 상호 보완 적입니다. :-)
TJ Crowder

4
meh. 나는 이것에 대한 필요성을 사지 않습니다. 나는 oop에서 벗어나기 위해 C #에서 물러 났고 이제 oop은 자바 스크립트에 들어온다. 나는 유형을 좋아하지 않는다. 모든 것은 var / object / function이어야합니다. 그게 다야. 더 이상 관련없는 키워드가 없습니다. 상속은 사기입니다. 유형과 비 유형 사이의 좋은 싸움을보고 싶다면. 비누 대 휴식을 살펴보십시오. 누가 이겼는지 우리 모두 알고 있습니다.
foreyez

3
@foreyez : class구문은 이전보다 JavaScript를 더 이상 형식화하지 않습니다. 내가 대답에서 말했듯이, 이미 거기에 있었던 것에 대해 대부분 (훨씬) 더 간단한 구문입니다. 그것은 절대적으로 필요했고, 사람들은 계속해서 오래된 구문을 잘못 이해하고있었습니다. 또한 이전보다 더 이상 상속을 사용해야한다는 의미도 아닙니다. (당신이 이전 구문으로 "클래스"를 설정 한 적이 있다면 물론, 하나, 새로운 구문을 그렇게 할 필요가 없습니다.)
TJ 크라우

22

ES6 클래스는 오늘날 우리가 사용하는 프로토 타입 클래스 시스템을위한 구문 설탕입니다. 그들은 당신의 코드를 더 간결하고 자체 문서화하기 때문에 (제 생각에는) 그것을 사용하기에 충분한 이유입니다.

Babel을 사용하여이 ES6 클래스를 트랜스 파일합니다.

class Foo {
  constructor(bar) {
    this._bar = bar;
  }

  getBar() {
    return this._bar;
  }
}

다음과 같은 것을 줄 것입니다.

var Foo = (function () {
  function Foo(bar) {    
    this._bar = bar;
  }

  Foo.prototype.getBar = function () {
    return this._bar;
  }

  return Foo;
})();

두 번째 버전은 훨씬 더 복잡하지 않고 유지 관리 할 코드가 더 많습니다. 상속이 관련되면 이러한 패턴은 더욱 복잡해집니다.

클래스는 우리가 사용해 왔던 동일한 프로토 타입 패턴으로 컴파일되기 때문에 동일한 프로토 타입 조작을 수행 할 수 있습니다. 여기에는 런타임시 메소드 추가,에서 메소드 액세스 Foo.prototype.getBar등이 포함됩니다.

액세스를 원하지 않는 개체를 내 보내지 않는 것을 기반으로하지만 오늘날 ES6에는 개인 정보 보호에 대한 몇 가지 기본 지원이 있습니다. 예를 들어 다음을 수행 할 수 있습니다.

const BAR_NAME = 'bar';

export default class Foo {
  static get name() {
    return BAR_NAME;
  }
}

BAR_NAME다른 모듈을 직접 참조하는 데 사용할 수 없습니다.

extends메서드와 같은 함수 및 속성의 검증되지 않은 해시를 사용하는 Backbone과 같은 많은 라이브러리가이를 지원하거나 해결하려고 시도했지만 프로토 타입을 다루지 않는 프로토 타입 상속을 노출하는 일관된 시스템이 없습니다.

JS 코드가 더 복잡해지고 코드베이스가 커짐에 따라 우리는 상속 및 모듈과 같은 것을 처리하기 위해 많은 패턴을 발전시키기 시작했습니다. 모듈의 전용 범위를 만드는 데 사용되는 IIFE에는 많은 중괄호와 괄호가 있습니다. 이들 중 하나가 누락되면 완전히 다른 작업을 수행하는 유효한 스크립트가 생성 될 수 있습니다 (모듈이 다음 모듈을 매개 변수로 전달한 후 세미콜론을 건너 뛰는 것은 거의 좋지 않음).

tl; dr : 우리가 이미하고있는 일에 당연한 일이며 코드에서 귀하의 의도를 명확히합니다.

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