[JS] 모던 자바스크립트 Deep Dive - 19장-2

Leona·2023년 12월 7일

19.8 오버라이딩과 프로퍼티 섀도잉

섀도우 복싱 ㅋㅋㄹㅃㅃ

하위 객체에서 상위 객체 프로토타입 get

const Person = (function () {
  //생성자 함수
  function Person(name) {
    this.name = name;
  }
 
  // 프로토타입 메서드
  Person.prototype.sayHello = function () {
    console.log(`Hello, Im ${this.name}`);
  }
 
  // 생성자 함수를 반환
  return Person;
 }())

// 인스턴스 생성
const me = new Person('Leona');

// 인스턴스 메서드
me.sayHello = function () {
  console.log(`Hi, My name is ${this.name}`);
}

// 인스턴스 메서드 호출
me.sayHello(); // ???
  • 오버라이딩: 상위 클래스의 프로토타입 메서드인 sayHello를 재정의해서 사용
  • 프로퍼티 섀도잉: 상속 관계에 의해 프로퍼티가 가려지는 현상

하위 객체에서 상위 객체 프로토타입 set

// me 인스턴스에서 메서드 삭제
delete me.sayHello;

// me 인스턴스에는 sayHello 메서드가 없다.
me.sayHello(); // ???
  • me 인스턴스에서 메서드를 삭제했기 때문에 상위 객체 프로토타입에서 sayHello를 검색해서 실행한다.
  • 만약 위 과정을 한번 더 수행한다면? => 하위 객체가 상위 객체를 set할 순 없기 때문에 sayHello 메서드를 실행한다.
  • 하위 객체에서 상위 객체 프로토타입 프로퍼티를 set하려면 체인이 아닌 프로토타입에 직접 접근해야 한다.
    delete Person.prototype.sayHello;
    me.sayHello(); // TypeError: me.sayHello is not a function

19.9 프로토타입 교체

  • 프로토타입은 부모 객체인 프로토타입을 동적으로 변경할 수 있다.
  • 객체 간 상속 관계를 통해 동적으로 변경할 수 있다는 의미

19.9.1 생성자 함수에 의한 프로토타입 교체

const Person = (function () {
  function Person(name) {
    this.name = name;
  }
  
  // 생성자 함수의 프로토타입 교체
  Person.prototype = {
    sayHello() {
      console.log(`Hello world! My name is ${this.name}`);
    }    
  }
  
  return Person;
}());

const me = new Person('Leona');
  • 이렇게 교체한 객체 리터럴에는 constructor가 없다. JS 엔진이 프로토타입을 생성할 때 암묵적으로 추가한 프로퍼티라 me 객체의 생성자 함수를 검색하면 Person이 아닌 Object가 나옴

  • 프로토타입을 교체하면 constructor와 생성자 함수 연결이 끊긴다. 이를 되돌리려면 constructor 프로퍼티를 추가하면 된다.
    const Person = (function () {
     function Person(name) {
       this.name = name;
     }
     
     // 생성자 함수의 프로토타입 교체
     Person.prototype = {
       constructor: Person,
       sayHello() {
         console.log(`Hello world! My name is ${this.name}`);
       }    
     }
     
     return Person;
    }());
  • 그리고 생성자 함수가 constructor를 가리키게 된다.

19.9.2 인스턴스에 의한 프로토타입 교체

  • 인스턴스의 _ proto _ 접근자 프로퍼티(or Object.setPrototypeOf 메서드)를 통해 프로토타입을 교체할 수 있다.

     function Person(name) {
       this.name = name;
     }
    
    const me = new Person('Leona');
     
     // 프로토타입으로 교체할 객체
     const parent = {
       sayHello() {
         console.log(`Hello world! My name is ${this.name}`);
       }    
     }
     
     // === me.__proto__ = parent;
    Object.setPrototypeOf(me, parent);
    
    me.sayHello();

19.10 instanceof 연산자

  • 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인에 존재하면 true, 아니면 false를 리턴한다.

  • 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인에 존재하는지 확인한다.

    function Person(name) {
      this.name = name;
    }
    
    const me = new Person('Leona');
    
    console.log(me instanceof Person); // true
    console.log(me instanceof Object); // true
    const me = new Person('Leona');
    const parent = {};
    
    Object.setPrototypeOf(me, parent);
    
    // Object.prototype에 바인딩 했기 때문에 Person 객체와 체인이 끊긴 상태
    console.log(me instanceof Person); // false
    console.log(me instanceof Object); // true
    Person.prototype = parent;
    
    // Person 생성자 함수의 prototype에 바인딩 했기 때문에 체인이 걸렸다.
    console.log(me instanceof Person); // true
    console.log(me instanceof Object); // true

19.11 직접 상속

