개발/Javascript

[You don't know JS] Part3 - 5장. Prototype

lanace 2020. 9. 6. 06:43

[Part2] Ch5. Prototype

5.1 [[Prototype]]

[[prototype]]이라는 내부 프로퍼티가 있고, 다른 객체를 참조하는 단순 레퍼런스로 사용

보통 생성 시점에 할당되며 [[Get]]으로 접근했는데 없으면 이 [[prototype]]을 사용하여 값을 찾는다.

var anotherObject = {
    a: 2
};

var myObject = Object.create(anotherObject);
myObject.a;  // 2

myObject는 anotherObject와 [[prototype]]으로 링크되었다.

myObject에는 a라는 값이 없지만 anotherObject에서 찾아온것이다.

var anotherObject = {
    a: 2
};

var myObject = Object.create(anotherObject);

for(var k in myObject) {
    console.log(`${k} 를 발견`);
}

"a" in myObject;  // true

위와 같이 myObject에서 a라는 프로퍼티를 찾을 수도 있다.

5.1.1 Object.prototype

[[Prototype]]의 끝나는 지점은 내장 프로토타입 Object.prototype이다.

모든 객체는 Object.prototype객체의 자식이다.

여기에는 toString이나 valueOf와 같은 함수도 포암되어 있다.

5.1.2 프로퍼티 세팅과 가려짐

myObject.foo = "bar";

위의 코드는 여러 작업들을 하는데, 하나씩 살펴보자

  1. myObject에 foo라는 값이 있는경우 직접 수정
  2. myObject에 foo가 없는경우 [[Prototype]]을 통해 foo라는 값을 찾음
  3. [[Prototype]]에서 찾으면 해당 foo에 값을 할당
  4. [[Prototype]]에서 못찾으면 myObject 객체에 foo를 추가한 뒤 값을 할당

가려짐

위의 과정중 myObject에 foo가 있어서 [[Prototype]]에 있는 foo를 찾지 못하면 이를 가려짐 이라고 한다.

  • [[Prototype]]연쇄의 상위 수준에서 foo가 존재하는데, 읽기 전용이 아니면 가려짐 발생
  • [[Prototype]]상위에 foo가 존재하고 읽기전용이면 가려짐이 발생하지 않는다. (strict mode에선 에러)
  • [[Prototype]]상위에 foo가 존재하고, setter인 경우 setter가 호출됨. 가려짐이 발생하지 않음

만약 아래 두가지 경우에서 가려짐을 사용하고 싶다면 (myObject에 직접 프로퍼티 생성이 필요할시) Object.defineProperty()를 사용하면 된다.

2번 케이스에 대한 이슈? 뭔지 잘 모르겠음... (117p)

메서드간 위임이 필요한 상황에선 가림현상 때문에 안좋은 명시적 의사다형성이 유발되므로 가급적 피하는게 좋다.

⇒ 이를 해결하기 위해 6장의 작동 위임을 대안으로 사용 가능

var anotherObject = {
    a: 2
}

var myObject = Object.create(anotherObject);

anotherObject.a;  // 2
myObject.a; // 2

anotherObject.hasOwnProperty("a");  // true
myObject.hasOwnProperty("a");  // false

myObject.a++;  // 암시적 가려짐 발생

anotherObject.a;  // 2
myObject.a; // 3

myObject.hasOwnProperty("a");  // true

근데 왜 부모에 있는 a에 대입이 아니라 myObject에 직접 접근해서 프로퍼티를 생성하는거지??

5.2 클래스


객체와 다른 객체를 연결해야 되는 이유

⇒ ???

5.2.1 클래스 함수

자바스크립트는 prototype이라는 공용, 열거 불가의 프로퍼티를 가지고 있다.

function Foo() {...};

Foo.prototype; // {}

var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype;  // true

Foo.prototype 객체와 [[Prototype]]은 링크로 연결된다.

new를 통해 이루어지는 4단계중 하나가 Foo.prototype과 [[Prototpye]]을 연결시키는 것이다.

일반적인 상속과 자바스크립트의 상속의 차이

일반적 상속은 복사를 통해 부모로부터 가져온다.

하지만 자바스크립트는 복사가 아니라 링크를 걸고, 각각이 prototype을 통해 불러오는것이다.

차등 상속

일반적인 객체와 비교하여 차이가 있는 부분만을 기술하는 것

5.2.2 생성자

Foo 클래스 처럼 보이는 이유는 new 키워드 때문이다.

function Foo() {
    ...
}

var a = new Foo();

a.constructor === Foo;  // true

new는 일반 함수 호출을 도중에 가로채어 원래 수행할 작업 외에 객체 생성이라는 잔업을 더 부과하는 지시자이다.

function NothingSpecial() {
    console.log('');
}

var a = new NothingSpecial();
a; // {}

NothingSpecial은 평범한 함수이다.

이를 앞에 new를 붙여주면 생성자 호출이 된다.

하지만 NothingSpecial가 생성자는 아니다

5.2.3 체계

function Foo(name) {
    this.name = name;
}

Foo.prototype.myName = function() {
    return this.name;
}

var a = new Foo("a");
var b = new Foo("b");

a.myName();  // 'a'
b.myName();  // 'b'

위 예제는 클래스지향 꼼수이다.

  1. this.name = name 할당시 name 프로퍼티가 a, b 객체에 추가된다.
  2. Foo.prototype에 myName이라는 함수를 추가했다.