수색…


통사론

  • class Foo {}
  • Foo 클래스는 Bar {}를 확장합니다.
  • class Foo {constructor () {}}
  • class Foo {myMethod () {}}
  • class Foo {get myProperty () {}}
  • class Foo {set myProperty (newValue) {}}
  • class Foo {static myStaticMethod () {}}
  • class Foo {정적 get myStaticProperty () {}}
  • const Foo = 클래스 Foo {};
  • const Foo = 클래스 {};

비고

class 지원은 2015 표준의 일부로 JavaScript에만 추가되었습니다.

Javascript 클래스는 JavaScript의 기존 프로토 타입 기반 상속에 대한 구문 적 설탕입니다. 이 새로운 구문은 JavaScript에 새로운 객체 지향 상속 모델을 도입하지 않고 객체와 상속을 처리하는 간단한 방법입니다. class 선언은 기본적으로 생성자 function 를 수동으로 정의하고 생성자의 프로토 타입에 속성을 추가하기위한 속기입니다. 중요한 차이점은 함수를 직접 호출 할 수 있다는 것입니다 ( new 키워드없이). 반면에 직접 호출 된 클래스는 예외를 throw합니다.

class someClass {
    constructor () {}
    someMethod () {}
}
 
console.log(typeof someClass);               
console.log(someClass);
console.log(someClass === someClass.prototype.constructor);                         
console.log(someClass.prototype.someMethod);
 
// Output:
// function
// function someClass() { "use strict"; }
// true
// function () { "use strict"; }

이전 버전의 JavaScript를 사용하는 경우 대상 플랫폼에서 이해할 수있는 버전으로 코드를 컴파일하려면 또는 와 같은 변환기가 필요합니다.

클래스 생성자

대부분의 클래스의 기본 부분은 생성자입니다.이 생성자는 각 인스턴스의 초기 상태를 설정하고 new 호출 할 때 전달 된 매개 변수를 처리합니다.

class 블록에서는 constructor 라는 메서드를 정의하는 것처럼 정의되지만, 실제로는 특별한 경우로 처리됩니다.

class MyClass {
    constructor(option) {
        console.log(`Creating instance using ${option} option.`);
        this.option = option;
    }
}

사용 예 :

const foo = new MyClass('speedy'); // logs: "Creating instance using speedy option"

여기서 주목해야 할 것은 클래스 생성자가 static 키워드를 통해 정적으로 만들어 질 수 없다는 것입니다.

정적 메소드

정적 메소드 및 속성은 클래스 / 생성자 자체 에 정의되며 인스턴스 객체에는 정의되지 않습니다. 이것들은 static 키워드를 사용하여 클래스 정의에 지정됩니다.

class MyClass {
    static myStaticMethod() {
        return 'Hello';
    }

    static get myStaticProperty() {
        return 'Goodbye';
    }
}

console.log(MyClass.myStaticMethod()); // logs: "Hello"
console.log(MyClass.myStaticProperty); // logs: "Goodbye"

정적 속성이 객체 인스턴스에 정의되어 있지 않음을 알 수 있습니다.

const myClassInstance = new MyClass();

console.log(myClassInstance.myStaticProperty); // logs: undefined

그러나, 서브 클래스에 정의되어 있습니다 :

class MySubClass extends MyClass {};

console.log(MySubClass.myStaticMethod()); // logs: "Hello"
console.log(MySubClass.myStaticProperty); // logs: "Goodbye"

게터와 세터

게터와 세터를 사용하면 클래스의 특정 속성을 읽고 쓰는 데 필요한 사용자 지정 동작을 정의 할 수 있습니다. 사용자에게는 일반 속성과 동일하게 나타납니다. 그러나 내부적으로 사용자가 제공하는 사용자 지정 함수는 속성에 액세스 할 때 값을 결정하고 (getter) 속성을 할당 할 때 필요한 변경을 수행하는 데 사용됩니다 (setter).

class 정의에서 getter는 get 키워드로 접두어가 붙은 인자없는 메소드처럼 작성됩니다. setter는 하나의 인수 (새 값이 할당 됨)를 허용하고 대신 set 키워드가 사용된다는 점을 제외하면 비슷합니다.

다음은 .name 속성에 대한 getter 및 setter를 제공하는 예제 클래스입니다. 할당 될 때마다 내부 이름 .names_ 배열에 새 이름을 기록합니다. 액세스 할 때마다 최신 이름이 반환됩니다.

class MyClass {
    constructor() {
        this.names_ = [];
    }

    set name(value) {
        this.names_.push(value);
    }

    get name() {
        return this.names_[this.names_.length - 1];
    }
}

const myClassInstance = new MyClass();
myClassInstance.name = 'Joe';
myClassInstance.name = 'Bob';

console.log(myClassInstance.name); // logs: "Bob"
console.log(myClassInstance.names_); // logs: ["Joe", "Bob"]

