코어 자바스크립트 - 06. 프로토타입

iamsummer__·2021년 1월 13일
0
post-thumbnail

자바스크립트는 프로토타입 기반 언어이다.
클래스 기반 언어에서는 상속을 사용하지만, 프로토타입 기반 언어에서는 어떤 객체의 원형을 삼고, 이를 복제함으로써 상속과 비슷한 효과를 얻는다.

1️⃣ 프로토타입 개념 이해

const instance = new Construtor();

위의 코드를 그림으로 도식화 하면 아래와 같다.

어떤 생성자함수(constructor)를 new 연산자와 함께 호출하면
constructor에서 정의된 내용을 바탕으로 새로운 인스턴스(instance)가 생성된다.
이 때 instance에는 proto 라는 프로터피가 자동으로 부여된다.
이 프로퍼티는 contructor의 prototype이라는 프로퍼티를 참조한다.
실무에서는 가급적
proto를 사용하지 않아야한다. 그 이유는 브라우저가 아닌 다른환경에서는 지원하지 않는 방식이 아닐수도 있기 때문이다.

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

Person.prototype.getName = function() {
	console.log(this._name);
}

const ju = new Person('ju');
ju.getName(); // ju
ju.__proto__.getName(); // undefined

ju.proto.getName()로 호출을 하게 되면 undefined라고 콘솔에 찍히게 된다.
에러가 발생하지 않는 거로 보아
proto를 사용하여 메소드를 호출해도 타입에러는 발생하지 않는다.

💣 그러면 의심해봐야할 내용은 this에 바인딩된 대상이 잘못되었나?⁉️

메서드로서 호출할 때는 메서드명 바로 앞의 객체가 곧 this가 된다고 했다.
그러면 ju.proto.getName()에서 this는 ju.proto가 되는 것이다.
그렇기 때문에 undefined를 반환하였다.
proto는 생략 가능한 프로퍼티이다. 그러므로 proto를 생략하여 사용한다.

ju(__proto__).getName(); // ju
ju.getName(); // ju

new 연사자와 함께 함수를 호추할 경우, 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 __proto가 자동으로 생성되며, 이 프로퍼티는 생성자 함수의 prototype프로퍼티를 참조한다.

__proto가 생략가능하도록 구현돼 있기 때문에 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 메서드나 프로퍼티에 접근할 수 있게 된다.

const arr = [1,2];
console.dir(arr);
console.dir(Array);


arr를 출력한 결과이다.
첫줄에는 Array(2)라고 되어 있다. Array라는 생성자 함수를 원형으로 삼아 생성됐고, length가 2이다.
인텍스 0,1이 짙은색상으로, length, proto는 옆은 색상으로 표기 되었다.
proto를 열어보니 배열 메서드 push,pop,map 등이 있다.

색상의 차이는 열거가능여부에 따른다.
짙은색: enumerable, 즉 열거가능한 프로퍼티
옅은색: innumerable, 열거할 수 없는 프로퍼티


첫줄에 함수라는 의미에 f가 표시되어 있고 둘째줄 부터는 함수의 기본적은 프로퍼티인 arguments, caller등이 있다.
Array함수의 정적 메서드인 from, isArray, of도 존재한다.
prototype을 열어보니 위의 __proto와 동일한 내용으로 구성되어 있다.

instance인 [1,2]가 만들어지고, __proto는 Array.prototype을 참조하는데 여기에는 배열 메서들이 있다. 그러므로 인스턴스는 push,pop등 배열 메서드들을 마치 자신의 것처럼 호출할 수 있다.
그러나 Array의 prototype프로퍼티 내부에 있지 않는 from, isArray등은 인스턴스가 직접 호출할 수 없다.

arr.forEah(function() {});
Array.isArray(arr); // true
arr.isArray(); // TypeError: arr.isArray is not a function

📝 constructor 프로퍼티

