[JavaScript] ProtoType

DY·2022년 8월 31일
0

JavaScript

목록 보기
3/12

javascript는 prototype기반 언어이다.

  • javaScript의 모든 객체들이 메소드와 속성들을 상속받기위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는의미.

  • 상속되는 속성과 메소드들은 해당 객체가 아니라 prototype속성에 정의 되어 있다.

  • 즉 prototype은 상속받은 멤버들이 정의되는 곳이다.(해당 객체가 복사되는것이 아닌 마치 체인을 타고 올라가며 접근하는것 처럼 '참조'하고 있음)

  • 프로토타입 객체는 __proto__ 속성으로 접근 가능한 내장 객체이고, 'prototype 속성'은 상속 시키려는 멤버들이 정의된 객체를 가리킨다.

  • [[Prototype]]은 __proto__로 접근이 가능하고 obj.toString은 prototype chain에 의해 obj.__proto__.constructor 처럼 찾아간다

  • Array는 클래스다. 지금가지 써왔던 배열은 Array클래스의 인스턴스였고 Array.메소드 들은 Array클래스의 prototype에서 가져온것 이다.

  • push, slice ... 은 Array.prototype에 구현되어있다

prototype chain

  • prototype객체가 상위 prototype 객체로부터 메소드와 속성을 상속받을 수 있고 이 prototype객체 역시 상위 prototype객체에서 상속받을수 있다. 이를 prototype chain이라 부른다.
  • 하위 prototype에서 특정 프로퍼티를 찾을 때 상위 prototype 프로퍼티 까지 찾는것도 prototype chain의 특징이다.
  • 따라서 JavaScript는 prototype chain을 통해 상속을 구현한다.

[[Prototype]]

  • JavaScript의 모든 객체에는 숨김 프로퍼티가 존재하고 이를 '[[Prototype]]' 이라 부른다
  • 다른 객체를 참조하는 경우 참조 되는 대상을 'prototype'이라 부른다.
  • 만약 객체의 프로퍼티를 읽으려고 하는데 해당 객체에 찾고자하는 프로퍼티가 존재하지 않으면 Prototype chain에의해 [[Prototype]]에서 프로퍼티를 찾는다.

__proto__

  • [[Prototype]] 프로퍼티는 __proto__를 사용하여 값을 설정할 수 있다.
let plants = {
	photosynthesis : true
}

let Rose = {
	color: "red"
}

let flower = {
	petal : 5,
    __proto__ : Rose
}

console.log(Rose.photosynthesis) // undefined

Rose.__proto__ = plants;		/* [[Prototype]]의 프로퍼티 값 할당.*/

console.log(Rose.photosynthesis) // true
- __proto__는 [[Prototype]]의 getter,setter 역할을 한다. 

- Rose에 photosynthesis라는 프로퍼티가 없기 때문에 [[Prototype]]의 프로퍼티를 자동으로 탐색함.


console.dir(Rose)	//red

console.dir(Rose.__proto__)	//photosynthesis

  • [[Prototype]]이 Plants객체를 참조하게 만듬

  • 즉 Rose는 Plants를 상속 받는다 라고 할 수 있음. 객체지향형 프로그래밍의 상속과 비슷.

  • 마찬가지로 Plants에 함수가 멤버로 있다면 Rose에서도 사용이 가능해진다.

  • 근래엔 Object.getPrototypeOf , Object.setPrototypeOf를 사용한다고함.

  • proto의 값은 객체나 null만 가능하다.

  • 하위 체인에서 상위 체인의 프로퍼티를 수정할수 없다. 수정시 해당 체인의 프로퍼티가 추가되는거로 간주한다. -> getter만 가능하다는 뜻

for...in 반복문

  • 상속 프로퍼티도 순회 대상에 포함이 된다.
  • hasOwnProperty(key)는 상속프로퍼티를 순회 대상에서 제외할 수 있다.

  • Object.keys, Object.values 는 기본적으로 상속 프로퍼티를 제외하고 동작한다.

내장 객체의 프로토타입

let sampleobj = {};
alert ( sampleobj ) // [object Object]
  • sampleobj = {} 는 sampleobj = new Object() 과 같다.
  • new 키워드를 이용해 Object()함수로 생성자 함수를 호출하면 이 생성자 함수의 prototype은 다양한 메서드가 구현되어있는 객체인 Object의 Object.prototype을 참조한다.(위에 설명한 일반 객체를 가리키는 것과는 다름)
