
Object.create 메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성한다.
다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate를 호출한다.Object.create 메서드는 첫 번째 매개변수에 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다.
// 프로토타입이 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.creat(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('Hyeongyu')와 동일하다.
obj = Object.create(Person.prototype);
obj.name = 'Hyeongyu';
console.log(obj.name); // Hyeongyu
console.log(Object.getPrototypeOf(obj) === Person.prototype); // true
Object.create 메서드는 첫 번째 매개변수에 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다
객체를 생성하면서 직접적으로 상속을 구현하는 것이다.
- Object.create 메서드의 장점
- new 연산자 없이 객체를 생성할 수 있다.
- 프로토타입을 지정하면서 객체를 생성할 수 있다.
- 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.
// 프로토타입이 null인 객체를 생성한다.
const obj = Object.create(null);
obj.a = 1;
// console.log(obj.hasOwnProperty('a'));
// TypeError: obj.hasOwnProperty is not a function
// Object.prototype의 빌트인 메서드는 객체로 직접 호출하지 않고 간접적으로 호출하는 것이 좋다.
console.log(Object.prototype.hasOwnProperty.call(obj, 'a')); // true
프로토타입 체인의 종점에 위치하는 객체는 Object.prototype의 빌트인 메서드를 사용할 수 없기 때문에 객체로 직접 호출하지 않고 간접적으로 호출하는 것이 좋다.
객체 리터럴 내부에서 __proto__ 접근자 프로퍼티를 사용하여 직접 상속을 깔끔하게 구현할 수 있다.
const myProto = { x: 10 };
// 객체 리터럴에 의해 객체를 생성하면서 프로토타입을 지정하여 직접 상속받을 수 있다.
const obj = {
y: 20,
// 객체를 직접 상속받는다.
// obj -> myProto -> Object.prototype -> null
__proto__ : myProto
};
/* 위 코드랑 아래 코드는 동일하게 동작한다.
const ojb = 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;
}
// 프로토타입 메서드
Persopn.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('Hyeongyu');
// 생성자 함수에 추가한 정적 프로퍼티/메서드는 생성자 함수로 참조/호출한다.
Person.staticMethod(); // staticMethod
// 정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.
// 인스턴스로 참조/호출할 수 있는 프로퍼티/메서드는 프로토타입 체인 상에 존재해야 한다.
me.staticMethod(); // TypeError: me.staticMethod is not a function
Person 생성자 함수는 객체이므로 자기 자신의 프로퍼티/메서드를 소유할 수 있다.
Person 생성자 함수 객체가 소유한 프로퍼티/메서드는 따로 인스턴스를 생성하지 않고 참조/호출할 수 있는 프로퍼티/메서드이기 때문에 정적 프로퍼티/메서드라고 한다.정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.
function Foo() {}
// 프로토타입 메서드
// this를 참조하지 않는 프로토타입 메서드는 정적 메서드로 변경하여도 동일한 효과를 얻을 수 있다.
Foo.prototype.x = function () {
console.log('x');
};
const foo = new Foo();
// 프로토타입 메서드를 호출하려면 인스턴스를 생성해야 한다.
foo.x(); // x
// 정적 메서드
Foo.x = function () {
console.log('x');
};
// 정적 메서드는 인스턴스를 생성하지 않아도 호출 가능하다.
Foo.x(); // x
key in object
위 예제는 in 연산자의 사용법이다.
key: 프로퍼티 키를 나타내는 문자열
object: 객체로 평가되는 표현식
const person = {
name: 'Hyeongyu',
address: 'Seoul'
};
console.log('name' in person); // true
console.log('address' in person); // true
console.log('age' in person); // false
console.log('toString' in person); // true
in 연산자는 person 객체가 속한 프로토타입 체인 상에 존재하는 모든 프로토타입에서 프로퍼티를 검색하기때문에 주의해야 한다.
toString은 Object.prototype의 메서드이므로 'toString' in person은 true를 반환한다.
const person = { name : 'Hyeongyu' };
console.log(Reflect.has(person, 'Hyeongyu')); // true
console.log(Reflect.has(person, 'toString')); // true
in 연산자 대신 Reflect.has 메서드도 사용가능하다.
Reflect.has 메서드는 in 연산자와 동일하게 동작한다.
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false
console.log(person.hasOwnProperty('toString')); // false
Object.prototype.hasOwnProperty 메서드로도 객체에 특정 프로퍼티가 존재하는지 확인 가능하다. Object.prototype.hasOwnProperty 메서드는 인수로 전달받은 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true를 반환하고 상속받은 프로토타입의 프로퍼티 키인 경우 false를 반환한다.
객체의 모든 프로퍼티를 순회하며 열거하려면 for...in 문을 사용하면 된다.
for (변수선언문 in 객체) {...}
const person = {
name: 'Hyeongyu',
address: 'Seoul'
};
// for...in 문의 변수 prop에 person 객체의 프로퍼티 키가 할당된다.
for (const key in person) {
console.log(key + ':' + person[key]);
}
// name: Hyeongyu
// address: Seoul
Object.prototype.toString 프로퍼티의 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false이기 때문에 열거되지 않는다.
프로퍼티 어트리뷰트 [[Enumerable]]은 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖는다.
**for ... in 문은 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어뷰트 [[Enumerable]]의 값이 true인 프로퍼티를 순회하며 열거한다.
const person = {
name: 'Hyeongyu',
address: 'Seoul',
__proto__: { age: 23 }
};
console.log(Object.keys(person)); // ["name", "address"]
console.log(Object.values(person)); // ["Hyeongyu" , "Seoul"]
console.log(Object.entries(person)); // [["name", "Hyeongyu"], ["address", "Seoul"]]
Object.keys 메서드는 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환한다.
Object.values 메서드는 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환한다.
Object.entries 메서드는 객체 자신의 열거 가능한 프로퍼티 값의 쌍의 배열을 배열에 담아 반환한다.