Class 오브젝트 -ECMAScript
Class(클래스)를 완전하게 이해하려면 객체지항 프로그래밍(OOP:Object Oriented Programming)에 대한 이해가 필요합니다.
OOP만 다루는 책이 있을 정도로 범위가 넓고 깊으므로
OOP는 나중에 자세히 다루고 ES6기준으로 살펴봅니다.
- Class 오브젝트
- Class 선언문
- Class 표현식
- Class 특징
- strict 모드에서 실행
- 클래스에 메서드 작성 방법
- prototype에 프로퍼티 연결
- prototype에 프로퍼티 추가
- constructor
- constructor 반환 값 변경
- getter, setter
- 상속
- extends 키워드
- super 키워드
- 메서드 오버라이딩
- 빌트인 오브젝트 상속
- Object에서 super 사용
- static 키워드
- Class 호이스팅
- computed name
- this
- 제너레이터
- new.target
- name 프로퍼티
- 오브젝트 상속
Class 선언문
Class name {}
Class name extends super_name {}
엔진이 function 키워드를 만나면 Function 오브젝트를 생성하듯이class 키워드를 만나면 Class 오브젝트를 생성합니다.Class 오브젝트는 Function 오브젝트, String 오브젝트와 같이 하나의 오트젝트 타입입니다.
name에 클래스 이름을 작성합니다.name다음의extends는 키워드로super_name(슈퍼 클래스)를 상속받을 때 사용합니다.
이 형태를 클래스 선언문 이라고 합니다.
class는 클래스를 선언하는 키워드이고
엔진이 class 키워드로 생성한 오브젝트는 Class 오브젝트 입니다.
문맥에 따라 Class 오브젝트를 class라고 말하기도 합니다.
1 | class Member { |
class키워드를 작성하고 이어서 클래스 이름을 작성합니다. 그리고 블록{}을 작성하고 블록에 클래스 코드를 작성합니다. 엔진이class키워드를 만나면 클래스(Class) 오브젝트를 생성합니다.
new연산자로Member()를 호출하면 인스턴스를 생성하여 반환합니다.function name(){}는new연산자로 인스턴스를 생성하지 않고name()형태로 호출할 수 있지만,
클래스는new연산자로 인스턴스 생성없이name()형태로 호출할 수 없으며TypeError가 발생합니다.
obj는 인스턴스이고getName()은class Member{}안에 작성한 메서드 입니다.
이와 같이new연산자로 생성한 인스턴스를 사용하여 클래스에 작성한 메서드를 호출할 수 있습니다. 실행 결과 “이름”이 출력됩니다.
참고
new 연산자로 인스턴스를 생성하는 함수는 함수 이름의 첫 문자를 대문자로 작성합니다.
클래스 또한 new 연산자로 인스턴스를 생성하므로 클래스 이름의 첫 문자를 대문자로 사용합니다.
이는 스펙에 정의된 것은 아니며 자바스크립트 개발자들 사이의 관례입니다.
개발자가 코드의 대문자를 보고 인스턴스를 생성한다는 것을 알 수 있으므로 지키는 것이 좋습니다. 빌트인 Number 오브젝트, String 오브젝트도 이와 같은 맥락입니다.
같은 클래스를 두 번 선언하려고 시도할 때
클래스 선언문으로 같은 클래스를 두 번 선언하면 오류가 발생합니다.class Foo {};
class Foo {}; // Uncaught SyntaxError: Identifier ‘Foo’ has already been declared
이전에 표현식으로 정의한 경우에도 오류가 발생합니다.
var Foo = class {};
class Foo {}; // Uncaught TypeError: Identifier ‘Foo’ has already been declared
Class 표현식
클래스 표현식으로 클래스를 선언합니다.
let name = class {}
let name = class inner_name {}
let name = class extends super_name {}
let name = class inner_name extends super_name {}
할당 연산자 (=) 왼쪽에 클래스 이름을 작성하고 오른쪽에 class 키워드를 작성하고 블록{}안에 클래스 코드를 작성합니다. 이를 클래스 표현식이라고 합니다.
Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있습니다. 이름을 가진 class 표현식의 이름은 클래스의 body에 대해 local scope에 한하여 유효합니다.
4번째 구문 class 와 extends 키워드 사이의 inner_name은 클래스 안에서 자신을 호출할 때 사용합니다. function 키워드 함수에서도 inner_name을 작성할 수 있지만 사용하지 않듯 클래스도 사용하지 않습니다.
1 | let Member = class { |
엔진이 클래스 키워드를 만나면 클래스 오브젝트를 생성하여 Member 변수에 할당합니다.
클래스 특징
Class는 사실 함수입니다. 함수를 함수 표현식과 함수 선언으로 정의할 수 있듯이 class 문법도 class 표현식과 class 선언 두 가지 방법을 제공합니다.
strict 모드에서 실행
“use strict”을 선언하지 않아도 클래스의 코드는 strict 모드에서 실행됩니다.
클래스에 메서드 작성 방법
1 | class Member{ |
setName(){ }과 같이function키워드와콜론(;)을 작성하지 않고 메서드 이름만 작성합니다.
setName()과getName()메서드 사이에 콤마를 작성하지 않습니다.
- 클래스의
typeof는function입니다. 이는class가function구조라는 것을 의미합니다.
- function name(){ }은 글로벌 오브젝트(window Object)에 설정되지만 class name(){ }은 글로벌오브젝트에 설정되지 않습니다.
따라서window.Member로 클래스에 접근하면undefined가 반환됩니다.
Class 오브젝트의 프로퍼티는 for()문 등으로 열거할 수 없습니다.
prototype에 프로퍼티 연결
1 | class Member{ |
prototype에 메서드를 연결하여 prototype.setName과 같이 작성하지 않고setName을 작성합니다. 자바스크립트는 기본적으로 prototype에 메서드를 연결하는 구조이므로 클래스 안에 작성된 메서드를 엔진이 자동으로 prototype에 연결합니다.
즉. 엔진이 Member.prototype.setName 형태로 연결해줍니다.
이는 중요한 의미를 갖습니다. 자바스크립트의 기본 아키텍처(구성 방식 혹은 컴퓨터 소프트웨어의 호환성)를 유지하면서 객체지향 언어의 특징을 반영하려는 접근입니다.
prototype에 프로퍼티 추가
Member.prototype.getTitle = function( ){ };
클래스 밖에서 Member 클래스에 메서드를 추가하려면 위와 같이Member.prototype에 메서드를 연결하여 작성합니다.Member 클래스를 선언할 때는 클래스 블록{}안에 작성하겠지만,
인스턴스를 생성한 후 상황에 따라 추가할 때 이 형태로 작성합니다.
참고
이렇게 메서드를 추가하면 이미 생성된 인스턴스에서 추가한 메서드를 공유할 수 있도록 엔진이 처리하게 되므로 부하 혹은 자원낭비가 됩니다.
좋은 사용 예시로는 사용자의 행동이나 서버 데이터에 따라 메서드를 따로 추가할 수 있는 점이 있습니다.(역동성이 높다.)
constructor
constructor메소드는class인스턴스를 생성하고 생성된 인스턴스를 초기화하는 역활을 합니다.
“constructor”라는 이름을 가진 메소드는 클래스 안에 한 개만 존재할 수 있습니다. 만약 클래스에 여러 개의constructor메소드가 존재하면SyntaxError가 발생할 것입니다.
constructor는 부모 클래스의constructor를 호출하기 위해super키워드를 사용할 수 있습니다.
참고
클래스에
constructor를 작성하지 않으면prototype의constructor가 호출됩니다.
이를 디폴트constructor라고 하고constructor가 없으면 인스턴스를 생성할 수 없습니다.ES5에서 클래스 오브젝트를 실행하면 엔진이 디폴트constructor를 호출해서 이를 활용할 수 없었습니다.ES6에서는 개발자가constructor를 정의할 수 있어서Class오브젝트 뿐 아니라Proxy오브젝트,Reflect오브젝트에서 활용할 수 있습니다.
1 | 1. class Member { |
클래스 안에
constructor를 작성했으며constructor안에 생성한 인스턴스에 초기값을 설정하는 코드를 작성했습니다.constructor안의this는 생성하는 인스턴스를 참조합니다.new Member(“스포츠”)를 실행하면Member클래스에 작성한constructor가 자동으로 호출되며 파라미터 값으로“스포츠”를 넘겨 줍니다.new연산자는constructor를 호출하면서 파라미터를 넘겨주는 역활- 호출된
constructor가 인스턴스를 생성하여 반환하면new연산자가 받아new를 실행한 곳으로 반환합니다.
클래스 와 인스턴스 생성 과정 이해를 위한 개념적인 순서
new Member(“스포츠”)를 실행합니다.
new 연산자가 constructor를 호출하면서 파라미터 값을 넘겨줍니다.
constructor의 블록 코드를 실행하기 전에 빈 Object 오브젝트를생성합니다.
이 것이 인스턴스입니다. 인스턴스가 생성되면 빈 오브젝트를 채웁니다.
인스턴스 구성에 필요한 프로퍼티를 Object 오브젝트에 설정합니다.
constructor 블록에 있는 코드를 실행합니다.
인스턴스를 먼저 생성하므로 constructor에서 this로 인스턴스를 참조할수 있습니다.
생성된 인스턴스를 반환합니다.
console.log(memberObj.getName());를 호출하면 다음 코드가 실행됩니다.
getName() {
return this.name;
}
constructor에서 this에 “스포츠”를 설정했으므로 “스포츠”가 반환됩니다.
아래는 생성된 memberObj 인스턴스 구조입니다.
constructor에서 파라미터로 받은 “스포츠”를 this.name에 할당했으며,
이때 this가 생성하는 인스턴스를 참조하므로 인스턴스 프로퍼티로 설정됩니다.
- __proto__는 인스턴스를 생성하면 엔진이 자동으로 첨부합니다.
- __proto__에
Member.prototype에 연결된 프로퍼티를 첨부하므로getName도 첨부됩니다.
- __proto__에
Object오브젝트의prototype에 연결된 프로퍼티가 첨부됩니다.
constructor 반환 값 변경
constructor에는 일반적으로 return 문을 작성하지 않으며, 생성한 인스턴스를 반환합니다.return을 사용하면 인스턴스 이외의 값을 반환할 수 있습니다.
1 | class Member { |
constructor(){ }안에 return 1;을 작성하였습니다.일반적으로 숫자 값을 반환하지 않습니다, 엔진 처리 방법 예시를 위해 작성되었습니다.
constructor에서 Number 또는 String 값을 반환하면 이를 무시하고 생성한 인스턴스를 반환합니다.
console.log(memberObj.getName();)을 호출하면 constructor에서 1을 반환하여memberObj에 1이 설정됩니다. 이후에 getName()을 호출하면 인스턴스 1에 getName()이 존재하지 않으므로 에러가 발생합니다. 하지만 Member 인스턴스를 반환하므로 getName()이 호출됩니다.
1 | class Member { |
constructor에서 Object오브젝트를 return하면 이를 반환합니다. 즉, {name: “홍길동”}이 반환됩니다.
memberObj에 반환된{name: “홍길동”}이 설정되어 있으므로memberObj.name값이 홍길동 으로 출력됩니다.
- 클래스에
getName메서드를 작성했지만, 인스턴스를 반환하지 않고{name: “홍길동”}을 반환하므로MemberObj에getName이 존재하지 않습니다.undefined가 출력됩니다.
getter, setter
클래스에도 getter와 setter를 선언할 수 있습니다.
메서드 이름 앞에 “get”을 작성하면 getter, “set”을 작성하면 setter가 됩니다.
1 | class Member { |
get getName()과 같이 메서드 이름 앞에 get을 작성하여 getter로 선언합니다.
getName이 getter이므로 메서드로 호출됩니다.
1 | class Member { |
memberObj.setName = “이름”과 같이 setter로 선언된 메서드 이름에 값을 할당하면setName이 메서드로 호출됩니다. 이때 할당하는 값인 “이름”을 파라미터 값으로 넘겨줍니다.
상속
객체지향 프로그래밍에서 상속은 주요한 기능 중 하나입니다.
클래스를 상속받으면 상속받은 클래스의 메서드와 프로퍼티를 사용할 수 있습니다.
참고
상속해 주는 클래스를 일반적으로 부모 클래스라고 부릅니다만 앞으로는
“슈퍼 클래스”라고 표기해줍시다.
슈퍼 클래스라고 표기하는 이유는 ES6에서 super키워드가 있으며 슈퍼 클래스를 지칭하므로 직관적이기 때문입니다.
상속받는 클래스도 일반적으로 자식 클래스라고 부릅니다만, 슈퍼 클래스와 운을 맞추기 위해“서브 클레스”로 표기해줍시다.
자바스크립트 (객채지향 프로그래밍)의 상속 형태는 상속이 아니다?
일반적으로 상속이라고 하면 부모의 재산을 자식에게 물려주면 자식이 부모의 능력을 고스란히 물려받습니다.
자바스크립트에서는 객체도 생성자도 모두 프로토타입에 접근할 수 있고 심지어 변경까지할 수 있습니다.
이는 상속받을 것들을 자기 마음대로 선택,변경할 수 있게 되고 다른 언어의 상속과는 다른 형태를 갖습니다.
상속이라고 부르지만, 프로토타입의 (자원)공유로 이해하는 것이 적절해 보입니다.
ES5에서의 상속 구현 형태
1 | 1. function Sports(member){ |
Sports첫 문자를 대문자로 작성한 것은new연산자로 인스턴스를 생성하려는 의도입니다.new Sports()를 실행하면Sports()가 호출되고, 다시 디폴트constructor를 호출합니다. 그래서Sports()를 생성자(constructor)함수라고 부릅니다.
this.member에서this가 생성하는 인스턴스를 참조하므로member는 인스턴스 프로퍼티가 됩니다.Sports(생성자 함수)함수에서this.member = member형태가 인스턴스에 초기값으로 설정됩니다.이렇게 설정된 값은 생성된 모든 인스턴스에서 공유하지 않고 인스턴스마다 값을 각각 유지합니다. 이것이 인스턴스를 만드는 목적 중의 하나입니다.
- 생성자 함수가 있으면
Sports.prototype.setItem과 같이prototype에 메서드를 연결한 코드가 작성되어 있습니다. 이를 작성하지 않으면 생성자 함수가 아닌 일반 함수 입니다. 이 형태가 ES5에서 인스턴스를 구현하는 기본 형태입니다.
Soccer()가 호출되면Sports()를 호출합니다.Soccer의 첫 문자가 대문자이므로 인스턴스를 사용한다는 것을 알 수 있습니다. 그런데new Sports()가 아닌Sports.call()형태로 함수를 호출한 것은, 바로 다음 코드에서Sports.prototype을 사용하여 인스턴스를 생성하므로 인스턴스에 초기값만 성정하면 되기 때문입니다.
Object.create()의 두 번째 파라미터setGround를 첫 번째 파라미터인Soccer.prototype에 첨부합니다. 그리고Sports.prototype에 연결된 메서드를Soccer.prototype.__proto__에 첨부합니다.
이렇게 연결된 후에new Soccer()로 인스턴스를 생성하면Soccer.prototype과Sports.prototype에 연결된 메서드를 인스턴스 메서드로 호출할 수 있습니다.ES5에서는 이와 같은 방법으로 상속을 구현합니다.
Soccer.prototype에constructor가 연결되어 있는데, 앞 코드에서Soccer.prototype에 프로퍼티를 연결하므로constructor가 지워집니다.Soccer를 설정하지 않아도 인스턴스가 생성되지만,constructor에서Soccer전체를 참조하는 것이 정상입니다.
new연산자로Soccer()생성자 함수를 호출하여 인스턴스를 생성합니다.Sports.prototype에 연결된 메서드는 인스턴스에 포함되지만,Sports()생성자 함수는 포함되지 않으므로Sports.call(this.member);코드를 수행하여 인스턴스에 초기값을 설정합니다.this는 생성한 인스턴스를 참조하게 됩니다.
setItem()은 상속받은Sports.prototype에 연결된 메서드 입니다.
상속을 받으면 인스턴스에서 직접 상속받은 메서드를 호출할 수 있습니다.
- 생성자 함수를 모두 호출하여 인스턴스에 초기값을 설정했으므로 인스턴스 프로퍼티로 프로퍼티 값을 구할 수 있습니다.
ES5에서는 이와 같이prototype에 연결해야 하며 직관적이지 않은 점도 있습니다.
extends 키워드
ES6에서는 extends 키워드로 상속을 구현합니다.
class subClass extends superClass { }
subClass
상속 받는 자식 클래스(서브 클래스).superClass
상속 해주는 부모 클래스(슈퍼 클래스).new subClass()로 인스턴스를 생성하면 인스턴스에서subClass클래스와super클래스의 메서드를 호출할 수 있습니다.
1 | 1. class Sports { |
Sports클래스를 상속받으므로Sports클래스는슈퍼 클래스입니다.new Soccer()로 인스턴스를 생성하면constructor가 호출되며,this가 생성하는 인스턴스를 참조하므로 파라미터로 받은 값을 인스턴스의member프로퍼티에 설정할 수 있습니다.
extends키워드 기준으로 왼쪽의Soccer클래스가서브클래스오른쪽Sports클래스가슈퍼클래스입니다. 즉,Soccer클래스에서Sports클래스를 상속받습니다.this.ground에서this는 생성한 인스턴스를 참조합니다.- 이 시점의 Soccer 클래스 구조입니다.
Soccer.prototype에setGround가 연결되어 있으며Soccer.prototype.__proto__에 Sports.prototype에 연결되어 있는
getMember가 첨부되어 있습니다.Soccer.__proto__에 Sports.prototype에 연결된 프로퍼티도 첨부되어 있습니다.
- 이와 같이
extends키워드는 서브클래스의prototype에 __proto__를 만들고 여기에 슈퍼클래스의prototype에 연결된 프로퍼티를 연결합니다.슈퍼클래스의 prototype에 연결된 메서드를 복사하는 것이 아니라 공유합니다.new Soccer()로 인스턴스를 생성할 때Soccer.prototype에 연결된 프로퍼티를 사용하므로 서브클래스와 슈퍼클래스의 메서드가 인스턴스에 포함됩니다.
new Soccer(11)을 실행하면 다음의 순서와 방법으로 실행합니다.
Soccer클래스의constructor가 호출됩니다.Soccer클래스에는constructor를 작성하지 않았습니다.- 슈퍼 클래스의
constructor가 호출되면서 11을 파라미터 값으로 넘겨줍니다. - 슈퍼 클래스의
constructor에서 this는 현재의 인스턴스를 참조하므로 인스턴스의member프로퍼티에 파라미터로 받은 11을 설정한 후 돌아오게 되며, - 생성한 인스턴스를
obj에 할당합니다.
다음은 obj의 인스턴스 구조입니다.
인스턴스를 생성하는 주체는 서브 클래스입니다.
new Sports()가 아닌new Soccer()로 인스턴스를 생성합니다.슈퍼 클래스의
constructor에서this.member에 설정한 값이 인스턴스 프로퍼티로 설정됩니다.__proto__에 서브 클래스의 prototype에 연결된 메서드가 첨부되었으며
__proto__에 슈퍼 클래스의 prototype에 연결된 메서드가 첨부됩니다.
메서드를 호출할 때 __proto__를 작성하지 않아도 되므로
setGround()와getMember()를 인스턴스에서 직접 호출할 수 있습니다.
obj.getMember()를 호출하면 우선 obj.__proto__에서 메서드를 찾습니다. 존재하지 않으면 obj.__proto__.__proto__에서 찾습니다. 메서드가 존재하면 호출됩니다.
이것이 자바스크립트의 상속 메커니즘 입니다.
super 키워드
서브 클래스와 슈퍼 클래스에 같은 이름의 메서드가 존재하면 슈퍼 클래스의 메서드는 호출되지 않습니다.
이때 super 키워드를 사용하여 슈퍼 클래스의 메서드(혹은 함수)를 호출할 수 있습니다.
서브 클래스 constructor에 super()를 작성하면 슈퍼 클래스의 constructor가 호출됩니다.super.name()과 같이 super 키워드에 이어서 호출하려는 메서드 이름을 작성합니다.
메서드 오버라이딩
서브 클래스와 슈퍼 클래스에 같은 이름의 메서드가 있을 때 서브 클래스의 메서드가 호출 되는 것을
메서드 오버라이딩(Overriding)이라고 합니다. 메서드 오버라이딩은 의도적인 접근 방식입니다.
서브 클래스와 슈퍼 클래스의 같은 이름의 메서드가 같은 목적을 가진 것을 나타내면서
서브 클래스의 목적에 맞도록 보완할 때 사용합니다.(슈퍼 클래스의 메서드 기능을 사용하면서 서브 클래스에서 기능을 추가,변경할 때 사용합니다.)
슈퍼 클래스와 서브 클래스의 메서드 기능/목적이 다른 경우에는 같은 이름을 사용하지 않습니다.
1 | class Sports { |
Sports 클래스는 슈퍼 클래스 입니다. 상속을 해주지만 단독으로 인스턴스를 생성할 수도 있습니다.
Class Sports{}에는 그라운드(setground)가 필요합니다.Sports의 종목이 여러개라면 종목마다 그라운드(setGround)형태도 달라져야 할 수 있습니다.
그런 의미에서setGround()메서드는 의미 없어 보일 수 있습니다.
- 하지만
Sports클래스를 상속받는 서브 클래스에서setGround를 오버라이딩 하면 일관성 있게
그라운드를 정의할 수 있습니다. 메서드 이름을 유지하면서 서브클래스 마다
적합한 그라운드(setGround)를 설정해 주는 것입니다.
이를 객체 지향에서 추상화(Abstraction)라고 합니다.
Soccer클래스에서Sports클래스를 상속받습니다.Sports클래스에setGround()가 있으며Soccer클래스에도 있으므로 메서드가 오버라이딩 됩니다.new Soccer()로 인스턴스를 생성한 후setGround()를 호출하게 되면 서브클래스(Soccer)의
메서드가 호출됩니다. 이때super.setGround()는 슈퍼클래스(Sports)의setGround()를 호출합니다.
super.setGround()를 호출하면서 파라미터 값은 정해주지 않았습니다.
슈퍼클래스(Soccer)의setGround()에서this.ground에undefined가 설정됩니다.super.setGround()를 실행한 후 돌아오면 바로 다음 줄의this.ground에서 파라미터로 받은 값을 설정합니다.
new Soccer()로 인스턴스를 생성하고setGround()를 호출하면 인스턴스의ground프로퍼티에 파라미터 값이 설정됩니다.ground가 인스턴스 프로퍼티이므로obj.ground로 값을 출력할 수 있습니다.
1 | class Sports { |
new Soccer(123)을 실행하면Soccer클래스의constructor가 호출됩니다.
constructor첫째 줄의super(member)를 실행하면 슈퍼 클래스의constructor를
호출 하면서 파라미터로 받은 값을 넘겨줍니다. 슈퍼 클래스의constructor를
호출하려면 서브클래스constructor의 첫째 줄에super()를 작성해야 합니다.
super() 앞에 변수를 선언하거나 변수에 값을 할당하는 코드는 작성해도 되지만,
this 키워드는 super() 앞에 사용할 수 없습니다.
super()로 인해constructor가 호출되면this로 인스턴스를 참조할 수 있습니다.
따라서 파라미터로 받은member값을 인스턴스의member프로퍼티에 할당할 수 있습니다.
빌트인 오브젝트 상속
extends 키워드로 Array 오브젝트 등의 빌트인 오브젝트를 상속받을 수 있습니다.
상속을 받지 않고 서브 클래스에서 빌트인 오브젝트의 메서드를 호출해도 되지만 상속 받는 것과 차이가 있습니다.
서브 클래스에서 빌트인 오브젝트를 상속받으면 빌트인 오브젝트의 메서드를 마치 서브 클래스에서 선언한 것처럼 사용할 수 있게 됩니다.
1 | 2. class ExtendArray extends Array { |
- 클래스
ExtendArray에 빌트인 오브젝트인Array오브젝트를 상속받았습니다.new연산자로 인스턴스를 생성하면 인스턴스는Array오브젝트의 특징을 갖게됩니다.
따라서 인스턴스에서push()와 같은Array메서드를 직접 호출할 수 있습니다.“[].push()”형태가 아닌“인스턴스.push()”형태로 호출할 수 있습니다.
이 형태의 차이에 상속 받는 목적이 담겨있다고 할 수 있습니다.
new ExtendArray()를 실행하면 아래에 작성한constructor가 호출됩니다.constructor(){
super();
}super()가 슈퍼 클래스의constructor를 호출하므로Array오브젝트constructor가 호출됩니다.
obj인스턴스에push()메서드가 상속되어 있으므로obj.push(10,20)형태로 호출할 수 있습니다.obj.push(10, 20)과[].push(10, 20)에서obj인스턴스가Array 리터럴[]에 해당됩니다.
따라서obj인스턴스에 10 과 20를 설정하는 것은Array인스턴스에 설정하는 것과 같습니다.
이것이 빌트인 오브젝트를 상속받는 목적 중의 하나입니다.
obj.getTotal()을 호출하면for-of문으로 [10, 20]을 합산하여 반환합니다.값을 합산해주는 빌트인 함수도 있지만 for(var value of this)문에서
this를 다루기 위해 의도적으로 for-of 문을 사용했습니다.this가obj인스턴스를 참조합니다.obj인스턴스는 [10, 20]값이 설정되어 있으며Array오브젝트를 상속받은 상태 이므로length프로퍼티를 갖고있습니다.
즉, 이터레이션을 수행할 수 있는 조건을 충족합니다.for(var value of [10, 20]){
total += value;
} return total
위 헝태가 되어 엘리먼트(10, 20)를 하나씩 읽어가면서 for-of 문을 반복합니다.
Object에서 super 사용
두 개의 Object 오브젝트가 연결된 구조에서 super.name() 형태로 슈퍼 오브젝트의 메서드를 호출할 수 있습니다.
1 | let Sports = { |
Object.setPrototypeOf()을 실행하면 Soccer.__proto__에Sports오브젝트의 프로퍼티가 첨부됩니다.Object오브젝트가 대상이므로 생성자 함수가 없지만,__proto__에 프로퍼티가 첨부되는 것이 상속 구조(형태)입니다.
Soccer.getTitle();이 호출되면, 첫째 줄에서super.getTitle()을 호출합니다.
상속을 하면 __proto__로 계층을 만들고,__proto__에 상속받을 오브젝트의 프로퍼티를 첨부하므로,super는 한 단계 아래의 __proto__를 참조하는 것과 같습니다.
현재 Soccer.__proto__에 Sports 오브젝트의 getTitle()이 첨부되어 있습니다.
따라서 super.getTitle()을 호출하면, super가 Soccer.__proto__를 참조하므로 Sports오브젝트의getTitle()`이 호출됩니다.
이와 같이 Object.setPrototypeOf()으로 __proto__구조를 만들고 상속받을 오브젝트의 프로퍼티를 첨부하면, super키워드로 상속 계층 구조에 있는 메서드를 호출할 수 있습니다.
static 키워드
클래스에 static(정적) 메서드를 정의합니다.
static methodName() { … }
prototype에 연결된 메서드는 인스턴스를 생성하여 호출 하지만,static 메서드는 인스턴스를 생성하지않고 클래스에 직접 연결하여 호출합니다.
static 메서드는 prototype에 연결되지 않으므로 인스턴스에서 호출할 수 없습니다.
클래스 이름을 지정하여 static 메서드를 호출해야 합니다.
중요 포인트
엔진이
class키워드를 만나면 클래스 안에static메서드 작성 여부를 체크합니다.static메서드가 존재한다면 이를Function오브젝트로 생성합니다.
(메서드를 호출하기 위해서는 메서드가Function오브젝트여야 하고,
이렇게 생성함으로써 클래스 아래의static메서드를 호출할 수 있게 합니다.)
1 | function(){ // Function 오브젝트입니다. |
function 안에 function은 Function 오브젝트로 생성하지 않고, function이 호출되어 안에 있는 function으로 들어갔을 때 Function 오브젝트를 생성합니다.
따라서 function을 호출하지 않으면 안에 있는 function은 아무것도 아닙니다. 그저 작성돼있는 함수 입니다. 이 점이 static 키워드를 사용한 메서드와 차이점 입니다.
1 | class Sports { |
getGround(){} 앞에 static을 작성하여 static메서드로 선언했습니다.
Sports는 클래스 이름이고getGround()는static메서드 입니다.
이와 같이 앞에 클래스 이름을 작성하고 이어서static메서드를 작성하여 호출합니다.
Class 호이스팅
클래스는 호이스팅(Hoisting)이 되지 않습니다.
1 | // Class |
호이스팅 된다면 Member 클래스가 result 변수에 할당됩니다.
호이스팅 되지않으면 Member를 인식하지 못합니다. //Error
클래스는 호이스팅 되지않아
result에 할당할Member를 인식하지 못합니다.result에Member값을 넣으려면 클래스 문이 완전히 끝난 뒤에 작성해야합니다.function은 호이스팅되므로result2에Member2를 인식하여 할당할 수 있습니다.
computed name
클래스의 메서드 이름을 조합(computed name)하여 작성할 수 있습니다.
1 | let type = "Type"; |
변수 type 에 문자열 값 “Type”을 할당해 줬습니다.static 메서드 []안에 문자열 “get” 과 클래스 밖에 작성된 type 변수 값을
작성해줬습니다. static메서드의 이름이 getType이 됩니다.
이와 같이 static 메서드 []안에 조합할 이름을 작성합니다.
- 문자열
“get”과 변수인type을 조합하여 호출합니다.
파라미터 값이 1 (true)이므로 호출된getType()에서 “스포츠”를 반환합니다.
물론console.log(Sports.getType(1));형태로 호출할 수 도 있습니다.
this
static 메서드에서 this는 클래스 오브젝트를 참조합니다.constructor 안에서 this.constructor.name()형태로 static 메서드를 호출할 수 있습니다.
1 | class Sports { |
Sports.setGround(“상암구장”)를 실행하면 static메서드인 setGround()가 호출됩니다.this.ground에서 this가 Sports 클래스를 참조하므로 Sports 클래스에{ground: “상암구장”} 형태로 설정됩니다.
Sports.getGround()를 실행하면 getGround() 정적 메서드가 호출됩니다.this.Ground에서 this가 Sports 클래스를 참조하므로 setGround()에서Sports 클래스에 설정한 ground 프로퍼티 값을 구할 수 있습니다. //상암구장
1 | class Sports{ |
new Sports()를 실행하면 constructor가 호출됩니다.constructor 블록{}안에서 static메서드인 getGround를 호출하며, this.constructor 형태도 사용하고 있습니다.
Sports클래스에static메서드로getGround()를 작성했으므로Sports.getGround()형태로 호출할 수 있습니다.constructor블록{} 아래에getGround()가 작성되어 있습니다만 이미Function오브젝트로 생성된 상태이므로 호출이 됩니다.
constructor가Sports클래스를 참조하며 인스턴스의 __proto__에constructor가 첨부되어 있으므로this.constructor.getGround()형태로static메서드를 호출할 수 있습니다. 구조는 다음 사진과 같습니다.
- this.getGround()형태로는 호출할 수 없습니다. (//undefined)
this가new Sport()로 생성한 인스턴스를 참조하고,getGround는static메서드 이므로 인스턴스에 존재하지 않기 때문입니다.
제너레이터
클래스 안에 제너레이터 함수를 작성할 수 있습니다.
클래스 안에 작성한 제너레이터 함수는 prototype에 연결됩니다.
따라서 static 메서드로 호출할 수 없고 인스턴스를 생성하여 호출해야 합니다.
1 | class Member{ |
sports(); 와 같이 new 연산자를 사용하지 않고 호출하면 new.target값은 undefined 입니다.
new sports();로 호출하면 new.target은 constructor를 참조합니다.sports 함수에 constructor를 작성하지 않았으므로 디폴트 constructor가 호출됩니다.
디폴트 constructor = (function: Object() {native code})constructor가 sports 전체를 참조하므로 sports 함수의 코드가 출력됩니다.
name 프로퍼티
ES6는 클래스, 함수 오브젝트에 name 프로퍼티가 존재하며 이름이 설정됩니다.
1 | class Sports { |
new Sports()를 호출하면Sports클래스이므로 클래스name프로퍼티에“Sports”가
설정되어 있습니다.constructor에서new.target은constructor를 참조하므로Sports클래스의name프로퍼티 값“Sports”가 출력됩니다.
new Soccer()를 호출하면super()로 인해Sports의constructor가 호출됩니다.Sports의constructor에서new.target은super()로 호출한Soccer의constructor를 참조합니다. 따라서new.target.name값으로Sports가 아닌Soccer가 출력됩니다.
Image 오브젝트 상속
DOM(Document Object Model)에서 제공하는Image인터페이스,Audio인터페이스 등을 상속받을 수 있습니다.
빌트인Array오브젝트를 상속받으면Array오브젝트 특징을 갖듯이 상속받은 인터페이스 특징을 갖습니다.DOM의Image인터페이스는 웹 페이지에png파일과 같은 이미지 파일을 표현하기 위한 속성을 제공합니다.
인터페이스는 객체지향 용어로 이 자체를 그대로 사용할 수 없고 오브젝트로 변환하여 사용해야 합니다.DOM은 오브젝트를 제공하지 않고 인터페이스를 제공하는데,이는 자바스크립트뿐만 아니라 Java등의 다른 언어에서도 사용하기 때문입니다. 각 언어에서 DOM인터페이스를 언어에 맞게 변환하여 사용합니다.
인터페이스를 오브젝트로 변환하려면 extends 키워드를 사용합니다.
1 | 1. class ExtendsImage extends Image{ |
Image오브젝트를extends키워드로 상속받습니다.ExtendsImage클래스는Image특성을 갖게됩니다.
즉, < img > 엘리먼트의 속성을 직접 사용할 수 있습니다.
또한 this로 엘리먼트 속성에 접근할 수 있습니다.파라미터로 받은 < img > 속성 값을 this가 참조하는
imageObj인스턴스에 설정합니다.< body> 엘리먼트에 자식 요소로
imageObj를 첨부합니다.
body 엘리먼트에imageObj안에 있는img엘리먼트 속성도 첨부 횝니다.
웹페이지에 이미지가 표시되고 그 속성은 다음과 같습니다.
< img src = “file/rainbow.png”
alt= “나무와 집이 있고 그 위에 무지개가 있는 모습”
title= “무지개” >