let sampleobj = {
	__proto__ : Object.prototype	//Object를 참조 하는게 아님
}
  • sampleobj.toString()을 호출하면 Object.prototype의 프로퍼티toString()을 가져오게 된다.

  • 배열의 경우도 new Array()로 Array.prototype을 [[Prototype]]이 참조하여 만들어진다.

  • Date, Function도 위와 같다.

  • 모든 객체 ( Array.prototype포함 )의 상위에는 Object.prototype이 존재하고 그 상위는 null을 가리킨다.

let arr = [1, 2, 3];

arr.__proto__ === Array.prototype				//true
arr.__proto__.__proto__ === Object.prototype	//true

원시값

  • 원시값은 객체가 아니기때문에 실질적으로 프로퍼티가 존재하지 않는다.
  • 원시값도 사용하려면 new연산자를 이용해서 할당해줘야하는데 이때는 임시 래퍼 (wrpper)객체가 생성되고, 메서드를 제공하고 난 뒤에는 사라진다.
  • null 과 undefined에는 해당하는 래퍼가 존재하지 않는다. 프로토타입도 존재하지 않는다.

내장 prototype에 메서드 추가

  • Array.prototype에 객체에 키를 추가하듯이 프로퍼티를 추가 할 수 있다.
  • 이렇게 추가하면 해당 Array.prototype에 의해 생성된 배열은 추가한 프로퍼티를 사용할 수 있게 된다.
  • 여러가지 충돌문제로 인해 사용을 금지시 한다.
  • 필요시 "메서드 빌리기" 같은 방법을 사용할 수 있다.
let obj = {
  0: "Hello",
  1: "world!",
  length: 2,
};

obj.join = Array.prototype.join;

alert( obj.join(',') ); // Hello,world!​

Array.prototype.join = F // join함수를 F함수로 바꿔버림 -> 배열들의 join메서드들은 F함수가 실행됨 ->프로토타입체인 특성

proto 를 '지양'

  • Object.create(proto, [descriptors]) – [[Prototype]]이 proto를 참조하는 빈 객체를 만듭니다. 이때 프로퍼티 설명자를 추가로 넘길 수 있습니다.
  • Object.getPrototypeOf(obj) – obj의 [[Prototype]]을 반환합니다.
  • Object.setPrototypeOf(obj, proto) – obj의 [[Prototype]]이 proto가 되도록 설정합니다.
let plants = {
	photosynthesis : true
};

let Rose = {};
Rose.__proto__ = plants; //이 과정을

let Rose = Object.create(plants); //이렇게 바꿈.

함수의 prototype 프로퍼티

  • 생성자 함수의 프로토타입이 객체인 경우에 new연산자를 통해 만든 객체는 생성자 함수의 프로토타입 정보를 이용해 [[Prototype]]을 설정한다
let plants = {
	photosynthesis : true
};

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

rose.prototype = plants;	// plants를 참조하는 객체를 생성

let Rose = new rose();  // Rose.__proto__ = photosynthesis

함수의 디폴트 프로퍼티

  • 함수가 참조할 prototype을 할당하지 않으면 기본적으로 constructor 프로퍼티 하나만 있는 객체를 가리킨다.
  • constructor 프로퍼티는 함수 자기 자신을 가리킨다. 모든 함수는 기본적으로 가지고 있기 때문에 해당 함수의 생성자를 알고 싶다면 constructor를 확인해 보면 된다.
  • constructor : 인스턴스가 초기화될 때 실행하는 생성자 함수.
function Rose() {}

/* 
디폴트 prototype
Rose.prototype = { constructor: Rose };
*/

생각해보기

class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
}

let kimcoding = new Human('김코딩', 30);

Human.prototype.constructor === Human; // 결과는?
Human.prototype === kimcoding.__proto__; //결과는?
Human.prototype.sleep === kimcoding.sleep; //결과는?
  

reference

https://ko.javascript.info/prototype-inheritance

https://ko.javascript.info/native-prototypes

https://ko.javascript.info/prototype-methods

https://ko.javascript.info/function-prototype

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes

profile
프론트엔드 개발자가 되기 위해 공부중입니다. 블로그는 공부한 내용을 올리고 있습니다.

0개의 댓글