(ES6) - 16 class 오브젝트

  • SUNGMIN SHIN
  • 45 Minutes
  • 2017년 9월 15일

[ ECMA Script6 ]

(http://book.naver.com/bookdb/book_detail.nhn?bid=11556385)


16.1 Class 선언문


1
2
class ClassName { }
class ClassName extends super_name {}

class 오브젝트는 위와 같은 형태로 선언 합니다. class 오브젝트는 function 오브젝트, String 오브젝트와 같이 하나의 오브젝트 타입 입니다.
하지만 class에 typeof로 형을 체크해보면 ‘function’이 반환되는 데 실제로 사용방식, 구조가 흡사하기 때문에 ES6에서의 class는 특별한 함수라고 하기도 합니다.
즉 class는 기존의 function를 이용한 prototype의 기반의 class를 만드는 것보다 더 명료한 문법을 제공하는 것으로 정리할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
// class Member
class Member {
// method getName
getName() {
return '이름'
}
}

let obj = new Member();
console.log(obj.getName());


16.2 class 표현식


let ClassName = class {};
let ClassName = class inner_name {};
let ClassName = class extends super_name {};
let ClassName = class inner_name extends super_name {};

함수와 마찬가지고 키워드 class와 class 표현식의 형태로 선언할 수 있습니다. inner_name의 경우 class 내부에서 자신을 호출해야 하는 경우 사용할 수 있지만 함수와 마찬가지로 많이 사용되지는 않습니다.

16.3 class의 특징

class는 function 키워드 함수와 매우 유사하지만 몇 가지 특징이 있습니다.

  • ‘use strict’ 선언 유무와 관계 없이 strict 모드에서 실행
  • method 작성 방식의 차이
  • 전역 스코프에서 선언되어도 window객체에 포함되지 않음


class에 method 작성 방식

function

1
2
3
4
5
6
7
8
9
10
function Member() {}
Member.prototype.setName = function(name) {
this.name = name;
};
Member.prototype.getName = function() {
return this.name;
};
Member.prototype.setTitle = function(title) {
this.title = title;
};

class

1
2
3
4
5
6
7
8
9
10
11
12
class Member {
setName (name) {
this.name = name;
}
getName () {
return this.name
}
}
// method 추가
Member.prototype.setTitle = function(title) {
this.title = title;
}

class에 method를 추가하는 방식은 function 키워드를 사용하는 것과 동일합니다
(이러한 형태로 생성하는 경우 이미 생성된 인스턴스 들에 부하를 줄 수 있으므로 권장하지는 않습니다)
그 외 class 작성 방식의 특징은 메서드 사이에 콤마를 작성하지 않으며 각 메서드 블록의 끝 및 클래스 블록의 끝에 세미콜론은 선택적으로 사용할 수 있다는 점등이 있습니다.


16.3 constructor


constructor는 class 인스턴스를 생성하고 생성한 인스턴스를 초기화하는 역활을 합니다.

1
2
3
4
5
6
7
8
9
10
11
class Member {
constructor(name) {
this.name = name;
}
getName () {
return this.name
}
}

let methodObj = new Member('스포츠');
console.log(Obj.getName());

위의 코드와 같이 new Member()를 실행하면 member.prototype.constructor가 자동으로 호출됩니다.
ES5에서도 constructor는 호출되었지만 엔진이 default constructor를 호출하였기 때문에 이를 활용할 수 없었습니다.

위 코드의 순서를 개념적으로 정리하면 아래와 같습니다.

  1. new Member(‘스포츠’) 실행
  2. new 연산자가 constructor를 호출하면서 파라미터 값을 넘김
  3. 빈 object를 생성
  4. 빈 object에 인스턴스 구성에 필요한 프로퍼티를 설정
  5. constructor의 코드가 실행하기(여기서 인스턴스가 먼저 구성되었으므로 this를 참조할 수 있음)
  6. 생성한 인스턴스 반환


16.5 constructor 반환 값 변경


constructor에는 일반적으로 return문을 작성하지 않고 따로 return문을 선언하지 않는 경우 기본적으로 생성한 인스턴스를 반환합니다.
return 문을 통해 Number, String을 반환하는 경우에는 이를 무시하고 생성한 인스턴스를 반환하지만 아래의 예와 같이 Object는 정상적으로 반환 되며 이 때 반환된 Object로 인해 class 내에 선언한 메서드, 맴버 변수는 사용할 수 없게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Member {
constructor() {
return {
name: '홍길동'
}
}
getName () {
return '이름'
}
}
let memberObj = new Member();
console.log(memberObj.name);
// constructor에서 반환된 Object로 인해 사용할 수 없음
console.log(memberObj.getName());

16.6 getter, setter


class에서 사용되는 getter와 setter는 별도의 키워드를 사용하여 선언할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Member {
constructor() {
this.name = 'null';
}
get getName() {
return this.name;
}
set setName(name) {
this.name = name;
}
}

let test = new Member();
test.setName ='이름';
alert(test.getName);

class문 내에서는 별도의 키워드를 사용하는 것 외에는 method와 작성 방식이 동일하지만 실제 사용할 때에는 함수형태가 아닌 프로퍼티의 형태로 사용되는 것이 다른 점 입니다.


16.7 상속


ES5에서의 상속

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// super Class
function Sports(member) {
this.member = member;
}
// prototype 지정
Sports.prototype.setItem = function(item) {
this.item = item;
};

// sub Class
function Soccer(member) {
// this의 유효범위를 Soccer로 변경하여 Sports.call을 실행
Sports.call(this, member);
}
// Object.create를 이용하여 지정된 프로토타입 객체(Sports.prototype) 및 속성을 갖는 새 객체(setGround) 생성
// 이 과정에서 Soccer의 prototype.constructor가 Sports.prototype.constructor로 지정 됨
Soccer.prototype = Object.create(Sports.prototype, {
setGround: {
value: function(ground) {
this.ground = ground;
}
}
});
// Soccer의 prototype.constructor를 Soccer의 것으로 재지정
Soccer.prototype.constructor = Soccer;

var obj = new Soccer(11);
obj.setItem('축구');
obj.setGround('상암');

console.log(obj.member);
console.log(obj.item);
console.log(obj.ground);

ES6에서의 상속

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// super Class
class Sports {
constructor(member) {
this.member = member;
}
getMember() {
return this.member;
}
};

// sub Class
// extends 키워드를 사용하여 Sports를 상속
class Soccer extends Sports {
setGround(ground) {
this.ground = ground;
}
};

let obj = new Soccer(11);
console.log(obj.getMember());

ES5에서 상속을 위해 작업자가 직접 처리해야했던 부분들을 class 메서드 내에서 쉽게 처리해주는 것을 볼 수 있습니다. 작업의 편의성 뿐아니라 코드의 가독성도 크게 향상 시킬 수 있습니다.

참고) js에서의 상속(http://unikys.tistory.com/320)


16.8 extends 키워드


ES6에서는 extends 키워드를 사용하여 상속을 구현합니다.

class subClassName extends superClassName {

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// super Class
class Sports {
constructor(member) {
this.member = member;
}
getMember() {
return this.member;
}
};

// sub Class
// extends 키워드를 사용하여 Sports를 상속
class Soccer extends Sports {
setGround(ground) {
this.ground = ground;
}
};

let obj = new Soccer(11);
console.log(obj.getMember());

여기서 Sports는 Soccer의 슈퍼 클래스이고 Soccer는 서브 클래스가 됩니다.
위 코드의 실행 순서를 정리해보면 아래와 같습니다.

  1. Soccer 클래스의 constructor 호출 -> 존재하지 않음
  2. 슈퍼 클래스인 Sports의 constructor 호출
  3. 슈퍼 클래스의 constructor의 this가 현재 인스턴스(이후 obj가 될)를 참조 하므로 인스턴스의 member 프로퍼티에 매개변수로 받은 11을 설정
  4. 이렇게 생성한 인스턴스를 obj에 할당

인스턴스의 메서드 역시 가까운 대상에서 먼 대상을 찾아서 실행하는 형태로 여기에서 obj.getMember()를 실행하면 obj.__proto__(서브 클래스)에서 메서드를 찾고 존재하지 않는다면 obj.__proto__.__proto__(슈퍼 클래스)를 찾습니다


16.9 super 키워드


서브 클래스와 슈퍼 클래스에 같은 이름의 메서드가 존재하면 슈퍼 클래스의 메서드는 호출되지 않게되는 데 이때 super 키워드를 사용하면 슈퍼 클래스의 메서드를 호출할 수 있습니다.

메서드 오버라이딩(method overriding)

서브 클래스와 슈퍼 클래스에 같은 이름의 메서드가 있을 때 서브 클래스의 메서드가 호출되는 것을 메서드 오버라이딩이라고 합니다.
메서드 오버 라이딩은 의도적인 접근으로 서브 클래스와 슈퍼 클래스의 메서드가 같은 목적을 가지 것을 나타냄과 동시에 클래스의 목적에 맞게 보완하는 의도로 사용 됩니다.
즉 슈퍼 클래스의 메서드 기능을 사용하면서 서브 클래스에서 기능을 추가, 변경하는 경우에 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Sports {
getGround(ground) {
this.ground = ground;
}
};

class Soccer extends Sports {
getGround(ground) {
// Sports.getGround 호출 (매개변수가 없으므로 this.ground는 undefined)
// 이는 ground 프로퍼티를 슈퍼클래스와 함께 공유하려는 의도
super.getGround();
this.ground = ground;
}
};

// Soccer 인스턴스 생성
let obj = new Soccer();
obj.getGround('상암구장');
console.log(obj.ground);

위와 같이 메서드 이름을 유지하면서 서브 클래스에 적합한 그라운드를 설정할 수 있으므로 더 구체적으로 정의할 수 있습니다. 이를 객체지향에서는 추상화라고 합니다.

아래는 constructor가 실행되는 순서를 보기 위한 예제 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// super Class
class Sports {
constructor(member) {
this.member = member;
console.log(this.member);
}
};

// sub Class
class Soccer extends Sports {
constructor(member) {
// 상속 받은 Sports의 constructor 실행을 위해 super 키워드를 실행
super(member);
this.member = 456;
console.log(this.member);
}
};

// Soccer 인스턴스 생성
let obj = new Soccer(123);

  1. obj에 Soccer(서브 클래스)의 인스턴스를 생성
  2. Soccer(서브 클래스) 내 constructor가 실행
  3. 내부에 존재하는 super 키워드를 만나 Sports(슈퍼 클래스)의 constructor를 실행


16.10 빌트인 오브젝트 상속


extends 키워드로 Array, String등과 같은 빌트인 오브젝트를 상속 받을 수 있습니다. 서브 클래스에서 빌트인 오브젝트를 상속받게 되면 빌트인 오브젝트의 메서드를 마치 서브 클래스에서 선언한 것처럼 사용할 수 있게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ExtendArray extends Array {
constructor() {
// Array 빌트이 오브젝트 상속
super();
}
getTotal() {
// this 값은 이후 push를 통해 받아 오게 됨
let total = 0;
// es6에서 추가된 메서드인 for of 를 사용하여 연산
for(var value of this) {
total+= value;
}
// 연산 합계 반환
return total
}
}

let obj = new ExtendArray();
// Array 내장 메서드인 push를 사용
// 이 시점에 this는 [10, 20]
obj.push(10, 20);
console.log(obj.getTotal());

위 예제는 Array 빌트인 오브젝트를 상속 받아서 Array 메서드인 push를 활용하는 에제로 new 연산자로 인스턴스를 생성하면 Array 오브젝트의 특징을 갖게할 수도 있습니다.


16.12 static 키워드


static 키워드는 class static(정적) 메서드를 정의하는 키워드 입니다.
정적 메서드는 클래스의 prototype에 연결되지 않고 클래스에 직접 연결됩니다. static 메서드의 가장 큰 특징은 인스턴스를 생성하지 않고 호출할 수 있다는 것과 인스턴스를 생성하면 실행할 수 없다는 것 입니다.

1
2
3
4
5
6
7
8
9
10
class Sports {
static getGround() {
return '상암구장'
}
};
let test = Sports.getGround();
let test2 = new Sports();
console.log(test);
// 에러 발생
console.log(test2.getGround());


16.13 class 호이스팅


class 메서드는 let, const와 마찬가지로 호이스팅이 일어나지 않습니다.
(class 선언문, 표현식 모두 동일)


16.14 computed name


class 내 메서드의 이름을 조합(computed name)하여 작성할 수 있습니다.

1
2
3
4
5
6
7
8
let type = "Type";

class Sports {
static ['get' + type](kind) {
return kind ? '스포츠' : '음악';
}
}
console.log( Sports['get' + type](1) );


16.15 this


정적(static) 메서드에서 this는 클래스 오브젝트를 참조합니다. this.constructor.name()의 형태로 호출할 수 있습니다.
(즉 정적 메서드를 호출하기 위해서는 this.name()의 형태가 아닌 this.constructor.name()의 형태로 사용해야 한다는 것)

this를 사용한 정적(static) 메서드의 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Sports {
constructor(){
console.log(Sports.getGround());
console.log(this.constructor.getGround());
/*
// 에러 발생
console.log(this.getGround());
*/
}
static getGround() {
return '상암구장'
}
}

let obj = new Sports();

this는 new Sports()로 생성한 인스턴스를 참조하는 데 정적(static) 메서드는 인스턴스에 존재하지 않으므로 this.getGround()의 형태로 호출할 수 없습니다.
하지만 constructor는 Sports 클래스를 참조하며 인스턴스의 proto에 constructor가 첨부 되어 있으므로 this.constructor.getGround()의 형태로 정적 메서드를 호출할 수 있습니다.


16.16 제너레이터


클래스 안에 제너레이터 함수를 작성할 수 있습니다. 클래스 안에 작성한 제너레이터 함수는 prototype에 연결되며 그러므로 정적(static) 메서드로 호출할 수 없고 인스턴스를 생성하여 호출해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Member {
// 제너레이터 함수(메서드)이므로 앞에 *를 붙여서 작성
*gen(){
yield 10;
yield 20;
}
};

let obj = new Member();
let genObj = obj.gen();

/*
console.log(genObj.next());
console.log(genObj.next());
*/

// 제네레이터는 이터레이터이므로 이렇게도 출력가능
for (let value of genObj) {
console.log(value);
}


16.17 new.target


new.target은 메타(meta) 속성(property)으로 생성자 함수와 클래스에서 constructor를 참조합니다. new연산자로 인스턴스를 생성하지 않으면 new.target의 값은 undefined 입니다.

1
2
3
4
5
6
let Sports = function(){
console.log(new.target);
};

Sports();
new Sports();

생성자 함수로 호출하지 않는 경우는 undefined가 출력되며 생성자 함수로 호출한 new.target의 값은 함수 전체를 출력합니다. 이는 constructor를 작성하지 않았으므로 기본 constructor(함수 전체)를 출력한 것입니다.


16.20 기타


접근 제한자

private, public, protected와 같은 키워드를 사용하는 접근 제한자를 지원하지 않습니다.


정리


class 오브젝트

  • class는 기존 방식에 비해 객체지향 개발에 더 적합하게 만들어진 메서드
  • 단 새롭게 생긴 기능이라기보다는 사용의 편의성 및 코드의 가독성을 향상 시킨 문법(syntactic sugar)
  • 메서드 사이에 ,(컴마)는 선택사항
  • getter와 setter 키워드를 제공
  • extends 키워드를 사용한 상속 기능 제공
  • super키워드를 이용 슈퍼 클래스의 프로퍼티를 참조하거나 constructor를 호출할 수 있다
  • static 키워드로 메서드를 생성하면 인스턴스 환경이 아닌 경우에도 호출할 수 있지만 반대로 인스턴스 환경에서는 호출할 수 없다
  • class는 호이스팅이 일어나지 않는다
  • class 내 메서드의 이름은 조합하여 작성할 수 있다.
  • private, public, protected와 같은 키워드를 사용하는 접근 제한자를 지원하지 않는다.