
function Circle(radius) {
this.radius = radius;
}
// Circle의 프로토타입에 "getArea" 함수를 선언할 수 있다
Circle.prototype.getArea = function() {
return Math.PI * this.radius ** 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
// 서로 다른 객체라도, 프로토타입으로부터 getArea 메서드를 상속받아 공유하고 있다.
console.log(circle1.getArea == circle2.getArea); // true
console.log(circle1.getArea()); // 3.14..
console.log(circle2.getArea()); // 12.56..
const obj = {};
const parent = { x: 1 };
console.log(obj.x); // undefined
console.log(parent.x); // 1
// 이건 안되는데
obj = parent; // TypeError: Assignment to constant variable.
// 이건 된다
obj.__proto__ = parent; // __proto__를 호출하면, 해당 접근자 프로퍼티의 getter, stter가 호출된다
console.log(obj.x); // 1
console.log(parent.x); // 1
// __proto__는 객체가 가진 프로퍼티가 아니라, prototype의 프로퍼티이다.
const obj = {};
const parent = { x: 1 };
obj.__proto__ = parent;
console.log(obj.x); // 1
console.log(parent.x); // 1
parent.x = 2; // parent만 바꿔본다
console.log(obj.x); // 2
console.log(parent.x); // 2
const parent = {};
const child = {};
child.__proto__ = parent;
parent.__proto__ = child; // TypeError: Cyclic __proto__ value
const obj = Object.create(null);
const parent = { x: 1 };
console.log(obj.__proto__); // undefined, 아예 할당이 안되어 타입조차 없다
console.log(Object.getPrototypeOf(obj)); // null
obj.__proto__ = parent; // setter 호출도 안된다
console.log(obj.x); // undefined
Object.setPrototypeOf(obj, parent); // setPrototypeOf() 함수는 정상 동작한다
console.log(obj.x); // 1
console.log((function() { }).hasOwnProperty('prototype')); // true, 생성자를 가진 함수
console.log(({}).hasOwnProperty('prototype')); // false, 객체
console.log((() => { }).hasOwnProperty('prototype')); // false, 화살표 함수
const obj = {
foo() { } // 메서드 축약 표현
};
console.log(obj.hasOwnProperty('prototype')); // false, obj는 객체
console.log(obj.foo.hasOwnProperty('prototype')); // false
| 구분 | 소유 | 값 | 사용 주체 | 사용 목적 |
|---|---|---|---|---|
| __proto__ 접근자 프로퍼티 | 모든 객체 | 프로토타입의 참조 | 모든 객체 | 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용 |
| prototype 프로퍼티 | constructor | 프로토타입의 참조 | 생성자 함수 | 생성자 함수가 자신이 생성할 객체(인스턴스)의 프로토타입을 할당하기 위해 사용 |
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
console.log(Person.prototype === me.__proto__); // true
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
console.log(me.constructor === Person); // true
// 여러 리터럴 표기법
// 객체 리터럴
const obj = {};
// 함수 리터럴
const add = function(a,b) {return a + b;};
// 배열 리터럴
const arr = [1,2,3];
// 정규 표현식 리터럴
const regexp = /is/ig;
console.log(obj.constructor === Object); // 객체 리터럴로 생성된 obj 객체의 생성자 함수는 Object 생성자 함수
// 그러면... obj는 Object 생성자 함수가 만들었을까? => NoNo
| 리터럴 표기법 | 생성자 함수 | 프로토타입 |
|---|---|---|
| 객체 리터럴 | Object | Object.prototype |
| 함수 리터럴 | Function | Function.prototype |
| 배열 리터럴 | Array | Array.prototype |
| 정규표현식 리터럴 | RegExp | RegExp.prototype |
// Person {}
// constructor: ƒ Person(name)
//__proto__: Object
console.log(Person.prototype);
function Person(name) {
this.name = name;
}
빌트인 생성자 함수 : Object, String, Number, Function, Array, RegExp, Date, Promise 등등
객체의 생성 방법들
- 객체 리터럴
- Object 생성자 함수
- 생성자 함수
- Object.create 메서드
- 클래스
const obj = { x: 1 };
console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty('x')); // true
const obj = new Object();
obj.x = 1;
console.log(obj.constructor === Object); // true, Object가 아닐 수가 있나?
console.log(obj.hasOwnProperty('x')); // true
function Person(name) {
this.name = name;
}
const me = new Person('Ed');
const you = new Person('Lee');
Person.prototype.sayHello = function() {
console.log(`Hi! My name is ${this.name}`);
}
me.sayHello(); // Hi! My name is Ed
you.sayHello(); // Hi! My name is Lee
function Person(name) {
this.name = name;
}
const me = new Person('Ed');
console.log(me.hasOwnProperty('name')); // true
프로토타입의 최상위 객체는 언제나 Object.prototype이다
const Person = (function() {
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hi! My name is ${this.name}`);
}
return Person;
}());
const me = new Person('Ed');
me.sayHello = function() {
console.log(`하이! 마이 네임 이즈 ${this.name}`);
}
me.sayHello(); // 하이! 마이 네임 이즈 Ed
const Person = (function() {
function Person(name) {
this.name = name;
}
Person.prototype = {
// constructor: Person, // 이게 없으면, 함수 연결이 파괴되어 Object와 연결된다
sayHello() {
console.log(`Hi! My name is ${this.name}`);
}
};
return Person;
}());
const me = new Person('Ed');
console.log(me.constructor === Person);
console.log(me.constructor === Object);
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
// 축약 표현이라 constructor가 없다
const parent = {
sayHello() {
console.log(`Hi! My name is ${this.name}`);
}
}
Object.setPrototypeOf(me, parent);
me.sayHello();
// 그런데 parent는 생성자 함수가 없으므로.. Person과의 연결이 파괴된다
console.log(me.constructor === Person); // false
console.log(me.constructor === Object); // true
그런데 이처럼 프로토타입 교체를 동적으로 직접 교체하지는 않는다. 보통은 직접 상속이나 클래스를 사용한다
객체 instanceof 생성자 함수
- 우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
console.log(me instanceof Person); // true
console.log(me instanceof Object); // true
// 프로토타입이 null인 객체를 생성한다. 생성된 객체는 프로토타입 체인의 종점에 위치한다.
// obj → null
let obj = Object.create(null);
console.log(Object.getPrototypeOf(obj) === null); // true
// Object.prototype을 상속받지 못한다.
console.log(obj.toString()); // TypeError: obj.toString is not a function
// obj → Object.prototype → null
// obj = {};와 동일하다.
obj = Object.create(Object.prototype);
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
// obj → Object.prototype → null
// obj = { x: 1 };와 동일하다.
obj = Object.create(Object.prototype, {
x: { value: 1, writable: true, enumerable: true, configurable: true }
});
// 위 코드는 다음과 동일하다.
// obj = Object.create(Object.prototype);
// obj.x = 1;
console.log(obj.x); // 1
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
const myProto = { x: 10 };
// 임의의 객체를 직접 상속받는다.
// obj → myProto → Object.prototype → null
obj = Object.create(myProto);
console.log(obj.x); // 10
console.log(Object.getPrototypeOf(obj) === myProto); // true
// 생성자 함수
function Person(name) {
this.name = name;
}
// obj → Person.prototype → Object.prototype → null
// obj = new Person('Lee')와 동일하다.
obj = Object.create(Person.prototype);
obj.name = 'Lee';
console.log(obj.name); // Lee
console.log(Object.getPrototypeOf(obj) === Person.prototype); // true
const myProto = { x: 10 };
// 객체 리터럴에 의해 객체를 생성하면서 프로토타입을 지정하여 직접 상속받을 수 있다.
const obj = {
y: 20,
// 객체를 직접 상속받는다.
// obj → myProto → Object.prototype → null
__proto__: myProto
};
/* 위 코드는 아래와 동일하다.
const obj = Object.create(myProto, {
y: { value: 20, writable: true, enumerable: true, configurable: true }
});
*/
console.log(obj.x, obj.y); // 10 20
console.log(Object.getPrototypeOf(obj) === myProto); // true
// 생성자 함수
function Person(name) {
this.name = name;
}
// 프로토타입 메서드
Person.prototype.sayHello = function () {
console.log(`Hi! My name is ${this.name}`);
};
// 정적 프로퍼티
Person.staticProp = 'static prop';
// 정적 메서드
Person.staticMethod = function () {
console.log('staticMethod');
};
const me = new Person('Lee');
// 생성자 함수에 추가한 정적 프로퍼티/메서드는 생성자 함수로 참조/호출한다.
Person.staticMethod(); // staticMethod
// 정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.
// 인스턴스로 참조/호출할 수 있는 프로퍼티/메서드는 프로토타입 체인 상에 존재해야 한다.
me.staticMethod(); // TypeError: me.staticMethod is not a function
const person = {
name: 'Lee',
address: 'Seoul'
};
// person 객체에 name 프로퍼티가 존재한다.
console.log('name' in person); // true
// person 객체에 address 프로퍼티가 존재한다.
console.log('address' in person); // true
// person 객체에 age 프로퍼티가 존재하지 않는다.
console.log('age' in person); // false
for ( 변수 선언문 in 객체 ) { ... }
const person = {
name: 'Lee',
address: 'Seoul'
};
// for...in 문의 변수 prop에 person 객체의 프로퍼티 키가 할당된다.
for (const key in person) {
console.log(key + ': ' + person[key]);
}
// name: Lee
// address: Seoul
const person = {
name: 'Lee',
address: 'Seoul',
__proto__: { age: 20 }
};
console.log(Object.keys(person)); // ["name", "address"]
console.log(Object.values(person)); // ["Lee", "Seoul"]
console.log(Object.entries(person)); // [["name", "Lee"], ["address", "Seoul"]]
Object.entries(person).forEach(([key, value]) => console.log(key, value));
/*
name Lee
address Seoul
*/