이 글은 다른분의 글을 그대로 복사하는 것에 불과하며, 글의 내용을 온전히 이해하고싶어 작성한 내용입니다.
자바스크립트 객체는 Prototype
이라는 내부 프로퍼티가 존재
거의 모든 객체
가 생성시점에 이 프로퍼티에 null이 아닌 값이 할당된다.
우선 이 코드를 보자.
const woody = {
riding: true
}
woody.riding // true
woody.riding
처럼
객체 프로퍼티(속성)을 참조할 경우
[[GET]]이 호출되어 객체 내부에 해당 프로퍼티가 존재하는지 탐색한다.
그렇다면 객체 내부에 없는 프로퍼티를 호출하게 되면 어떻게 될까?
[[GET]]은 객체 내부에서 해당 프로퍼티를 찾지 못하면 바로 [[Prototype]]링크를 따라가 프로퍼티를 탐색한다.
(왜 자기 멋대로 내부를 뒤지는걸까..)
모든 일반 객체의 최상위 프로토타입 연쇄는
내장 Object.prototype
이고
이 지점에서도 찾이 못하면 탐색이 종료된다.(undefined 반환)
const buzz = {
flying: true
}
const woody = Object.create(buzz);
// 우디가 날 수 있게 되었다.
woody.flying; // true
woody와 buzz는 [[Protortpe]]이 링크되었다.
woody 내분에는 flying 이라는 프로퍼티가 없지만,
연결된 buzz에서 해당 프로퍼티를 찾아 그 값을 반환한 것이다.
for ... in
루프에서도 객체를 순회할 때,
prototype연결을 통해 탬색 가능한 프로퍼티라면 모두 열거한다.
function Foo() {
}
함수를 정의하면 함수만 생성되는 것이 아니라 Prototype object
도 같이 생성된다.
Prototype Object는 기본 속성으로
constructor
와
__proto__
를 가지고 있다.
(따라서 정확히 말하자면 Foo.prototype객체가 프로토 타입을 의미하는 것은 아니다. constructor
역시 가지고 있으니!!)
constructor:
내가 선언한 생성자 함수(Foo)를 가리킨다.
new 키워드와 함께 함수를 호출할 경우 constructor함수를 실행하고 부수효과로 객체가 생성된다.
생성자 함수가 아니라 함수를 생성하는 호출이라고 생각하는 것이 맞다.
prototype:
생성자 함수에 정의한 모든 객체가 공유할 원형이다.
proto:
[[Prototype]]링크이다. 생성자 함수에 정의해 두었던 prototype을 참조한다.
const f = new Foo();
Object.getPrototypeOf(f) === Foo.prototype; // true
prototype
은 생성자 함수에 사용자가 직접 넣는 거고,
function Foo() {};
__proto__
는 new 를 호출할 때 prototype을 참조하여 자동으로 만들어진다.
const f = new Foo();
생성자 함수는 prototype
에,
생성자로부터 만들어진 객체에는 __proto__
에
생성자의 prototype이 들어간다.
new
Foo()로써 만들어진 모든 객체(f
)는
결국 Foo.prototype 객체와 내부적으로
[[Prototype]]링크로 연결된다.
결국 Foo와 f는 상호 연결된 두개의 객체가 된다.
객체지향언어에서 클래스를 인스턴스화 하기 위해 new 연산자를 사용한다.
그리고 js에서도 두 개의 객체를 연결하기 위해 new 연산자를 사용한다.
하지만 전자의 경우
클래스로부터 작동을 복사하여 새로운 객체를 만드는 것이고
후자의 경우
복사 과정 없이 그저 두 객체를 연결할 것이 전부이다.
따라서 js의 new 키워드는 우리가 흔히 아는 new 키워드와 다르게 동작한다는 걸 알 수 있다.
js에서 new 연산자는 결국 새 객체를 다른 객체와 연결하기 위한 간접적인 우회 방법이고,
Object.create()
를 이용해 직접적으로 객체를 연결해주는 방법도 있다.
[[Prototype]]체계를 흔히들 프로퍼타입 상속이라고 부른다.
하지만 상속은 기본적으로 복사를 수반한다.
js는 객체 프로퍼티를 복사하지 않는다.
따라서 프로퍼타입 상속 ==> 프로퍼타입위임
이
js 객체 - 연결 체계를 훨씬 더 정확하게 나타내는 용어라고 할 수 있다.
앞 예제에서 new를 붙여 Foo함수를 호출하여 객체가 "생성"되는 현장을 목격했으니 Foo를 생성자라고 믿고 싶은 욕망을 버리기란 쉽지 않다.
하지만 Foo는 생성자가 아니다, 그저 함수
일 뿐이다.
함수는 결코 생성자가 아니지만, new를 붙여 호출하는 순간
이 함수는 생성자 호출
를 한다.
new 키워드는 일반 함수 호출 도중에 원래 수행할 작업 외에 객체 생성이라는 잔업을 더 부과하는 지시자인 것이다.
Foo(); // 함수 호출!
new Foo(); // Foo.prototype을 위임한 새로운 객체 생성!
// 왜 이렇게 만든거야.. ㅠㅠㅠㅠ
평범한 함수인 Foo을 new로 호출함으로써 객체가 생성
되고
부수효과로 생성된 그 객체를 a에 할당하는 것이다.
이것을 보통 생성자 호출 이라고 부르지만, nothing함수 자체는 생성자가 아니다.
즉, 자바스크립트 앞에 new를 붙여 호출한 함수를 모둔 생성자라고 할 수있다.
함수는 결코 생성자가 아니지만, new를 사용하여 호출할 때에만 생성자 호출
이다.
일반 함수가 생성자로 바뀌는 Magic..ㅠㅠ
// this를 사용
function Toy(name) {
this.name = name;
this.battery = 100;
this.charge = function() {
battery += 10;
console.log(`charging is finished. battery is ${this.battery}`)
}
}
const woody = new Toy('woody');
const buzz = new Toy('buzz');
// prototype 사용
function Toy(name) {
this.name = name;
this.battery = 100;
}
Toy.prototype.charge = function() {
this.battery += 10;
console.log(`charging is finished. battery is ${this.battery}`)
}
const woody = new Toy('woody');
const buzz = new Toy('buzz');
prototype은 모든 객체가 공유
하고 있어서 한번
만 만들어지지만,
this에 넣은 것은 객체 하나를 만들 때 마다 메소드도 하나씩 따로 만들어지기 때문에
불필요한 메모리 낭비가 발생한다.(물론 의도적으로 그렇게 했다면 불필요하지 않겠지만, )
객체에 존재하지 않는 프로퍼티를 접근하려 시도하면
해당 객체의 내부 [[Prototype]]링크를 따라 다음 수색 장소를 결정한다.
모든 일반 객체의 최상위 프로토타입 연쇄는 내장 Object.prototype이고
이 지점에서도 찾지 못하면 탐색을 종료하고 undefined를 반환한다.
두 객체를 서로 연결짓
는 가장 일반거인 방법은
new
키워드를 앞에 붙이는 것이다.
new 키워드는
일반 함수 호출 + "객체" 생성 이라는 잔업을 더 부과하는 지시자이다.
const f = new Foo();
를 실행하면 Foo 함수가 실행되고,
객체가 생성되여 변수 f 에 할당된다.
참고