Prototype 정리

지환·2024년 6월 16일
0

자바스크립트

목록 보기
21/30

proto 접근자 프로퍼티

  • 모든 객체는 proto접근자 프로퍼티 를 통해 자신의 프로토타입

  • [Prototype] 내부 슬롯에 간접적으로 접근 가능

  1. __prototype__접근자 프로퍼티이다.

    • __proto__는 접근자 프로터피이며, 간접적으로 접근할 수 있는 수단을 제고

    • __proto__가 접근자 프로퍼티이므로, getter/setter 접근자 함수를 통해 프로토타입 취득 및 할당 가능

const obj = {};
const parent = {x:1};
// proto 접근자 프로퍼티의 getter 접근자 함수로 obj 객체의 프로토타입 객체 취득
console.log(obj.__proto__); 
// [Object: null prototype] {}

obj.__proto__ = parent; 
// setter 접근자 함수로 obj 객체의 프로로타입에 값 할당.
  1. __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근

    • 프로토타입 체인은 단방향 연결 리스트로 구현 되어야 함.
    • 즉, 프로퍼티 식별자 검색 방향이 한쪽 방향 으로만 흘러야 함.

리터럴 표기법으로 생성된 객체의 생성자 함수와 프로토타입

const obj = {};
// obj 객체의 생성자 함수는 Object 생성자 함수다.
console.log(obj.constructor === Object); // true

프로토타입의 생성 시점

프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성된다.

  • 생성자 함수에는

    • 사용자 정의 생성자 함수
    • 자바스크립트에서 기본으로 제공해주는 빌트인 생성자로 구분된다.

사용자 정의 생성자 함수와 프로토타입 생성 시점

함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.

//함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입 더불어 생성
//함수 호이스팅 적용
console.log(Person.prototype); // {constructor : f}

function Person(name){
	this.name = name;
}
  • 생성된 프로토타입은 constructor 프로퍼티만을 갖는 객체이다.

  • 프로토타입도 객체이다. (모든 객체는 프로토타입을 갖는다)

  • 생성된 프로토타입의 프로토타입은 Object.prototype

빌트인 생성자 함수와 프로토타입 생성시점

빌트인 생성자 함수는 빌트인 생성자 함수가 생성되는 시점에 프로토타입 생성된다.

  • 모든 빌트인 함수는 전역 객체가 생성되는 시점에 생성된다.

  • 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩


객체 생성 방식과 프로토타입 결정

  1. 객체 리터럴

  2. Object 생성자 함수

  3. 생성자 함수

  4. Object.create 메소드

  5. 클래스 (ES6)

  • OrdinaryObjectCreate 호출로 -> 빈 객체를 생성

  • 객체에 추가할 프로퍼티 목록이 인수로 전달될 경우 -> 프로퍼티를 객체에 추가

객체 리터럴에 의해 생성된 객체의 프로토타입

  • 객체 리터럴에 의해 생성되는 객체의 프로토타입은 Object.prototype이다.
const obj = {x:1}; // 객체 리터럴로 생성된 obj 객체

1. obj객체에는 (constructor/hasOwnProperty) 소유x

2. 사용 가능 이유 -> Object.prototype에 있는 프로퍼티 상속 받았기 때문

console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty("x")); // true

Object 생성자 함수에 의해 생성된 객체의 프로토타입

Object 생성자 함수에 의해 생성되는 객체의 프로토타입은 Object.prototype

const obj = new Object();
obj.x = 1;

console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty("x")); // true

생성자 함수에 의해 생성된 객체의 프로토타입

생성자 함수에 의해 생성되는 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩 되어 있는 객체

  • Object 생성자 함수와 객체 리터럴로 생성된 객체의 프로토타입인 Object.prototype과 달리 오로지 constructor 프로퍼티만 존재한다.
// 사용자 정의 생성자 함수
function Person(name) {
  this.name = name;
}

// Person.prototype 에 프로퍼티를 동적으로 추가/삭제 가능
Person.prototype.sayHello = function () {
  console.log(`Hi! My name is ${this.name}`);
};

// 사용자 정의 생성자 함수로 생성된 인스턴스
const me = new Person("WI");
const you = new Person("KIM");

me.sayHello(); // Hi! My name is WI
you.sayHello(); // Hi! My name is KIM

console.log(me.constructor === Person); // true
console.log(you.constructor === Person); // true

프로토타입 체인

  • 프로토타입의 프로토타입은 언제나 Object.prototype이다.
  • 자바스크립트는 -> 객체의 프로퍼티(메소드 포함)에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 있는지 확인한다.

    • 없다면 -> [[prototype]] 내부 슬롯의 참조값을 따라

      • 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색
  • 이런 일련의 과정을 프로토타입 체인이라고 한다.

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

const me = new Person("WI");

