자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어로 원시 타입 값을 제외한 나머지 값(함수, 배열, 정규 표현식 등)은 모두 객체임.
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getArea = function() {
return Math.PI * this.radius * 2;
};
}
// 반지름이 1인 인스턴스
const circle1 = new Circle(1);
// 반지름이 2인 인스턴스
const circle2 = new Circle(2);
// 인스턴스마다 동일 동작의 메서드가 중복 생성됨
console.log(circle1.getArea === circle2.getArea); // false
console.log(circle1.getArea()); // 3.141592653589793
console.log(circle2.getArea()); // 12.566370614359172
getArea
메서드를 중복 생성→ 메모리 낭비 & 퍼포먼스 악영향.// 생성자 함수
function Circle(radius) {
this.radius = radius;
}
// 모든 인스턴스가 프로토타입의 getArea 메서드를 공유
Circle.prototype.getArea = function() {
return Math.PI * this.radius * 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); // true
console.log(circle1.getArea()); // 3.141592653589793
console.log(circle2.getArea()); // 12.566370614359172
프로토타입 객체는 객체 간 상속을 구현하기 위해 사용.
[[Prototype]]
을 가지며, 이 값은 “프로토타입”의 참조를 담고 있음. Object.prototype
이 프로토타입이 됨.모든 객체는 하나의 프로토타입을 가지며, 모든 프로토타입은 생성자 함수와 연결되어 있음. → 객체와 프로토타입, 생성자 함수는 서로 밀접히 연결되어 존재함.
__proto__
접근자 프로퍼티obj.__proto__
를 통해 자신의 프로토타입([[Prototype]]
)에 간접적으로 접근 가능. (ES6 이후 일부 권장 X) [[Get]]
, [[Set]]
) 역할을 수행하며, 상호 참조 방지 등을 위해 자바스크립트 엔진 내부적으로 제약을 두고 있음. __proto__
가 가리키는 부모 프로토타입을 따라 체인을 거슬러 올라감.prototype
프로퍼티prototype
프로퍼티는 생성자 함수가 만들 인스턴스의 프로토타입을 가리킴. prototype
프로퍼티를 소유하지 않음.모든 객체가 갖는
__proto__
접근자 프로퍼티와, 함수 객체만이 갖는prototype
프로퍼티는 동일한 프로토타입 객체를 가리키지만, 사용 주체가 다름.
구분 | 소유 주체 | 값 | 사용 주체 | 사용 목적 |
---|---|---|---|---|
__proto__ 접근자 프로퍼티 | 모든 객체 | 프로토타입의 참조 | 객체 자신 | 객체가 자신의 프로토타입에 접근하거나 교체할 때 사용 |
prototype 프로퍼티 | 생성자 함수 | 프로토타입의 참조 | 생성자 함수 자체 | 생성자 함수가 인스턴스의 프로토타입을 할당하기 위해 사용 |
constructor
프로퍼티와 생성자 함수constructor
프로퍼티를 가지며, 이는 자신(프로토타입)과 연결된 생성자 함수를 가리킴. // 생성자 함수
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
console.log(me.constructor === Person); // true
me
객체는 직접 constructor
프로퍼티를 갖고 있지 않지만, me.__proto__
(즉, Person.prototype
)의 constructor
프로퍼티를 통해 Person
을 참조함.constructor
프로퍼티가 진짜 객체를 생성한 함수와 항상 일치한다고 단정할 수는 없음. 리터럴 표기법 | 생성자 함수 | 프로토타입 |
---|---|---|
객체 리터럴 | Object | Object.prototype |
함수 리터럴 | Function | Function.prototype |
배열 리터럴 | Array | Array.prototype |
정규 표현식 | RegExp | RegExp.prototype |
결국 리터럴 표기법으로 생성된 객체도 생성자 함수와 연결되어 있고, 그 프로토타입을 상속받게 됨.
console.log(Person.prototype); // { constructor: f }
function Person(name) {
this.name = name;
}
Object
, String
, Number
, Function
, Array
, RegExp
, Date
, Promise
등 빌트인 생성자 함수도 전역 객체가 생성될 때 함께 프로토타입이 미리 준비됨.객체가 생성되기 이전, 생성자 함수와 프로토타입은 이미 존재. 이후 객체를 생성할 때, 그 객체의
[[Prototype]]
에 해당 프로토타입이 연결됨.
OrdinaryObjectCreate
를 통해 생성된다는 공통점이 있음. OrdinaryObjectCreate
가 어떤 프로토타입을 할당할지는 객체 생성 방식이 결정함.Object.prototype이 프로토타입이 됨.
const obj = { x: 1 }; // obj -> Object.prototype 상속 console.log(obj.constructor === Object); // true console.log(obj.hasOwnProperty('x')); // true
Object
생성자 함수로 생성된 객체의 프로토타입Object.prototype이 프로토타입이 됨.
const obj = new Object(); obj.x = 1; console.log(obj.constructor === Object); // true console.log(obj.hasOwnProperty('x')); // true
생성자 함수의
prototype
프로퍼티가 가리키는 객체가 그 인스턴스의 프로토타입이 됨.function Circle(radius) { this.radius = radius; } const circle = new Circle(2); // circle -> Circle.prototype
[[Prototype]]
을 따라 부모 프로토타입에서 찾고, 없으면 그 위를 찾아감. Object.prototype
이며, 여기서도 찾을 수 없으면 undefined
를 반환(오류 X).즉, 프로토타입 체인은 상속 & 프로퍼티 검색을 위한 연결 고리임.
instanceof
연산자객체 instanceof 생성자함수
prototype
객체가 좌변 객체의 프로토타입 체인상에 존재하면 true
, 그렇지 않으면 false
.Math.random()
, Array.isArray()
등. const person = {
name: 'Lee',
address: 'Seoul'
};
in
연산자console.log('name' in person); // true
Object.prototype.hasOwnProperty()
console.log(person.hasOwnProperty('name')); // true
for...in
문hasOwnProperty
검사 필요.const person = {
name: 'Lee',
address: 'Seoul',
__proto__: { age: 20 }
};
for (const key in person) {
if (!person.hasOwnProperty(key)) continue;
console.log(key, person[key]);
}
// name Lee
// address Seoul
Object.keys / Object.values / Object.entries
Object.keys(obj)
→ 키 배열 Object.values(obj)
→ 값 배열 Object.entries(obj)
→ [키, 값]
쌍 배열