[ 모던 자바스크립트 스터디 ] 프로토타입 - 2

2022. 10. 2. 14:47·Archive/Develop
728x90
반응형

프로토타입 Person.prototype 에 프로퍼티를 추가해

자식 객체가 상속받을 수 있도록 구현한 코드이다.

 

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

Person.prototype.sayHello = function () {
	console.log(`Hi! ${this.name}`);
}

const me = new Person('Hong');
const you = new Person('Bi');


me.sayHello(); // Hi! Hong
you.sayHello(); // Hi! Bi

 

 

 

프로토타입 체인

 

자바스크립트는 객체의 프로퍼티에 접근하려 할 때 해당 객체에 접근하려는 프로퍼티가 없다면

[[Prototype]] 내부 슬롯의 참조를 따라 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다.

 

이를 프로토타입 체인이라고 한다.

 

위의 예제에서 me.hasOwnProperty('name') 은 true 를 반환한다.

 

이 과정은 아래와 같이 설명된다.

 

1. hasOwnProperty 메서드를 호출한 me 객체에서 hasOwnProperty 를 검색한다. me 객체에는 없으므로 [[Prototype]] 에 바인딩되어 있는 프로토타입(Person.prototype) 으로 이동해 또 다시 hasOwnProperty를 검색한다.

 

2. Person.prototype 에도 hasOwnProperty 메서드가 없으므로 또 다시 [[Prototype]] 에 바인딩된 프로토타입(Object.prototype) 을 따라서 hasOwnProperty를 검색한다.

 

3. Object.prototype 에는 hasOwnProperty가 있다. 따라서 Object.prototype.hasOwnProperty 메서드를 호출한다. 이때 Object.prototype.hasOwnProperty 메서드의 this 는 me 객체가 바인딩 된다.

 

Object.prototype.hasOwnProperty.call(me, 'name');

 

Object.prototype 은 프로토타입 체인의 종점이다.

 

그렇기에 Object.prototype 의 프로토타입, 즉 [[Prototype]] 의 값은 null 이다.

 

만약 Object.prototype 에서도 프로퍼티를 검색할 수 없다면 undefined 를 반환한다.

 

 

전에 학습했던 스코프 체인과 개념이 혼동될 수 있다.

하지만 스코프체인은 식별자 검색을 위한 매커니즘이라는 것을 기억해두자.

 

me.hasOwnProperty('name');

 

스코프체인의 관점에서, 위 예제는 먼저 스코프 체인 내에서 me 식별자를 검색한다.

me 식별자는 전역에서 선언되었으므로 전역 스코프에서 검색한 후,

me 객체의 프로토타입 체인에서 hasOwnProperty 메서드를 검색한다.

 

 

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

 

const Person = (function() {
	function Person(name) {
    	this.name = name;
    }
    
    Person.prototype.sayHello = funciton () {
    	console.log(`hi! ${this.name}`);
    };
    
    // 생성자 함수 반환
    return Person;
}());

const me = new Person('Hong');

me.sayHello = function() {
	console.log(`hey~! ${this.name}`);
}

me.sayHello(); // hey~! Hong

 

프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면

인스턴스 프로퍼티로 추가한다.

 

이처럼 상속 관계에 의해 의해 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉이라고 한다.

 

만약 프로퍼티를 삭제한다고 가정해보자.

 

 

delete me.sayHello;

me.sayHello(); // hi! Hong

delete me.sayHello;

me.sayHello(); //hi! Hong

 

 

처음 sayHello 프로퍼티를 삭제했을 때는 인스턴스 프로퍼티를 삭제했기때문에

sayHello 를 호출했을 때 프로토타입 프로퍼티인 sayHello 를 호출한다.

 

두번째 삭제는 이뤄지지않는다.

하위 객체를 통해 프로토타입의 프로퍼티를 변경 또는 삭제하는 것은 불가능하기 때문이다.

 

프로토타입 프로퍼티를 변경/삭제 하려면 프로토타입에 직접 접근해야한다.

 

