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= “무지개” >