앞에서 다룬 객체 지향 프로그래밍을 위해 자바스크립트에서 사용되는 개념이 바로 클래스(class)와 인스턴스(instance)이다. 클래스는 어떠한 대상의 설계도, 혹은 청사진(blue print) 이라고 할 수 있으며, 인스턴스는 이러한 설계도를 바탕으로 실제 제작된 객체라고 볼 수 있다. 이러한 클래스와 인스턴스를 자바스크립트 내에서 작성하는 방법은 다음과 같다.
function Human(name, age) {
}
// ES6 이후 추가된 문법
class Human {
constructor(name, age) {
}
}
let lee = new Human('lee', 30)
ES6에서 class를 이용하는 아래의 방법이 추가되기 전엔 일반 함수의 선언과 같은 방법으로 class를 설정했다. 그러나 최근에는 아래의 방법을 많이 사용한다. class는 보통 Human과 같이 대문자, 그리고 일반명사로 만든다. constructor는 생성자 함수로, 인스턴스가 만들어질 때 실행된다. 참고로 이러한 생성자 함수는 return 값을 만들지 않는다.
아래는 설정한 class를 기반으로 인스턴스 객체를 만들었다. 인스턴스 생성 시에는 new 키워드를 사용하며, 생성과 동시에 생성자 함수가 실행되면서 새로운 인스턴스가 할당된다. 이러한 인스턴스는 클래스의 속성과 메소드를 동일하게 가진다.
여기서 속성과 메소드의 차이점을 알아보자. 속성(property)은 이름, 나이와 같은 말 그대로 대상이 가진 속성을 의미한다. 이와 달리 메소드(method)는 말하기, 걷기와 같은 대상의 동작을 나타낸다. 그렇기에 메소드는 walk()와 같이 실행할 수 있는 함수 형태인 것이다.
다음으로 클래스의 속성과 메소드를 정의해보자.
class Human {
constructor(name, age) {
this.name = 'kim';
this.age = 31;
walk() {
}
}
}
여기서 this란 함수 실행 시 해당 scope마다 생성되는 고유한 실행 컨텍스트로, new 키워드로 인스턴스를 생성했을 경우 해당 인스턴스를 가리킨다. 이렇게 정의된 속성과 메소드를 이용해 인스턴스를 생성하면 다음과 같다.
let kim = new Human('kim', 30) {
kim.age // 30;
kim.walk();
}
사실 이 과정은 배열과 형태가 매우 유사하다. 그 이유는 배열이 바로 Array라는 클래스의 인스턴스이기 때문이다.
let arr = new Array('kim', 'lee', 'jeong') {
arr.length; // 3
arr.push('park'); // ['kim', 'lee', 'jeong', 'park']
}
프로토타입은 다른 객체를 가리키는 내부 링크로, 모든 자바스크립트 객체가 가지고 있다. 이러한 프로토타입으로 직접 객체를 연결할 수 있는데, 이를 '프로토타입 체인'이라고 한다. 이는 객체 지향 프로그래밍 특징 중 '상속'과 관련되어 있으며, 매번 새롭게 속성과 메소드를 정의할 필요 없이 작성된 메소드를 그대로 사용할 수 있게 해준다.
MDN을 참고한 다음 예제를 보면 프로토타입에 대해서 쉽게 이해할 수 있다.
function Person(first, last, age, gender) {
this.first = first;
this.last = last;
this.age = age;
this.gender = gender;
}
let lee = new Person('john', 'lee', 30, 'male', ['music', 'design']);
다음과 같이 클래스와 인스턴스를 정의한 뒤 콘솔창에 인스턴스 'lee.'를 입력하면 다음과 같은 자동완성 팝업이 뜨는 것을 볼 수 있다.
팝업을 보면 위에서 lee의 프로토타입 객체인 Person()에 정의된 name, age, gender등과 함께 toString이나 valueOf와 같은 따로 설정하지 않은 메소드가 보이는 것을 확인할 수 있다. 이러한 메소드들은 Person()의 프로토타입 객체 Object에서 상속받은 것으로, 프로토타입 체이닝의 결과물이다.
다음과 같은 코드는 Person이라는 클래스의 프로토타입을 보여준다.
내용을 살펴보면 constructor는 우리가 클래스 생성과 함께 정의한 속성이라는 것을 알 수 있다, 그렇다면 __proto__는 과연 어떤 역할을 하는 것일까? 이것이 바로 상위 객체 Object의 메소드를 사용할 수 있도록 연결해주는 연결고리이다.
Object 객체 또한 .prototype을 이용해서 확인해 보자.
위와 같이 Object가 가지는 기본적인 기능들을 볼 수 있다. 이렇게 프로토타입 체이닝 덕분에 우리는 그동안 Object의 toString() 메소드를 이용할 수 있었던 것이다.