생성자 함수의 프로퍼티인 prototype객체 내부에는constructor라는 프로퍼티가 있다.
인스턴스의 __proto 객체 내부에도 마찬가지로 존재한다.
이 프로퍼티는 단어 그대로 원래의 생성자 함수를 참조하며 인스턴스로부터 그 원형이 무엇인지 알 수 있다.

const arr = [1,2]
Array.prototype.constructor === Array //true
arr.__proto__.constructor === Array // true
arr.constructor === Array // true

constructor을 임의로 변경 가능하지만 참조하는 대상이 변경될 뿐 이미 만들어진 인스턴스 원형이 바뀌거나 데이터 타입이 변하는 것은 아니다.
어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는게 항상 안전하지는 않다.

2️⃣ 프로토타입 체인

📗 메서드 오버라이드

prototype 객체를 참조하는 __proto를 생략하면 인스턴스는 prototype에 정의된 프로퍼티나 메서드를 마치 자신의 것처럼 사용할 수 있다고 했다.

💡 그러면 만약 인스턴스에 동일한 이름의 프로퍼티 또는 메서드를 가고 있다면 ❓❗️

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

Person.prototype.getName = function() {
	console.log(this.name);
}

const ju = new Person('ju');
ju.getName = function () {
	console.log('##', this.name);
}

ju.getName(); // ## ju

ju객체에 있는 getName메서드가 호출되어 ## ju라고 나온다.
이러한 현상을 메서드 오버라이딩이라 한다.

메서드 오버라이딩: 메서드 위에 메서드를 덮어씌웠다는 표현

js엔진은 getName이라는 메서드를 찾는 방식은 가장 가까운 대상인 자신의 프로퍼티를 검색하고 없으면 그 다음으로 가까운 __proto 를 검색하면서 순서대로 진행된다.
ju객체에 getName이라는 메서드가 있어 ju객체에 있는 getName의 메서드가 호출되었다.

📗 프로토타입 체인

const arr = [1,2]
arr(.__proto__).push(3);
arr).__proto__).hasOwnProperty(3); // true

proto는 생략가능한다. 그렇기 때문에 배열이 Array.prototype 내부의 메서드를 자신의 것처럼 사용할 수 있다. 마찬가지로 Object.prototype내부의 메서드도 자신의 것처럼 실행할 수 있다. 생략가능한 proto를 한번 더 따라가면 Object.prototype을 참조할 수 있기 때문이다.

프로토타입 체인: 어떤 데이터의 proto 프로퍼티 내부에서 다시 proto 프로퍼티가 연쇄적으로 이어진 것이다. 이 체인을 따라 검색하는 것은 프로토타입 체이닝이라고 한다.

📚 정리

📌 어떤 생성자 함수를 new 연산자와 함께 호출하면 constructor에 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데, 이 인스턴스에는 proto라는 constructor의 prototype프로퍼티를 참조하는 프로퍼티가 자동으로 부여된다.
📌
proto는 생략가능한 속성이며 인스턴스는 constructor.prototype의 메서드를 마치 자신의 메서드인 것 처럼 호출 가능하다.
📌 constructor.prototype에는 constuctor라는 프로퍼티가 존재하는데 이는 생성자 함수 자기 자신을 나타내며, 인스턴스가 자신의 생성자 함수가 무엇인지를 알고자 할 때 필요한 수단이다.
📌 proto안에 다시 proto를 찾아가는 과정을 프로토타입 체이닝이라하며, 프로토타입 체이닝을 통해 각 프로토타입 메서드를 자신의 것 처럼 호출할 수 있다.
이때 접근 방식은 자신으로 부터 가장 가까운 대상부터 점차 먼 대상으로 나가가며, 원하는 값을 찾으면 검색을 중단한다.
📌 Object.prototype에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재한다.
📌 프로토타입 체인은 반드시 2단계로만 이루어지지 않고 무한대의 단계를 생성할 수 있다.

profile
개발하는 프론트엔드개발자

0개의 댓글