JS prototype 글써보며 정리해보기

이종호·2021년 5월 22일
0

JavaScript

목록 보기
17/19
post-thumbnail

이 글은 다른분의 글을 그대로 복사하는 것에 불과하며, 글의 내용을 온전히 이해하고싶어 작성한 내용입니다.

출처: 자바스크립트 Prototype 완벽 정리

[[Prototype]] 링크

자바스크립트 객체는 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 연산자

객체지향언어에서 클래스를 인스턴스화 하기 위해 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 에 할당된다.

  • constructor:
    내가 선언한 생성자 함수(Foo)를 가리킨다.
    new 키워드와 함께 함수를 호출할 경우, constructor함수를 실행하고 부수효과로 객체가 생성된다.
  • prototype:
    생성자 함수에 정의한 모든 객체가 공유할 원형이다.
  • proto:
    [[Prototype]]링크이다. 생성자 함수에 정의해두었던 prototype을 참조한다.

참고

profile
코딩은 해봐야 아는 것

0개의 댓글