delete Person.prototype.sayHello;

 

 

 

다음 파트는 프로토타입의 교체인데, 사실 교체를 통해 객체 간의 상속 관계를 동적으로 변경하는 것은 번거롭기 때문에

직접 교체하는건 바람직하지 않다.

 

상속관계를 인위적으로 설정하려면 직접상속이 더 편리하고 안전하기에 깊은 설명은 하지않겠다.

 

 

instanceof 연산자

 

우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변 객체의 프로토타입 상에 존재하면

true 평가를, 그렇지 않으면 TypeError 가 발생한다.

 

객체 instanceof 생성자 함수

 

 

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

const me = new Person('Hong');

console.log(me instanceof Person); // true
console.log(me instanceof Object); // true

 

만약 프로토타입을 교체한다면?

 

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

const me = new Person('Hong');

const parent = {};

Object.setPrototypeOf(me, parent);


console.log(Person.prototype === parent); // false
console.log(parent.constructor === Person); // false


console.log(me instanceof Person); // false
console.log(me instanceof Object); // true

 

만약 Person.prototype = parent; 구문을 추가한다면

 

me instanceof Person 은 true 로 평가될 것이다.

 

이처럼 instanceof 연산자는 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수를 찾는것이 아닌

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

 

 

직접 상속

 

Object.create 에 의한 직접 상속

 

Object.create 메서드는 명시적으로 프로토타입을 지정해 새 객체를 생성한다.

Object.create 메서드도 다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate를 호출한다.

 

Object.create 메서드는 첫 번째 매개변수에 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다.

 

// 프로토타입이 null 인 객체 생성

let obj = Object.create(null);
console.log(Object.getPropertyOf(obj) === null); //true

// Object.prototype 상속 못받음
console.log(obj.toString()); // TypeError


//obj = {}; 와 동일
obj = Object.create(Object.prototype);
console.log(Object.getPropertyOf(obj) === Object.prototype); //true

obj = Object.create(Object.prototype, {
	x: { value: 1, writable: true, enumberable: true, configurable: true}
});

console.log(obj.x); //1
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

const myProto = { x: 10 };

obj = Object.create(myProto);

console.log(obj.x); // 10
console.log(Object.getPrototypeOf(obj) === myProto); // true

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

obj = Object.create(Person.prototype);
obj.name = 'Hong';
console.log(obj.name); //Hong

 

즉, 객체를 생성하면서 직접적으로 상속을 구현하는 것이다.

 

 

위 방법을 사용하면

 

new 연산자 없이 객체 생성,

프로토타입을 지정하며 객체 생성가능,

객체 리터럴에 의해 생성된 객체도 상속받을 수 있음

 

등의 장점을 가진다.

 

 

객체 리터럴 내부에서 __proto__ 에 의한 직접 상속

 

Object.create 메서드에 의한 직접 상속은 여러 장점이 있으나

두번째 인자로 프로퍼티를 정의하는게 굉장히 번거롭다.

 

ES6에서는 객체 리터럴 내부에서 __proto__ 접근자 프로퍼티를 사용해 직접 상속을 구현할 수 있다.

 

const myProto = {x: 10};

const obj = {
	y: 20,
    __proto__: myProto
};

console.log(obj.x, obj.y); // 10 20
console.log(Object.getPrototypeOf(obj) === myProto); //true

 

정적 프로퍼티/메서드

 

정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지않아도 참조/호출 할 수 있는 메서드나 프로퍼티를 말한다.

 

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

Person.staticProp = '정적 프로퍼티';
Person.staticMethod = function () {
	console.log('정적 메소드');
}

Person.staticMethod();

me.staticMethod(); // TypeError

 

정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.

 

 

프로퍼티 존재 확인

 

in 연산자

 

key in object

 

 

const person = {
	name: 'Hong',
    address = 'hh'
};

console.log('name' in person); // true

 

만약 console.log('toString' in person);  구문을 실행한다면 true 를 반환할 것이다.

toString 은 Object.prototype 의 메서드이기 때문에 true 를 반환한다.

 

 

