[JS Deep Dive] Prototype

웅이·2022년 5월 16일
0

객체

객체는 상태(프로퍼티)와 이 상태를 조작하는 동작(메서드)으로 이루어져 있다.

상속

객체지향 프로그래밍에서 어떤 객체의 프로퍼티나 메서드를 다른 객체가 상속받아 그대로 사용할 수 있도록 한다.
자바스크립트에서는 상속을 프로토타입 기반으로 구현해 불필요한 중복을 제거한다.

function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    return Math.PI * this.radius ** 2;
  };
}

const circle1 = new Circle(1);
const circle2 = new Circle(2);

console.log(circle1);
// Circle { radius: 1, getArea: [Function (anonymous)] }
console.log(circle2);
// Circle { radius: 2, getArea: [Function (anonymous)] }

위 코드에서 circle1과 circle2 인스턴스는 모두 Circle 생성자 함수로 생성돼 radius와 getArea 함수를 갖는다.

그런데 getArea 함수는 재사용이 가능한 함수로 굳이 모든 인스턴스마다 생성될 필요 없이 하나만 만들어서 공유하는 것이 낫다.

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
};

const circle1 = new Circle(1);
const circle2 = new Circle(2);

console.log(circle1);
// Circle { radius: 1 }
console.log(circle2);
// Circle { radius: 2 }

console.log(circle1.getArea(), circle2.getArea());
// 3.141592653589793 12.566370614359172

이렇게 Circle 생성자 함수의 prototype에 getArea 함수를 공유할 수 있도록 해두면 각 인스턴스는 부모객체가 가진 getArea 함수를 상속받아 사용할 수 있고 불필요한 중복이 제거 된다.

가장 많이 쓰는 Array라는 생성자 함수의 경우,

let a = new Array(1, 2, 3);
a.length;
a.sort();

평소 우리는 length나 sort와 같은 함수를 자주 가져다 쓴다.

이는 Array라는 생성자 함수가 갖고 있는 prototype 함수에 length나 sort 함수가 이미 구현되어 있기 때문이다.

console.log(Array.prototype) 을 해보면

이렇게 배열 구조에서 사용할 수 있는 프로토타입 함수들이 나온다.

만일 내가 자주 쓰는 함수를 또 추가해주고 싶으면

Array.prototype.newFunction = function() {
	blahblah...
}

해서 선언해주고 사용하면 모든 Array에서 사용가능하다.

프로토타입 객체 & constructor

모든 객체는 하나의 프로토타입을 갖고 모든 프로토타입은 생성자함수와 연결돼있다.
모든 prototype은 constructor 프로퍼티를 갖고 있다.

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

const p1 = new Person('Lee');
console.log(p1.constructor === Person); // true

p1 객체의 생성자함수는 Person이다.
p1 객체는 프로토타입의 constructor 프로퍼티를 통해 생성자 함수와 연결된다. me에는 constructor 프로퍼티가 없지만 Person.prototype으로부터 상속받아 사용한 것이다.

프로토타입 체인

function Ultra() {}
Ultra.prototype.ultraProp = true;

function Super() {}
Super.prototype = new Ultra();
Super.prototype.superProp = false;
Super.prototype.ultraProp = 2;

function Sub() {}
Sub.prototype = new Super();

let o = new Sub();
console.log(o);	// Ultra {}
console.log(o.ultraProp);	// 2
o.ultraProp = 1;
console.log(o.ultraProp);	// 1
console.log(o.superProp);	// false

console.log(Ultra.prototype);	// { ultraProp: true }
console.log(Super.prototype);	// Ultra { superProp: false, ultraProp: 2 }

자바스크립트 엔진은 자식이 갖고 있지 않으면 갖고 있는 부모가 나올때까지 찾도록 되어 있다.

console.log(o.ultraProp)에서 o라는 객체는 사실 ultraProp을 갖고 있지 않지만 Sub를 상속받고 있고 Sub의 프로토타입의 constructor는 Super 생성자함수를 상속받고 있으며 Super의 프로토타입의 constructor는 Ultra 생성자함수를 상속받고 있으므로 Ultra의 프로토타입이 가진 UltraProp 에 접근해 값을 받아올 수 있다.
원래 Ultra프로토타입의 UltraProp이라는 값의 default는 true이지만 여기까지 가지 않아도 Super의 프로토타입으로 UltraProp에 접근가능하며 이 값은 2로 선언됐으므로 2가 출력된다.
아래도 마찬가지다.

이처럼 상속과 프로퍼티 검색을 위한 메커니즘을 프로토타입 체인이라고 한다.

만일 프로토타입 체인의 종점에서도 프로퍼티 검색이 불가능한 경우 undefined를 출력하며 에러는 발생하지 않는다.

profile
나는 커서 무엇이 되려나

0개의 댓글