자바스크립트는 흔히 프로토타입 기반 언어(prototype-based language) 라 불린다.
모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object) 를 가진다는 의미이다.
상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype
이라는 속성에 정의되어 있다.
class Human {
constructor(name, age) {
this.name = name;
this.age = age;
}
sleep() {
console.log(`${this.name}은 잠에 들었습니다.`);
}
}
let kimcoding = new Human('김코딩', 30);
Human.prototype.constructor === Human; // true (Human 클래스의 생성자 함수는 Human 이다.)
Human.prototype === kimcoding.__proto__; // true (Human 클래스의 프로토타입은 Human 클래스의 인스턴스인 kimcoding '__proto__' 이다.)
Human.prototype.sleep === kimcoding.sleep; // true (Human 클래스의 sleep 메서드는 프로토타입에 있으며, Human 클래스의 인스턴스인 kimcoding에서 kimcoding.sleep으로 사용할 수 있다.)
class Machine { // 복사하는 기계
constructor(q, w) {
this.q = 'strike'; // 복사된 object가 기본으로 가질 속성
this.w = 'snowball';
}
}
let machine1 = new Machine(); // 부모 Machine 가 자식 machine1 생산
console.log(machine1); // {q: 'strike', w: 'snowball'}
일반적인 클래스 생성 과정이다.
하지만 Machine.prototype 을 개발자 도구에 입력하면
만든적 없는 prototype 이라는 속성의 결과값이 출력된다.
Machine 에는 자동으로 prototype
이라는 공간이 생긴다.
prototype
을 유전자 라고 생각하면 이해가 쉽다.
prototype
은 생성자 함수에 사용자가 직접 넣는 거, __proto__
는 new를 호출할 때 prototype을 참조하여 자동으로 만들어진다.
생정자 함수에는 prototype
, 생성자로부터 만들어진 객체(인스턴스)에는 __proto__
에 생성자의 prototype이 들어간다.
new Machine()
로써 만들어진 모든 객체(machine1
)는 결국 Machine.prototype 객체 와 내부적으로 [[Prototype]] 링크로 연결된다.
결국 machine1
와 Machine
은 상호 연결된 두 개의 객체가 된다.
class Machine {
constructor(q, w) {
this.q = 'strike';
this.w = 'snowball';
}
}
Machine.prototype.name = 'robot'; // 프로토타입에 네임이라는 속성 추가
let machine1 = new Machine();
machine1.name; // 'robot'
machine1에 name 속성을 구현한 적이 없는데 'robot' 이라는 출력값이 나온다.
하지만 machine1 에는 name 속성이 존재하지 않는다.
이러한 이유가 무엇일까?
- machine1에 name 속성이 있으면 속성값을 출력
- machine1에 name 속성이 없으면 machine1의 부모 유전자(prototype)에서 name 속성을 찾음
- prototype에 name 속성이 없으면 그 위 prototype에서 찾음
- 이러한 과정을 반복해 최상위 프로토타입에도 name 속성이 없으면 탐색을 종료하고
undefined
를 반환한다.
class Machine {
constructor(q, w) {
this.q = 'strike';
this.w = 'snowball';
this.action = function(){
console.log('move');
}
}
}
let machine1 = new Machine();
class Machine {
constructor(q, w) {
this.q = 'strike';
this.w = 'snowball';
}
}
Machine.prototype.action = function () {
console.log('move');
}
let machine1 = new Machine();
this에 넣은 것은 객체 하나를 만들 때 마다 메소드도 하나씩 만들어지지만, prototype은 모든 객체가 공유를 하고 있기 때문에 한 번만 만들어져 불필요한 메모리 사용을 줄일 수 있다.
let arr = [5, 3, 4, 2, 1];
arr.sort();
console.log(arr); // [1, 2, 3, 4, 5]
배열에서 .sort()
, .filter()
, .reduce()
가 사용 가능한 이유도 위와 같다.
arr를 선언할 때 sort()
메서드를 구현하지 않았는데 사용이 가능하다.
흔히 mdn에 Array 메서드를 검색했을 때 .prototype
이 붙는 이유이다.
배열을 만들 때 아래의 두 가지 방법이 있다.
let arr = [5, 3, 4, 2, 1];
let arr = new Array(5, 3, 4, 2, 1);
arr의 부모 유전자(prototype
)에 sort()
가 구현되어 있어 자식인 arr가 sort()
사용이 가능하다.
Array.prototype.hello = 'hi!';
arr.hello // 'hi!';
위처럼 prototype
에 저장하면 자식들도 사용이 가능하다.
배열(arr)는 Array
클래스의 인스턴스 이며, 프로토타입에는 다양한 메서드가 존재한다.
객체에 존재하지 않는 프로퍼티 접근을 시도하면 해당 객체 내부 [[Prototype]] 링크를 따라 다음 수색 장소를 결정한다.
모든 일반 객체의 최상위 프로토타입 연쇄는 내장 Object.prototype
이고 이 지점에서도 찾지 못하면 탐색이 종료 된다.
두 객체를 서로 연결짓는 가장 일반적인 방법은 함수 호출 시 new
키워드를 앞에 붙이는 것이다.
new
키워드는 일반 함수 호출 + '객체' 생성
이라는 잔업을 더 부과하는 지시자 이다.
const f = new Foo()
를 실행하면 Foo 함수가 실행되고, 객체가 생성되어 변수 f에 할당된다.
const f = new Foo();
typeof f; // object
typeof Foo; // function
constructor
는 내가 선언한 생성자 함수(Foo)를 가리킨다. new 키워드와 함께 함수를 호출할 경우 constructor 함수를 실행하고 부수효과로 객체가 생성된다.prototype
은 생성자 함수에 정의한 모든 객체가 공유할 원형이다.__proto__
는 [[Prototype]] 링크이다. 생성자 함수에 정의해두었던 prototype을 참조한다.참고
위 동영상 강의 보고 블로글 글 읽는 걸 추천합니다 🧔🏻♀️