LECTURE/JavaScript

프로토타입 - 상속, 생성자 함수 프로토타입, Object 프로토타입, 모던 메소드

heywoo 2023. 2. 9. 00:16
프로토타입 상속
자바스크립트의 객체는 [[Prototype]]이라는 숨김 프로퍼티를 갖는다.
이 프로퍼티 값은 null이거나 다른 객체에 대한 참조가 되는데, 다른 객체를 참조하는 경우 참조 대상을 프로토타입(prototype)이라 부른다.
 
 
const user = {
    activate : true,
    login : function () {
        console.log('로그인 되었습니다')
    }
};

const student = {
    passion : true 
};

.__proto__ [[Prototype]] 의 getter, setter라 할 수 있다.

student.__proto__ = user;

console.log(student.activate);    //true
student.login();				  //로그인 되었습니다.
console.log(student.passion);     //true

 

 => "student의 프로토타입은 user이다." 또는 "student는 user를 상속 받는다." 라고 표현한다.
 => 프로토타입에서 상속 받은 프로퍼티를 상속 프로퍼티라고 한다.

 

프로토타입 체인

object에서 프로퍼티를 읽으려 할 때 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 찾는다.

const greedyStudent = {
    class : 11,
    __proto__ : student
};

console.log(greedyStudent.activate);    //user에서 상속 받은 상속 프로퍼티
console.log(greedyStudent.passion);     //student에서 상속 받은 상속 프로퍼티

 

프로토타입 특징
const user = {
    id : 'user',
    login () {
        console.log(`${this.id}님 로그인 되었습니다.`)
    }
};

const student = {
    __proto__ : user
};

 

 

  • 프로퍼티 추가, 수정 삭제
프로토타입은 프로퍼티를 읽을 때만 사용하며 프로퍼티 추가, 수정, 삭제 연산은 객체에 직접한다.
login 메소드 내의 this는 프로토타입에 영향 받지 않으며 this는 언제나 동작할 때 .앞에 있는 객체를 의미한다. (student)
 
student.id = 'user01';

student.login();  //user01님 로그인 되었습니다.
 
  • for in 반복문
for in 반복문은 상속 프로퍼티도 순회 대상에 포함시킨다.
 
hasOwnProperty(key) : key에 대응하는 프로퍼티가 상속 프로퍼티가 아니라 객체에 직접 구현된 프로퍼티일 경우 true 반환
 
for(let key in student) {
   
    console.log(key);


    let isOwn = student.hasOwnProperty(key);

    if(isOwn) {
        console.log(`객체 자신의 property : ${key}`);
    }
   
}

// 출력
id
객체 자신의 property : id
login

 

생성자 함수 프로퍼티
 new 연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해서 [[Prototype]]을 설정한다.
const user = {
    activate : true,
    login () {
        console.log('로그인 되었습니다');
    }
};

function Student(name) {
    this.name = name;
}
F.prototypenew F를 호출할 때만 사용된다.
new F를 호출할 때 만들어지는 새로운 객체의 [[Prototype]]을 할당한다. (DNA가 전달된다고 생각해보자)
Student.prototype = user;  // new Student를 호출할 때 만들어지는 새로운 객체마다 user를 상속하게 한다.

let student = new Student('홍길동');    //student.__proto__ == user  (student의 조상은 user이다)
let student2 = new Student('유관순');

console.log(student.activate);    // true
student2.login();    // 로그인 되었습니다
 
 
Student의 다양한 DNA 정보에 user를 추가한다고 생각한다면 new Student로 만들어진 객체에 user의 DNA 정보가 상속된다.
prototype 은 .__proto__ 와 달리 name, age와 같은 일반 프로퍼티인데, 다만 위와 같은 기능을 갖고 있는 것이다.

 

Object prototype

Object는 내장 객체 생성자 함수인데 이 생성자 함수의 prototype은 toString을 비롯한 다양한 메소드가 구현 된 거대한 객체를 참조한다.

 new Object()나 {}를 사용해 객체를 만들 때 만들어진 객체의 [[Prototype]]은 Object.prototype을 참조한다.

(Object의 DNA 정보 즉, 다양한 메소드를 참조한다.)

const obj = {};    // new Object(); 와 같다
console.log(obj.__proto__ === Object.prototype);            //true obj도 객체로서 Object의 메소드들을 쓸 수 있다.
console.log(obj.toString === obj.__proto__.toString);       //true 상속 받은 toString이다.
console.log(obj.toString === Object.prototype.toString);	//true DAN정보에 있는 toString이다

 

built in object prototype
Fucntion, String, Number를 비롯한 내장 객체들 역시 프로토타입에 메서드를 저장한다.
모든 내장 프로토타입 상속 트리 꼭대기에는 Object.prototype이 있어야 한다고 규정한다.

 

const num = new Number(100);

// num은 Number.prototype을 상속 받았는가?
console.log(num.__proto__ == Number.prototype);      //ture

// num은 Object.prototype을 상속 받았는가?
console.log(num.__proto__.__proto__ === Object.prototype);    //true

// 체인 맨 위에는 null이 있다.
console.log(num.__proto__.__proto__.__proto__); 			//null

 

Object.prototype에도 toString이 있지만 중복 메서드가 있는 경우 체인에서 가까운 메서드가 사용되어 Number.prototype의 toString이 사용된다.
 
console.log(num);  //[Number: 100]
console.log(num.toString()); //100      ->toString 은 value값만 보여준다.
 
내장 프로토타입 수정 가능하다.
하지만 되도록 변경하지 않아야 하며, 명세서에 새로 등록된 기능을 쓰고 싶지만 자바스크립트 엔진에 구현되지 않은 경우 정도에만 변경한다. 
String.prototype.hello = function () {
    console.log(`hello, ${this}`);
}

"JavaScript".hello();     //hello, JavaScript

 

프로토 타입 접근 시 사용하는 모던 메소드
  • Object.create(proto) : [[Prototype]] 가 proto를 참조하는 빈 객체를 만듦
const user = {
    activate : true
};

const student = Object.create(user);
console.log(student.activate);    //true
  •  Object.getPrototypeOf(obj) - obj의 [[Prototype]]을 반환
console.log(Object.getPrototypeOf(student));           //{ activate: true }
console.log(Object.getPrototypeOf(student) === user);  //true
  •  Object.setPrototypeOf(obj, proto) - obj의 [[Prototype]]을 proto가 되도록 설정
Object.setPrototypeOf(student, {});
console.log(Object.getPrototypeOf(student));              // {}
console.log(Object.getPrototypeOf(student) === user);     // false
 __proto__를 getter, setter로 직접 사용하면 키가 "__proto__" 일 때 에러가 발생하는 의도치 않은 상황이 발생할 수 있다.
const obj = Object.create(user);  
let key = "__proto__";
console.log(obj[key]);  					// = obj.__proto__ 와 같기에 { activate: true } 출력
obj[key] = { test : "새로운 객체 덮어쓰기"} 
console.log(obj[key]);						//{ test: '새로운 객체 덮어쓰기' }
console.log(obj.__proto__);					//{ test: '새로운 객체 덮어쓰기' }