setter 만 정의하면 속성에 액세스하려고하면 undefined 가 반환됩니다.

const classInstance = new class {
    set prop(value) {
        console.log('setting', value);
    }
};

classInstance.prop = 10; // logs: "setting", 10

console.log(classInstance.prop); // logs: undefined

getter 만 정의하면 속성을 할당하려고해도 아무런 효과가 없습니다.

const classInstance = new class {
    get prop() {
        return 5;
    }
};

classInstance.prop = 10;

console.log(classInstance.prop); // logs: 5

클래스 상속

상속은 다른 객체 지향 언어와 마찬가지로 작동합니다. 수퍼 클래스에 정의 된 메서드는 확장 하위 클래스에서 액세스 할 수 있습니다.

서브 클래스가 자신의 생성자를 선언하는 경우, 그것은을 통해 부모 생성자 호출해야합니다 super() 가 액세스 할 수 있습니다 this .

class SuperClass {

    constructor() {
        this.logger = console.log;
    }

    log() {
        this.logger(`Hello ${this.name}`);
    }

}

class SubClass extends SuperClass {

    constructor() {
        super();
        this.name = 'subclass';
    }

}

const subClass = new SubClass();

subClass.log(); // logs: "Hello subclass"

비공개 멤버

JavaScript는 기술적으로 비공개 멤버를 언어 기능으로 지원하지 않습니다. Douglas Crockford에 의해 기술 된 프라이버시는 생성자 함수의 모든 인스턴스 생성 호출마다 각각 생성되는 클로저 (보존 된 함수 범위)를 통해 대신 에뮬레이트됩니다.

Queue 예제는 생성자 함수를 사용하여 로컬 상태를 보존하고 권한있는 메소드를 통해 액세스 할 수있는 방법을 보여줍니다.

class Queue {

  constructor () {                    // - does generate a closure with each instantiation.

    const list = [];                  // - local state ("private member").

    this.enqueue = function (type) {  // - privileged public method
                                      //   accessing the local state
      list.push(type);                //   "writing" alike.
      return type;
    };
    this.dequeue = function () {      // - privileged public method
                                      //   accessing the local state
      return list.shift();            //   "reading / writing" alike.
    };
  }
}


var q = new Queue;            //
                              //
q.enqueue(9);                 // ... first in ...
q.enqueue(8);                 //
q.enqueue(7);                 //
                              //
console.log(q.dequeue());     // 9 ... first out.
console.log(q.dequeue());     // 8
console.log(q.dequeue());     // 7
console.log(q);               // {}
console.log(Object.keys(q));  // ["enqueue","dequeue"]

Queue 형의 모든 인스턴스화로, 생성자는 클로저를 생성합니다.

따라서 Queue 유형의 자체 메소드 enqueuedequeue ( Object.keys(q) 참조)는 계속해서 생성 시간에 보존 된 해당 범위에서 계속해서 살아있는 list 대한 액세스 권한을 갖습니다.

이 패턴을 사용하여 권한있는 공용 메서드를 통해 개인 멤버를 에뮬레이트합니다. 모든 인스턴스에서 모든 속성 메서드에 대해 추가 메모리가 소비된다는 점을 명심해야합니다 (공용 / 재사용 할 수없는 코드이기 때문에). 클로저 내에 저장 될 상태의 양 / 크기에 대해서도 마찬가지입니다.

동적 메서드 이름

[] 사용하여 객체의 속성에 액세스하는 방법과 비슷한 방법으로 이름을 지정할 때 표현식을 평가하는 기능도 있습니다. 이것은 동적 속성 이름을 갖는 데 유용 할 수 있지만 종종 Symbol과 함께 사용됩니다.

let METADATA = Symbol('metadata');

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
  // example using symbols
  [METADATA]() {
    return {
      make: this.make,
      model: this.model
    };
  }

  // you can also use any javascript expression

  // this one is just a string, and could also be defined with simply add()
  ["add"](a, b) {
    return a + b;
  }

  // this one is dynamically evaluated
  [1 + 2]() {
    return "three";
  }
}

let MazdaMPV = new Car("Mazda", "MPV");
MazdaMPV.add(4, 5); // 9
MazdaMPV[3](); // "three"
MazdaMPV[METADATA](); // { make: "Mazda", model: "MPV" }

행동 양식

메소드를 클래스에서 정의하여 함수를 수행하고 선택적으로 결과를 리턴 할 수 있습니다.
그들은 호출자로부터 인수를받을 수 있습니다.

class Something {
    constructor(data) {
        this.data = data
    }

    doSomething(text) {
        return {
            data: this.data,
            text
        }
    }
}

var s = new Something({})
s.doSomething("hi") // returns: { data: {}, text: "hi" }

클래스를 사용한 개인 데이터 관리