console.log(me.hasOwnProperty("name")); // true
// 1. hasOwnProperty 메서드가 me 객체에 프로퍼티에 존재하는지 검색한다.
// 2. 없기 때문에, me 객체의 [[Prototype]] 내부 슬롯의 참조값을 통해, Person.prototype 객체의 프로퍼티를 검색
// 3. Person.prototype 에도 hasOwnProperty 메서드가 없으므로, [[Prototype]] 내부 슬롯의 참조값을 통해 Object.prototype 프로퍼티를 검색
// 4. Object.prototype 에는 hasOwnProperty 메서드가 있으므로, 자바스크립트 엔진은 Object.prototype.hasOwnProperty 메서드를 호출
// (이 때 this 에는 me 객체가 바인딩된다.)
  • 모든 객체는 Object.prototype를 상속 받는다.

    • Object.prototype를 프로토타입 체인의 종점 이라 한다.
    • Object.prototype의 프로토타입은 없다 --> null이다.
    • Object.prototype 에도 없는 프로퍼티를 조회할 경우, undefined 를 반환

프로토타입 체인 / 스코프 체인

  • 프토로타입 체인

    • 자바스크립트 엔진은 프로토타입 체인을 따라 -> 프로퍼티 & 메소드 검색
    • 객체 간의 상속 관계로 이뤄진 프로토타입의 계층적인 구조에서 객체의 프로퍼티 검색
    • 프로토타입 체인은 상속과 프로퍼티 검색을 위한 메커니즘이다.
  • 스코프 체인

    • 자바스크립트 엔진은 함수의 중첩 관계로 이뤄진 스코프의 계층적 구조에서 식별자를 검색
    • 따라서, 스코프 체인은 식별자 검색을 위한 메커니즘

스코프 체인과 프로토타입 체인은 서로 관련없이 별도로 동작하는게 아니라, 서로 협력하여 식별자와 프로퍼티를 검색하는데 사용한다.

  • 스코프 체인 → 프로토타입 체인 순

instanceof 연산자

  • 객체 instanceof 생성자 함수.
  • 우변의 생성자 함수의 prototype에 바인딩된 객체가

    -> 좌변의 객체의 프로토타입 체인 상에 존재하는지 체크한다.

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

const me = new Person("WI");
const parent = {};

Object.setPrototypeOf(me,parent);
// Person 생성자 함수와 parent 객체는 연결되어 있지 않다.
console.log(Person.prototype === parent); // false
console.log(parent.constructor === Person); // false


// parent 객체를 Person 생성자 함수의 prototype 프로퍼티에 바인딩
Person.prototype = parent;

// Person.prototype 이 me 객체의 프로토타입 체인 상에 존재함
console.log(me instanceof Person); // true
// Object.prototype 이 me 객체의 프로토타입 체인 상에 존재함
console.log(me instanceof Object); // true

직접 상속

Object.create에 의한 직접 상속

명시적으로 프로토타입을 지정하여 새로운 객체를 생성

  • 다른 객체 생성 방식과 마찬가지로 OrdinaryObjectCreate를 호출하는 것은 동일
  • 다른 점은 객체를 생성하면서 직접적으로 상속을 구현한다는 점
    • new 연산자 없이도 객체를 생성 가능
    • 프로토타입을 지정하면서 객체를 생성할 수 있다.
    • 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.
let obj = Object.create(null); 
console.log(Object.getPrototypeOf(obj) === null); // true


//obj -> Object.prototype -> null

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

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

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

const myProto = { x: 10 };
// obj -> myProto -> Object.prototype -> null
obj = Object.create(myProto);
console.log(Object.getPrototypeOf(obj) === myProto); // true

function Person(name) {
  this.name = name;
}
// obj -> Perosn.prototype -> Object.prototype -> null
obj = Object.create(Person.prototype);
console.log(Object.getPrototypeOf(obj) === Person.prototype); // true

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

const myProto = {x:10};
// 객체 리터럴에 의해 객체를 생성하면서, 프로토타입을 지정하여 직접 상속을 구현할 수 있다.
const obj = {
	y : 20,
  	__proto__ : myProto,

};
위 obj 정의는 다음과 같다.
const obj = Object.create(myProto, {
  y: { value: 20, writable: true, enumerable: true, configurable: true }
})
*/

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

정적 프로퍼티/메서드

생성자 함수로 인스턴스를 생성하지 않아도 참조/호출 가능한 프로퍼티/메소드

  • 생성자 함수도 객체이다.

  • 생성자 함수도 프로퍼티나 메소드를 소유할 수 있다.

  • 생성자 함수가 소유한 프로퍼티나 메소드를 정적 프로퍼티 / 메소드라고 한다.

    • 생성자 함수가 소유하고 있는 정적 프로퍼티/메소드는 인스턴스에서 직접 참조, 호출 할 수 없다.
// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHello = function () {
  console.log(`HI, My name is ${this.name}`);
};

// 정적 프로퍼티
Person.staticProp = "인간 생성자 함수의 정적 프로퍼티 !";

// 정적 메서드
Person.staticMethod = function () {
  console.log("인간 생성자 함수의 정적 메서드 호출 !");
};

const me = new Person("WI");

Person.staticMethod(); // 인간 생성자 함수의 정적 메서드 호출 !
me.staticMethod(); // TypeError: me.staticMethod is not a function

프로퍼티 존재 확인

in 연산자

객체 내에 특정 프로터피가 존재하는지 여부 확인

key in object
  • in 연산자 는 확인 대상 객체의 프로퍼티뿐만 아니라, 확인 대상 객체가 상속받은 모든 프로토타입의 프로토타입의 프로퍼티를 확인하는 것에 주의할 것
console.log("name" in person); // true
console.log("age" in person);
console.log("address" in person); //false

// toString 메서드는 person 객체의 프로퍼티에는 존재하지 않는다.
// 하지만, person 객체의 프로토타입인 Object.prototype 에 toString 메서드가 존재하기 때문에 true
// 이처럼, in 연산자는 조사할 객체의 상속받은 프로토타입의 프로퍼티까지 조사를 한다.
console.log("toString" in person); // true 🔍

Object.prototype.hasOwnProperty 메소드

in 연산자와 동일하게 객체 내에 특정 프로퍼티가 존재하는 여부 확인

  • 객체 고유의 프로퍼티 키 인 경우에만 true 반환

  • 상속받은 프로토타입의 프로퍼티 키인 경우는 false 반환

const person = {
  name: "WI",
  age: 100,
};

console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("age")); // true

// toString 메서드는 person 객체의 프로퍼티에 존재하지 않는다.
// 객체의 고유 프로퍼티일 때만 true 를 반환하는 Object.prototype.hasOwnProperty 메서드는 false 를 반환
// toString 은 person 객체에 존재하는 것이 아닌, 그에 상속된 Object.prototype 에 메서드이기 때문이다.
console.log(person.hasOwnProperty("toString")); // false 🔍

프로퍼티 열거

for ~ in 문

객체의 모든 프로퍼티를 순회하며 열거 할 필요가 있을 때 사용

for (변수선언문 in 객체) { ... }
  • for - in문은 순서를 보장하지 않는다.

  • 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서

    • 프로퍼티 어트리뷰트 [[Enumerable]] 의 값이 true 인 프로퍼티를 순회하며 열거한다.
const person = {
	name : "WI",
  	age : 100,
  
  	__proto__ : {
    	address : "Incheon"
    }
};

for(const key in person){
	console.log(`${key} : ${person[key]}`);
}

1. __proto__로 person 객체의 프로토타입에 프로퍼티로 address 프로퍼티를 추가했다. 


// in 연산자로 person 객체에 toString 프로퍼티(메서드)가 존재하는지 확인 -> 존재(true)
console.log("toString" in person); // true

1. 그럼에도 불구하고, toString 메서드는 for - in 문에서 key 에 할당되지 않았다.
2. 이는 toString 의 프로퍼티 어트리뷰트에서 [[Enumerable]] 값이 false 로 설정되어 있기 때문이다.

---
  
/*
name : WI
age : 100
address : Incheon
*/

오로지 객체의 프로퍼티 키들로만 for-in문을 순회하고 싶을 떄는 Object.prototype.hasOwnProperty 메소드를 호출하며 검사한다.

const person = {
	name : "WI",
  	age : 100,
  
  	__proto__ : {
    	address : "Incheon"
    }
};

for(const key in person){

  	if(person.hasOwnProperty(key)){
    
    	console.log(`${key} : ${person[key]}`);
    }
}

1. person 객체의 ** 고유 프로퍼티 ** 일 경우에만 정보를 출력한다.

2. __proto__가 나오지 않음 --> 고유 프로퍼티라는 점을 참고할것

객체 고유 프로퍼티만으로 구성된 열거하고 싶을 경우

  • Object.keys() : 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환 ( ES6+ )

  • Object.values() : 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환 ( ES8 )

  • Object.entries() : 객체 자신의 열거 가능한 프로퍼티 키,값을 배열로 반환 ( ES8 )

const person = {
  name: "WI",
  age: 100,

  __proto__: {
    address: "Incheon",
  },
};

console.log(Object.keys(person)); // [ 'name', 'age' ]
console.log(Object.values(person)); // [ 'WI', 100 ]
console.log(Object.entries(person)); // [ [ 'name', 'WI' ], [ 'age', 100 ] ]
profile
아는만큼보인다.

0개의 댓글