기술나눔

ES6 수업 요약 (9)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

ES6의 클래스는 객체 지향 프로그래밍을 위한 구문 설탕으로, 객체의 구조와 동작을 정의하는 간결한 방법을 제공합니다.

JavaScript 언어에서 인스턴스 객체를 생성하는 전통적인 방법은 생성자를 이용하는 것입니다. 아래는 예시입니다.

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

기본적으로 ES6 클래스는 단지 구문적 설탕으로 간주될 수 있으며, 대부분의 기능은 ES5에서 달성할 수 있습니다. 새로운 클래스 작성 방법은 객체 프로토타입의 작성 방법을 더 명확하고 객체 지향 프로그래밍의 구문과 더 유사하게 만듭니다. 위 코드는 ES6 클래스를 사용하여 다음과 같이 다시 작성되었습니다.

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

ES6 클래스는 생성자를 작성하는 또 다른 방법으로 간주될 수 있습니다.

class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

----------------------------------------------------------------------------
class Point {
  constructor() {
    // ...
  }
  toString() {
    // ...
  }
  toValue() {
    // ...
  }
}
// 等同于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

주요 특징:

1. 선언적 구문: 클래스를 선언하려면 class 키워드를 사용하세요.
2. 생성자: 생성자 메서드를 사용하여 클래스 인스턴스를 초기화합니다.
3. 인스턴스 메소드: 클래스 내부에 정의된 공통 메서드로, 인스턴스 속성에 액세스하는 데 사용합니다.
4. 정적 메소드: static 키워드를 사용하여 정의되며 클래스의 인스턴스에 의존하지 않습니다.
5. 인스턴스 속성: 생성자에서 초기화하거나 필드 선언 구문을 사용합니다(현재 3단계 제안).
6. 상속: 확장 키워드를 사용하여 구현되었습니다.
7. 슈퍼 키워드: 하위 클래스의 생성자에서 상위 클래스의 생성자 또는 메서드를 호출합니다.
8. 게터와 세터: 속성에 대한 접근자를 정의하려면 get 및 set을 사용하세요.
9. 개인 속성 및 메서드: #을 사용하여 개인 속성과 메서드를 정의합니다(현재 3단계 제안).

1. 기본 클래스 정의 및 인스턴스화

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Point(${this.x}, ${this.y})`;
    }
}

let point = new Point(10, 20);
console.log(point.toString()); // 输出: Point(10, 20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. 정적 메서드 및 속성

class MathUtils {
	constructor() {
    	console.log(MyClass.myStaticProp); // 42
    }
    static add(a, b) {
        return a + b;
    }
    static myStaticProp = 42;
}

console.log(MathUtils.add(1, 2)); // 输出: 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. 상속과 슈퍼

class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

class Square extends Rectangle {
    constructor(sideLength) {
        super(sideLength, sideLength);
    }
}

let square = new Square(5);
console.log(square.area()); // 输出: 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4. 게터와 세터

class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    get area() {
        return this.width * this.height;
    }

    set width(newWidth) {
        if (newWidth > 0) {
            this.width = newWidth;
        } else {
            console.log("Width must be positive.");
        }
    }
}

let rect = new Rectangle(4, 5);
console.log(rect.area); // 输出: 20
rect.width = -10; // 输出: Width must be positive.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

수업에 관한 참고 사항

(1) 엄격 모드

내부적으로 클래스와 모듈은 기본적으로 엄격 모드이므로 실행 모드를 지정하기 위해 use strict를 사용할 필요가 없습니다. 코드가 클래스나 모듈로 작성되어 있는 한 엄격 모드만 사용할 수 있습니다. 향후 모든 코드가 실제로 모듈에서 실행된다는 점을 고려하면 ES6은 실제로 전체 언어를 엄격 모드로 업그레이드합니다.

(2) 프로모션이 없습니다

클래스에는 ES5와 완전히 다른 변수 호이스트(hoist)가 없습니다.

new Foo(); // ReferenceError
class Foo {}

//不会报错
//因为 Bar 继承 Foo 的时候, Foo 已经有定义了。
//但是,如果存在 class 的提升,上面代码就会报错,
//因为 class 会被提升到代码头部,而 let 命令是不提升的,
//所以导致 Bar 继承 Foo 的时候, Foo 还没有定义。
{
  let Foo = class {};
  class Bar extends Foo {
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(3) 이름 속성

본질적으로 ES6 클래스는 ES5 생성자의 래퍼일 뿐이므로 name 속성을 포함하여 함수의 많은 기능이 Class에 상속됩니다.

class Point {}
Point.name // "Point"
//name 属性总是返回紧跟在 class 关键字后面的类名。
  • 1
  • 2
  • 3

(4)발전기 방식

메서드 앞에 별표(*)가 있으면 해당 메서드가 생성기 함수라는 의미입니다.

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}
for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

//Foo 类的 Symbol.iterator 方法前有一个星号,表示该方法是一个 Generator 函数。 
//Symbol.iterator 方法返回一个 Foo 类的默认遍历器, for...of 循环会自动调用这个遍历器。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(5) 이것의 요점

클래스 메서드에 이 항목이 포함되어 있으면 기본적으로 클래스의 인스턴스를 가리킵니다. 하지만 이 방법을 단독으로 사용하면 오류가 발생할 수 있으므로 매우 주의해야 합니다.

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }
  print(text) {
    console.log(text);
  }
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

이것을 사용하지 말고 생성자에서 이것을 바인딩하십시오.

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

이것을 사용하지 말고 화살표 기능을 사용하십시오.

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

이것을 사용하지 말고 프록시를 사용하십시오

function selfish (target) {
  const cache = new WeakMap();
  const handler = {
    get (target, key) {
      const value = Reflect.get(target, key);
      if (typeof value !== 'function') {
        return value;
      }
      if (!cache.has(value)) {
        cache.set(value, value.bind(target));
      }
      return cache.get(value);
    }
  };
  const proxy = new Proxy(target, handler);
  return proxy;
}
const logger = selfish(new Logger());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18