19.11.1 Object.create

  • 프로토타입을 명시적으로 지정해서 새로운 객체를 생성한다.

  • 첫번째 매개변수인 객체의 프로토타입 체인에 속하는 객체를 생성한다.(상속을 직접 구현)

  • new 연산자 없이, 프로토타입을 지정하면서 생성하며, 객체 리터럴도 상속받을 수 있다.

  • Object.prototype을 상속받기 때문에 해당 객체의 메서드를 호출할 수 있다.(hasOwnProperty 등)

    // prototype: 생성할 객체의 프로토타입으로 지정할 객체
    // propertiesObject: 생성할 객체의 프로퍼티를 갖는 객체
    Object.create(prototype[, propertiesObject]);
    
    // Object.prototype 상속받기
    const obj = Object.create(
      Object.prototype, {
      x: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true,
      }
    });
    
    console.log(Object.getPrototypeOf(obj) === Object.getPrototype); // true
    
    // 임의의 객체 상속받기
    const proto = { y: 2 };
    
    const obj2 = Object.create(proto);
    
    console.log(Object.getPrototypeOf(obj2) === proto); // true
    
    // 생성자 함수 상속받기
    function Person(name) {
      this.name = name;
    }
    
    const obj3 = Object.create(Person.prototype);
    obj3.name = 'Leona';
    
    console.log(obj3.name); // Leona
    console.log(Object.getPrototypeOf(obj3) === Person.prototype); // true

19.11.2 객체 리터럴 내부 _ proto _

  • ES6에서 객체 리터럴 내부 _ proto _ 접근자 프로퍼티로 직접 상속 구현

    const proto = { x: 1};
    
    const obj = {
      y: 2,
      __proto__: proto,
    }
    
    console.log(obj.x, obj.y); // 1, 2
    console.log(Object.getPrototypeOf(obj) === proto); // true

19.12 정적 프로퍼티/메서드

  • 생성자 함수로 인스턴스를 생성하지 않아도 참조 및 호출할 수 있는 프로퍼티, 메서드

  • 생성자 함수가 생성한 인스턴스는 자신의 프로퍼티, 메서드에 접근할 수 있지만, 프로퍼티, 메서드가 인스턴스에 접근할 수 없다. => 프로토타입 체인에 속하지 않았기 때문

    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
    
    // 정적 프로퍼티
    Person.staticProp = 'static prop';
    // 정적 메서드
    Person.staticMethod = function () {
      console.log('static method');
    }
    
    const me = new Person('Leona');
    
    Person.staticMethod(); // static method
    me.staticMethod(); // TypeError: staticMethod is not a function

19.13 프로퍼티 존재 확인

19.13.1 in 연산자

  • 객체 내에 특정 프로퍼티가 있는지 확인하는 연산자

    key in object
    
    const person = {
      name: 'Leona',
      age: 30,
    }
    
    console.log('name' in person);    // ???
    console.log('address' in person); // false
    console.log('age' in person);     // ???
  • 객체 프로퍼티 뿐 아니라 객체가 상속받는 모든 프로토타입의 프로퍼티를 확인한다.

  • 객체가 속한 프로토타입 체인 상에 존재하는 모든 프로토타입을 검색하기 때문

    // Object.prototype 메서드
    console.log('toString' in person); // true
  • ES6에서는 Reflect.has 메서드를 쓸 수 있다.

    Reflect.has(person, 'name'); // true

19.13.2 Object.prototype.hasOwnProperty 메서드

person.hasOwnProperty('name');    // true
person.hasOwnProperty('address'); //false

19.14 프로퍼티 열거

19.14.1 for... in 문

  • 객체의 모든 프로퍼티를 순회하며 프로퍼티를 검색한다.
    for (const key in person) {
      console.log(key + ': ' + person[key]); // name: Leona, age: 30 ...
    }
  • 상속받은 프로토타입의 프로퍼티까지 열거하나 Object.prototype의 프로퍼티는 열거할 수 없도록 정의된 프로퍼티라 열거하지 않는다.
  • 이는 [[Enumerable]] 값이 false이기 때문(프로퍼티 열거 가능 여부)
  • 정리하면 for... in 문은 Enumerable 값이 true인 프로퍼티만 순회하며 열거한다.

19.14.2 Object.keys/values/entries

  • 객체 고유의 프로퍼티만 열거할 때 사용하는 것이 좋다.
    const person = {
      name: 'Leona',
      age: 30,
    }
  • Object.keys: 열거 가능한 프로퍼티 키를 배열로 반환
    Object.keys(person); // ['name', 'age']
  • Object.values: 열거 가능한 프로퍼티 키의 값을 배열로 반환
    Object.values(person); // ['Leona', '30']
  • Object.entries: 열거 가능한 프로퍼티 키와 값을 배열 쌍으로 반환
    Object.entries(person); // [['name', 'Leona']], ['age', 30]]
profile
레오나의 기묘한 개발 일지

0개의 댓글