Object.prototype.hasOwnProperty 메서드

 

console.log(person.hasOwnProperty('name')); //true
console.log(person.hasOwnProperty('toString')); //false

 

 

in 과의 차이를 확인할 수 있다.

 

 

프로퍼티 열거

 

for ( 변수선언문 in 객체) {}

 

for ... in 문은 순회 대상 객체의 프로퍼티 뿐만아니라 상속받은 프로토타입의 프로퍼티까지 열거한다.

하지만 toString과 같은 Object.prototype 프로퍼티는 열거되지 않는데,

그 이유는 toString 메서드가 열거할 수 없도록 정의되었기 때문이다.

 

 

만약 상속받은 프로퍼티는 제외하고 객체 자신의 프로퍼티만 열거하고 싶다면

Object.prototype.hasOwnProperty 메서드를 이용해 객체 자신의 프로퍼티인지 확인해야한다.

 

const person = {
	name: 'Hong',
    address: 'ss',
    __proto__: {age: 20}
};


for (const key in person) {
	if (!person.hasOwnProperty(key)) continue;
    console.log(key + ': ' + person[key]);
}

// name: Hong
// address: ss

 

유의해야할 점은, for ... in 문은 순서를 보장하지 않는다는 것이다.

 

객체 자신의 고유 프로퍼티만 열거하기 위해서는 Object.keys/values/entries 메서드를 사용하길 권장한다.

 

Object.keys 메서드는 객체 자신의 프로퍼티 키를 배열로 반환하고,

Object.values 메서드는 객체 자신의 프로퍼티 값을 배열로 반환한다.

 

Object.entries 메서드는 객체 자신의 프로퍼티 키와 값의 쌍 배열을 배열에 담아 반환한다.

 

 

728x90
반응형

'Archive > Develop' 카테고리의 다른 글

[ 모던 자바스크립트 스터디 ] 클로저(closure)  (0) 2022.10.14
[ 모던 자바스크립트 스터디 ] 실행 컨텍스트  (1) 2022.10.12
[ 모던 자바스크립트 스터디 ] 프로토타입 - 1  (0) 2022.10.02
[ 모던 자바스크립트 스터디 ] strict mode  (0) 2022.10.02
[ 모던 자바스크립트 스터디 ] 왜 __proto__ 접근자 프로퍼티를 통해 접근하는가?  (0) 2022.10.02
'Archive/Develop' 카테고리의 다른 글
  • [ 모던 자바스크립트 스터디 ] 클로저(closure)
  • [ 모던 자바스크립트 스터디 ] 실행 컨텍스트
  • [ 모던 자바스크립트 스터디 ] 프로토타입 - 1
  • [ 모던 자바스크립트 스터디 ] strict mode
코뮤(commu)
코뮤(commu)
코딩으로 커뮤니케이션하는 코뮤입니다 😎
  • 코뮤(commu)
    코뮤(COMMU)
    코뮤(commu)
  • 전체
    오늘
    어제
    • 분류 전체보기
      • Archive
        • Hacking
        • Develop
        • ETC
      • Algorithm
      • DB&Infra
      • ETC
      • Node
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • IT지식보따리
    • IT가 맛있다
    • IT 천재
  • 공지사항

    • 배고픕니다
  • 인기 글

  • 태그

    자바스크립트 API
    백준 파이썬
    백준
    oracle db
    장고
    자바스크립트 객체
    Codeup
    오라클
    자바스크립트
    파이썬 기초
    파이썬 알고리즘
    코드업 파이썬 기초 100제
    파이썬 문제
    파이썬
    코드업 기초
    Django
    비박스
    javascript
    docker
    Python
    파이썬 백준
    보안뉴스
    카카오 100일 프로젝트
    백준 문제풀이
    Oracle
    C++
    Git
    백준 풀이
    파이썬 기초 문제
    코드업
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
코뮤(commu)
[ 모던 자바스크립트 스터디 ] 프로토타입 - 2
상단으로

티스토리툴바