| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [View Raw Code] [Original HTTPS Page] |
자바스크립트는 클래스 기반의 객체 지향 프로그래밍 언어가 아니지만 프로토타입 기반의 아주 유연한 객체 지향 프로그래밍 스타일로 객체 지향 언어의 상속과 캡슐화 등의 추상적 개념도 구현할 수 있습니다. 우리는 이 장에서 ES6에 등장한 클래스(Class)에 대해 알아봅니다.
ES6의 클래스가 프로토타입 기반 객체 지향 모델을 폐지한 것이 아닙니다. 클래스도 함수이기 때문이지요.
우리는 기존 객체 지향 프로그래밍을 구현할 때, 생성자 함수와 프로토타입, 클로저를 이용해왔습니다.
var Person = (function () {
// Constructor, 생성자 함수
function Person(name) {
this._name = name;
}
// public method
Person.prototype.sayHi = function () {
console.log('HELLO! ' + this._name);
};
// return constructor
return Person;
}());
// 인스턴스 생성
var me = new Person('Amy');
me.sayHi(); // HELLO! Amy
console.log(me instanceof Person); // true이를 클래스로 구현해볼까요?
class Person {
// constructor, 생성자
constructor(name) {
this._name = name;
}
sayHi() {
console.log(`HELLO! ${this._name}`);
}
}
// 인스턴스 생성
const me = new Person('Amy');
me.sayHi(); // HELLO! Amy
console.log(me instanceof Person); // true동일한 동작을 하는 것처럼 보이죠? 구문도 단순화되었습니다. 즉, ES6의 클래스는 함수이며 기존 프로타입 기반 패턴에 문법적 설탕(Syntactic sugar) 이 됩니다. 단, 클래스와 생성자 함수가 동일하게 동작하지는 않으니까 주의(일각에선 이런 이유로 클래스를 문법적 설탕으로 인정하지 않아요)하세요.
클래스는 class 키워드를 사용하여 정의하며, 파스칼 케이스로 작성하는 것이 일반적입니다.
클래스를 어떻게 작성하는지는 언급하지 않겠습니다. 기존 프로토타입과 유사하기 때문이죠. 이 장에서는 클래스의 특징만 짚고 넘어갑니다.
const NamedClass = class UnNamedClass {};
const name = new NamedClass();
console.log(name); // UnNamedClass {}
new UnNamedClass(); // ReferenceError: UnNamedClass is not definedclass NamedClass {};
const name = NamedClass(); // TypeError: Class constructor NamedClass cannot be invoked without 'new'class NamedClass {};
const name = NamedClass(); // TypeError: Class constructor NamedClass cannot be invoked without 'new'
// NamedClass는 생성자 함수(constructor)입니다.
console.log(Object.getPrototypeOf(name).constructor === NamedClass); // true클래스 필드에는 메서드만 선언할 수 있습니다.
class NamedClass {
name = ''; // SyntaxError
// 클래스 필드의 선언과 초기화는 반드시 constructor에서 실시합니다.
constructor(name) {
this.name = name;
}
}현재는 정상적으로 동작하는데, TC39 프로세스의 stage 3(candidate) 단계에 있는 클래스 몸체에서 직접 인스턴스 필드를 선언하고 private 인스턴스 필드를 선언할 수 있는 프로포절(Class field declarations proposal)의 필드 정의를 최신 브라우저와 최신 Node.js가 구현하였기 때문입니다.
최신 브라우저와 Node.js 12버전 이상에서는 여러 속성들을 지원하고 있습니다. 자세한 내용은 여기를 참조해주세요.
class NamedClass {
constructor(prop) {
this.prop = prop;
}
static staticMethod() {
// 정적 메서드는 this를 사용할 수 없습니다.
// 정적 메서드 내부에서 this는 클래스의 인스턴스가 아닌 클래스 자신을 가리킵니다.
return 'staticMethod';
}
prototypeMethod() {
return this.prop;
}
}
// 정적 메서드는 클래스 이름으로 호출합니다.
console.log(NamedClass.staticMethod());
const name = new NamedClass(123);
// 정적 메서드는 인스턴스로 호출할 수 없습니다.
console.log(name.staticMethod()); // Uncaught TypeError: NamedClass.staticMethod is not a functionvar NamedClass = (function () {
// 생성자 함수
function NamedClass(prop) {
this.prop = prop;
}
NamedClass.staticMethod = function () {
return 'staticMethod';
};
NamedClass.prototype.prototypeMethod = function () {
return this.prop;
};
return NamedClass;
}());
var name = new NamedClass(123);
console.log(name.prototypeMethod()); // 123
console.log(NamedClass.staticMethod()); // staticMethod
console.log(name.staticMethod()); // Uncaught TypeError: name.staticMethod is not a functionsuper 키워드는 양이 많아 단원을 분리합니다.
super 키워드는 함수처럼 호출하거나 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드입니다.
super 키워드는 아래와 같이 동작합니다.
자세히 알아볼까요?
new 연산자와 함께 서브 클래스를 호출하면서 전달한 인수는 super 호출을 통해 슈퍼 클래스의 constructor()에 전달됩니다.
슈퍼 클래스에서 추가한 프로퍼티와 서브 클래스에서 추가한 프로퍼티를 갖는 인스턴스를 생성한다면 서브 클래스의 constructor를 생략할 수 없습니다.
또한 new 연산자와 함께 서브 클래스를 호출하면서 전달한 인수를 슈퍼 클래스의 constructor에 super 키워드를 통하여 전달할 수 있습니다.
// 슈퍼 클래스
class Base {
constructor(a, b) { // ④
this.a = a;
this.b = b;
}
}
// 서브 클래스
class Derived extends Base {
// 암묵적으로 constructor가 정의되지만 직접 입력할 수 있습니다.
// constructor(...args) { super(...args); }
constructor(a, b, c) {
super(a, b);
this.c = c;
}
}
const derived = new Derived(1, 2, 3);
console.log(derived); // Derived {a: 1, b: 2, c: 3}이 때 주의사항은 아래와 같습니다.
class Base {}
class Derived extends Base {
constructor() {
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
console.log('constructor call');
}
}
const derived = new Derived();class Base {}
class Derived extends Base {
constructor() {
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
this.a = 1;
super();
}
}
const derived = new Derived(1);// 슈퍼 클래스
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
}
// 서브 클래스
class Derived extends Base {
sayHi() {
// super.sayHi는 슈퍼 클래스의 프로토타입 메서드를 가리킵니다.
return `${super.sayHi()}. how are you doing?`;
}
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee. how are you doing?super는 자심을 참조하고 있는 메서드가 바인딩된 객체의 프로토타입을 가리킵니다.
super를 참조로 사용하는 형태들을 볼까요?
서브 클래스의 프로토타입 메서드 내에서 super.메서드는 슈퍼 클래스의 프로토타입 메서드를 가리킵니다.
// 슈퍼 클래스
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
}
// 서브 클래스
class Derived extends Base {
sayHi() {
// super.sayHi는 슈퍼 클래스의 프로토타입 메서드를 가리킵니다.
return `${super.sayHi()}. how are you doing?`;
}
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee. how are you doing?// 슈퍼 클래스
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
}
class Derived extends Base {
sayHi() {
// __super는 Base.prototype을 가리킵니다.
const __super = Object.getPrototypeOf(Derived.prototype);
return `${__super.sayHi.call(this)} how are you doing?`;
}
}const obj = {
// [[HomeObject]]를 갖습니다.
foo() {},
// [[HomeObject]]를 갖지 않습니다.
bar: function () {}
};결국 super 참조를 의사 코드로 표현하면 아래와 같습니다.
super = Object.getPrototypeOf([[HomeObject]])// 슈퍼 클래스
class Base {
static sayHi() {
return 'Hi!';
}
}
// 서브 클래스
class Derived extends Base {
static sayHi() {
// super.sayHi는 슈퍼 클래스의 정적 메서드를 가리킵니다.
return `${super.sayHi()} how are you doing?`;
}
}
console.log(Derived.sayHi()); // Hi! how are you doing?클래스가 단독으로 인스턴스를 생성하는 과정보다 상속을 통해 인스턴스를 생성하는 과정이 더 복잡합니다.
서브 클래스가 new 연산자와 함께 호출되면 아래의 과정을 통해 인스턴스를 생성합니다.
| Back | FazBrowse Home | New Git URL |