Front-End

vsoghlv@naver.com

자바스크립트 프로토타입 체이닝

자바스크립트는 C 나 자바의 객체지향 언어와는 다른 프로토타입 기반의 객체지향을 지원한다. 자바 등의 객체지향 프로그래밍에서는 클래스를 정의 후 객체를 생성하지만 자바스크립트는 객체 리터럴이나 생성자 함수로 객체를 생성한다. 이때 생성된 객체의 부모객체가 프로토타입 객체이다. es6 에서 클래스 개념이 추가되긴 했지만 그래도 프로토타입 기반 언어라는 것은 바뀌지 않는다.

상속과 마찬가지로 자식 객체가 부모의 프로퍼티 접근이나 메서드를 상속받아 사용이 가능한데 함수 객체의 prototype 프로퍼티 와 객체의 숨은 프로퍼티인 [[Prototype]] 를 구분해야 한다.

자바스크립트의 모든 객체는 자신을 생성한 함수의 prototype 프로퍼티 가 가리키는 프로토타입 객체를 자신의 부모 객체로 설정하는 [[Prototype]] 링크로 연결한다.

//생성자 함수
function Person(name) {
  this.name = name;
}
// 객체생성
let foo = new Person("foo");

console.dir(Person);
//...prototype:
//constructor: ƒ Person(name) 
//__proto__: Object
console.dir(foo);
//...__proto__: //모든 객체에 있는 숨겨진 프로퍼티, 객체 자신의 프로토타입 객체를 가리킨다.
//constructor: ƒ Person(name)
//__proto__: Object

//Person 의 prototype 프로퍼티와 foo 의 prototype 링크가 같은 것을 가리키고 있다.

객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝

자바스크립트에서는 객체가 자신의 프로퍼티뿐만이 아닌 부모늬 프로토타입 객체의 프로퍼티에 접근 또한 가능하다.

let myObj = {
  name: "Sun",
  sayName: function () {
    console.log(this.name);
  },
};

myObj.sayName();//Sun
console.log(myObj.hasOwnProperty("name"));//true
myObj.sayAge();//Uncaught TypeError

위의 예제를 보면 sayAge 는 에러가 나는 반면 hasOwnProperty 는 오류가 나지 않는 것을 볼 수 있다.

이유는 간단하다. 프로토타입 체이닝이랑 자바스크립트에서 특정 객체의 프로퍼티, 메서드에 접근하려 할 때 해당 프로퍼티나 메서드가 없다면 [[Prototype]] 링크를 따라 프로토타입 객체의 프로퍼티를 차례대로 검색한다.

객체 리터럴로 생성한 객체는 Object() 내장 생성자 함수로 생성되는데 이 함수의 prototype 프로퍼티가 가리키는 객체는 Object.prototype 객체이다. 즉, myObj 는 결국 Object.prototype 으로 연결되는데 이때 hasOwnProprty 는 Object.prototype 가 가지고 있는 표준 api 이다. 따라서 myObj 에 없던 hasOwnProperty 를 사용할 수 있는 것이다.

생성자 함수로 생성된 객체의 프로토타입 체이닝

객체 리터럴 방식과는 조금 다른 방식으로 이뤄지는데 기본적으로 공통되는 것은 자바스크립트의 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 취급한다 라는 점이다.

function Person(name) {
  this.name = name;
}

let foo = new Person("foo");
console.log(foo.hasOwnProperty("name"));//true

크게 다른 점은 없다. foo 의 프로토타입 객체는 Person.prototype 이다. 하지만 위를 보면 Person 에도 hasOwnProperty 가 존재하지 않는다. 하지만 Person 또한 객체로써 결국 Person 의 프로토타입 객체는 Object.prototype 객체이다. 따라서 위에 말했듯이 foo 는 hasOwnProperty 찾아가 Object.prototype 까지 연결된다.

결국 객체 리터럴 방식이든 생성자 함수를 이용하든 Object.prototype 에서 프로토타입 체이닝이 끝나는 것을 알 수 있다.

이와 같은 방식을 이용해 우리는 Number.prototype, String.prototype 등 에 원하는 메소드를 추가해 사용할 수 있다. 이는 프로토타입 객체 또한 마찬가지다.

this 바인딩

만약 프로토타입 메서드 내부에서 this 를 사용했을 때 바인딩 되는 것은 그 메서드를 호출한 객체에 바인딩 된다.