Prototype Chain JS 21.04.13


prototype을 사용한 OOP

  • 모든 function은 prototype이라는 속성을 가진다.

  • contructor : 생성자. 특정 객체가 생성 될 때 실행 되는 코드

// contructor 함수
let Human = function(name) {
    this.name = name;
}

// Human이란 함수 역시 prototype을 갖는다. 
// Human의 prototype에 method나 속성을 정의 가능하다.
// prototype이 원형 객체(original form)이기 때문에.
// Human > prototype > sleep
Human.prototype.sleep = function() {
    console.log(`${this.name} is sleeping`)
};

// instantiation : instance를 생성하는 과정
let terry = new Human('terry');

// terry.__proto__ === Human.prototype
console.log(terry.__proto__);
// {constructor: ƒ}
//     > constructor: ƒ (name)
//     > __proto__: Object

terry.toString(); // "[object Object]"
// toString()이라는 method를 만든적이 없지만 작동이 된다
// 이유 : 내장 method이기 때문에. 
// terry의 prototype을 .__proto__로 계속 찾다보면 (체이닝)
// 원형 객체(Object)의 prototype에 toString이라는 method를 가지고 있다.

terry.sleep()
// terry is sleeping
  • prototype chain ex code : 내장 method가 어디에 위치에 있는지 확인하기.
terry.__proto__ // = Human.prototype

// {sleep: ƒ, constructor: ƒ}
//     sleep: ƒ ()
//     constructor: ƒ (name)
//     __proto__:
//         constructor: ƒ Object()
//         hasOwnProperty: ƒ hasOwnProperty()
//         isPrototypeOf: ƒ isPrototypeOf()
//         propertyIsEnumerable: ƒ propertyIsEnumerable()
//         toLocaleString: ƒ toLocaleString()
//         toString: ƒ toString() // !toString이 여기 위치해있다!
//         valueOf: ƒ valueOf()
//         __defineGetter__: ƒ __defineGetter__()
//         __defineSetter__: ƒ __defineSetter__()
//         __lookupGetter__: ƒ __lookupGetter__()
//         __lookupSetter__: ƒ __lookupSetter__()
//         get __proto__: ƒ __proto__()
//         set __proto__: ƒ __proto__()

terry.__proto__.__proto__
// toString은 terry의 prototype의 prototyep에 있다.
// 즉 toString() method는 terry.__proto__.__proto__ 에 위치해있다.
// 상속의 가장 상위 단계 Object에 있다.
  • prototype chain ex code 2 : HTML x DOM
let li = document.createElement('li');

li.__proto__ // === HTMLLIElement.prototype

li.__proto__.__proto__ 
// HTMLElement{...}

li.__proto__.__proto__.__proto__ 
// Element{...}

li.__proto__.__proto__.__proto__.__proto__ 
// Node{...}

li.__proto__.__proto__.__proto__.__proto__ .__proto__
// EventTarget {...}

li.__proto__.__proto__.__proto__.__proto__ .__proto__.__proto__
// {consturctor ...}

prototype은 할당 가능 (assignable)

  • ex
let makeArr = function() {}

console.log(makeArr.prototype)
// {constructor: ƒ}
//    > constructor: ƒ ()
//    > __proto__: Object


// 자바스크립트에서 함수는 객체 형식.
// 함수 makeArr의 prototype에 Array의 prototype 할당.
// 즉, 함수 makeArr의 prototype에 
// Array의 prototype이 가진 method(push, slice, etc..) 등을 할당
makeArr.prototype = Array.prototyep;

console.log(makeArr.prototype)
// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]


let arr = new makeArr();
// 위에서 Array의 prototype을 할당 했으므로 push 등의 method를 사용 가능
arr.push(1) // makeArr [1]

prototype을 사용한 상속 구현

let Human = function(name) {
    this.name = name;
}

Human.prototype.sleep = function() {
    console.log(`${this.name} is sleeping`)
};

let Student = function(name) {
    this.name = name;
}

Student.prototype.learn = function() {
    console.log(`${this.name} is studying programming`)
}

let terry = new Student('terry');

terry.learn(); // terry is studying programming

terry.sleep(); // 오류 발생 : Student가 Human의 sleep method를 상속 받지 못했기 때문에
  • 상속 방법
let Student = function(name) {
    // 상속
    Human.call(this, name) = name;
}

// Student의 prototype에 Human의 prototype을 copy하여 그걸 참조한다.
Student.prototype = Object.create(Human.prototype);
// Object.create(객체인자) : 첫번째 객체를 새로운 prototype으로 만든다. 

// 상속을 정확하게 하기 위해서는 연결고리를 다시 연결해줘야한다.
Student.prototype.constructor = Student;

// leran 함수를 할당해준다.
Student.prototype.sleep = function() {
    console.log(`${this.name} is sleeping`)
};


Student.prototype
// {learn: ƒ, sleep: ƒ, constructor: ƒ}

terry.sleep()
// terry is sleeping // 제대로 작동

terry instanceof Student 
// true. 
// a instanceof B : a가 B의 instance인지 확인

prototype으로 상속을 구현하기에는 너무 복잡하다. class keyword를 사용해라.

class

class Human {
    constructor (name) {
        this.name = name;
    }
    sleep () {
        console.log(`${this.name} is sleeping`)
    }
}

class Student extends Human {
    constructor (name, job) {
        // super를 사용하여 부모의 class의 this까지 상속
        super(name); // 만약 부모 class의 constructor가 똑같다면 생략가능
        // 이 코드에서는 새로운 속성 job을 넣어줬기 때문에 constructor와 super를 사용해야함.
            this.job = job;
    }
    learn () {
        console.log(`${this.name} is studying programming`)
    }

    intro() {
        console.log(`Hi My name is ${this.name}. I'm ${this.job}`)
    }
}

let user1 = new Student('terry', 'developer');

user1 // Student {name: "terry", job: "developer"}

// Human의 method 상속
user1.sleep() // terry is sleeping

// Student의 method
user1.learn() // terry is studying programming
user1.intro() // Hi My name is terry. I'm developer

prototype

function Person (name, job) {
    this.name = name;
    this.job = job;
    this.intro = function() {
        console.log(`Hi my name is ${this.name}, I'm ${job}`)
    }
}

let person1 = new Person ('terry', 'developer')

person1 // Person {name: "terry", job: "developer", intro: ƒ}

person1.toString() // "[object Object]"
// prototyep chain이 작동하는 증거 : toString() method는 person1에 없지만 에러가 발생하지 않는다.

위 코드에서 toString() method는 person1에 없지만 에러가 발생하지 않는다. 이는 prototyep chain이 작동하는 증거이다.

person1.toString() 이 코드가 실행될 때 다음과 같은 일이 일어난다.

  1. 브라우저는 우선 person1 object가 toString() method를 가지고 있는지 체크

  2. 없으므로 person1의 프로토타입 객체(=== Person() 생성자의 프로토타입)에 toString() method가 있는지 체크

  3. 여전히 없으므로 Person() 생성자의 프로토타입 객체의 프로토타입 객체(=== Object() 생성자의 프로토타입)가 toString() 메소드를 가지고 있는지 체크. 가지고 있으므로 체이닝 종료.

상속되는 속성과 메소드들은 각 객체가 아니라 객체의 constructor(생성자)의 prototype이라는 속성에 정의되어 있다.

Object.prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
// Object를 상속받은 객체에서 사용 가능한 수 많은 메소드들이 
// Object의 prototype 속성에 정의.
// prototype도 객체이다. 





© 2020.11. by creamer

Powered by CREAMer