클래스를 사용하는 가장 일반적인 장애물 중 하나는 개인 상태를 처리하기위한 적절한 접근 방법을 찾는 것입니다. 개인 상태를 처리하는 4 가지 일반적인 솔루션이 있습니다.

기호 사용

에서 정의 된 기호는 새로운 원시 형은, ES2015 년에 도입 MDN

기호는 객체 속성의 식별자로 사용할 수있는 고유하고 변경할 수없는 데이터 유형입니다.

기호를 속성 키로 사용하면 열거 할 수 없습니다.

따라서 for var in 또는 Object.keys 사용하여 공개되지 않습니다.

따라서 기호를 사용하여 개인 데이터를 저장할 수 있습니다.

const topSecret = Symbol('topSecret'); // our private key; will only be accessible on the scope of the module file
export class SecretAgent{
    constructor(secret){
        this[topSecret] = secret; // we have access to the symbol key (closure)
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret[topSecret]); // we have access to topSecret
        };
    }
}

symbols 는 고유하므로 개인 속성에 액세스하려면 원래 기호를 참조해야합니다.

import {SecretAgent} from 'SecretAgent.js'
const agent = new SecretAgent('steal all the ice cream');
// ok lets try to get the secret out of him!
Object.keys(agent); // ['coverStory'] only cover story is public, our secret is kept.
agent[Symbol('topSecret')]; // undefined, as we said, symbols are always unique, so only the original symbol will help us to get the data.

하지만 100 % 사적인 것은 아닙니다. 그 요원을 부셔 버리자! Object.getOwnPropertySymbols 메서드를 사용하여 객체 심볼을 가져올 수 있습니다.

const secretKeys = Object.getOwnPropertySymbols(agent);
agent[secretKeys[0]] // 'steal all the ice cream' , we got the secret.

WeakMaps 사용하기

WeakMapWeakMap 에 추가 된 새로운 유형의 객체입니다.

MDN에 정의 된대로

WeakMap 객체는 키가 약하게 참조되는 키 / 값 쌍의 모음입니다. 키는 오브젝트 여야하며 값은 임의의 값이 될 수 있습니다.

WeakMap 또 다른 중요한 특징은 MDN에 정의 된 바와 같습니다.

WeakMap의 키가 약하게 열립니다. 즉, 키에 대한 다른 강력한 참조가 없으면 전체 항목이 가비지 수집기에서 WeakMap에서 제거됩니다.

아이디어는 WeakMap을 전체 클래스의 정적 맵으로 사용하여 각 인스턴스를 키로 유지하고 개인 데이터를 해당 인스턴스 키의 값으로 유지하는 것입니다.

따라서 클래스 내부에서만 WeakMap 컬렉션에 액세스 할 수 있습니다.

우리 요원에게 WeakMap 사용해보십시오.

const topSecret = new WeakMap(); // will hold all private data of all instances.
export class SecretAgent{
    constructor(secret){
        topSecret.set(this,secret); // we use this, as the key, to set it on our instance private data
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret.get(this)); // we have access to topSecret
        };
    }
}

const topSecret 은 모듈 클로저 내부에 정의되어 있으며 인스턴스 속성에 바인드하지 않았 topSecret 접근법은 완전히 비공개이며 에이전트 topSecret 도달 할 수 없습니다.

생성자 내부의 모든 메소드 정의

여기에 아이디어는 생성자 내부의 모든 방법과 회원을 정의에 할당하지 않고 개인 회원에 액세스 할 수 폐쇄를 사용하는 것입니다 this .

   export class SecretAgent{
        constructor(secret){
            const topSecret = secret;
            this.coverStory = 'just a simple gardner';
            this.doMission = () => {
                figureWhatToDo(topSecret); // we have access to topSecret
            };
        }
    }

이 예에서도 데이터는 100 % 전용이며 수업 외부에 도달 할 수 없으므로 상담원은 안전합니다.

명명 규칙 사용

비공개 인 부동산에는 _ 가 접두사로 사용됩니다.

이 접근법의 경우 데이터는 실제로 비공개가 아닙니다.

export class SecretAgent{
    constructor(secret){
        this._topSecret = secret; // it private by convention
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(this_topSecret); 
        };
    }
}

클래스 이름 바인딩

ClassDeclaration의 Name은 다른 범위에서 다른 방식으로 바인딩됩니다.

  1. 클래스가 정의 된 범위는 - let 바인딩
  2. 클래스 자체의 범위 - class {} - const 바인딩의 {} 내에 있음
class Foo {
  // Foo inside this block is a const binding
}
// Foo here is a let binding

예를 들어,

class A {
  foo() {
    A = null; // will throw at runtime as A inside the class is a `const` binding
  }
}
A = null; // will NOT throw as A here is a `let` binding

이것은 함수에 대해 동일하지 않습니다 -

function A() {
  A = null; // works
}
A.prototype.foo = function foo() {
  A = null; // works
}
A = null; // works


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow