[ 모던 자바스크립트 스터디 ] 프로토타입 - 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 천재
  • 공지사항

    • 배고픕니다
  • 인기 글

  • 태그

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

  • 최근 글

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

티스토리툴바