[Javascript] Prototype과 __proto__의 이해


자바스크립트 프로토타입에 대하여

프로토타입을 이해한 순간 자바스크립트가 재미있어지고, 자바스크립트를 마음대로 다룰 수 있다고 생각합니다.

문제개요

어느날 이런 코드를 보았습니다.

function Test () { }

Test.sum = function() {
	return 1 + 1;
}

Test.prototype.sum = function () {
	return 2 + 1;
}

평소 자바스크립트의 프로토타입을 이해하고 있었다고 생각했지만, 이 코드를 보며 생각하지 않을 수가 없었습니다.
분명 sum 함수는 차이가 있는걸 알고 있지만 왜 차이가있는지, 왜 그렇게 쓰는지 알 수 없었습니다.

참고로 위 코드는 아래의 코드와 같습니다.

class Test {
	static sum () {
		return 1 + 1;
	}

	sum () {
		return 2 + 1;
	}
}

클래스에 대한 이야기는 다음에 다루도록 하겠습니다.


어떻게 다를까?

javascript_console

Test 객체를 new 키워드로 생성하니 2 + 1을 해주는 함수(이하 B함수)는 __proto__ 안에 들어갔고, 1 + 1을 해주는 함수(이하 A함수)는 사라졌습니다.

그럼 이번엔 조금 다른 코드를 실행해보겠습니다.

javascript_console

A함수가 __proto__와 같은 위치에 들어갔고, B함수는 proto 안에 있습니다.
이 상태에서 t.sum() 을 실행시키면 A함수가 실행됩니다.

그렇다면 이번엔 A함수를 정의하지 않고 t.sum() 을 실행시키겠습니다.

javascript_console

B함수가 실행이되어 3이 리턴됩니다. B함수는 __proto__안에 있는데 어떻게 실행이되고 3이 리턴이된걸까요?

이번엔 좀 다른 예제를 들어보겠습니다.

javascript_console

이번에도 역시 toString이란 함수는 없지만 정상적으로 실행이 되었습니다.

toString은 Object의 prototype중 하나입니다 Object.prototype.toString


__proto__가 주범

객체의 prototype은 객체를 생성했을때 __proto__ 안에 들어가게됩니다. 그래서 B함수가 __proto__ 안에 존재하는것이죠.

자바스크립트 객체는 만약 찾는 곳(이 포스팅 에서는 t.sum())이 없다면 __proto__ 에 있는지 확인하고 있다면 찾아주는 것입니다.
만약 __proto__에도 없다면 __proto____proto__까지 찾게됩니다.

다시 말 하자면 t.sum이 없다면 t.__proto__.sum 을 찾고 또 없다면 t.__proto__.__proto__.sum 까지 찾는것이죠. 이게 반복되어 결국 최상위까지 가게되고, 끝까지 없다면 undefined를 리턴해줍니다.

그렇다면 t.__proto__는 우리가 Test 생성자에 prototype으로 선언한것들이 들어갔습니다. 그래서 B함수(t.__proto__.sum)가 실행된거죠.
하지만 t.__proto__.__proto__에는 어느 객체의 prototype이 들어갔던걸까요? 방금전 봤던 콘솔을 다시 보겠습니다.

javascript_console

toString은 Object의 프로토타입입니다. 즉 t의 prototype link(= proto)중 Object가 있는것이죠.

javascript_console

그래서 t.__proto__.__proto__Object.prototype을 비교하면 true가 나옵니다. Number의 prototype과 비교하면 false가 나오겠습니다.


상속을 받은것인가?

그렇다면 Test로 생성된 t는 Object의 프로토타입을 사용할 수 있으니 상속을 받은것일까요?

정답은 그렇습니다. (저는 상속보다는 확장[extend]이 더 올바른 표현이라고 생각합니다)

저는 이런생각을 했습니다.

Object 객체가 아닌, 다른 객체(내가 만든 객체)를 상속해 줄 수는 없을까?

function Animal () {

}

Animal.prototype.bark = function () {
	console.log(this.barkSound);
}
Animal.prototype.getName = function () {
	console.log(this.animalName);
}

function dog () {
	this.barkSound = "bowwow";
	this.animalName = "dog";
}

var volt = new dog();

volt.bark(); // Uncaught TypeError

결국 오류가 났습니다. 저는 볼트가 짖는소리를 듣고싶은데 말이죠.

function Animal () {

}

Animal.prototype.bark = function () {
	console.log(this.barkSound);
}
Animal.prototype.getName = function () {
	console.log(this.animalName);
}

function Dog () {
	// 추가한 코드, Animal의 prototype을 원래 Object의 prototype 있던 자리에 넣습니다
	this.__proto__.__proto__ = Animal.prototype;

	this.barkSound = "bowwow";
	this.animalName = "dog";
}

var volt = new Dog();

volt.bark(); // bowwow
console.log(volt.toString) // [object Object]

Animal의 prototype을 강제로 넣어줌으로써 볼트의 울음소리를 들을 수 있게됐군요!

하지만 이렇게 하면 Object의 prototype을 쓸 수 있을까요?

네! 쓸 수 있습니다 Animal.prototype.__proto__Object.prototype이기 때문이죠

javascript_console

정리하자면 이렇게 되었습니다.

  • volt = 생성된 Dog객체.
  • volt.__proto__ = Dog객체의 prototype.
  • volt.__proto__.__proto__ = Animal객체의 prototype.
  • volt.__proto__.__proto__.__proto__ = Object객체의 prototype.

이렇게 되어서 volt는 Dog, Animal, Object 들의 prototype을 모두 쓸 수 있는것이죠!


문제해결

아직도 자바스크립트 이해가 잘 안된다. 더욱 더 공부를많이해야겠다.

참고한 사이트

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/super https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/class




© 2019. by jong-hui

Powered by aiden