19장 프로토타입

niyu·2021년 7월 4일
1
post-thumbnail

18장 함수와 일급 객체에서 함수 객체의 프로퍼티로 prototype__proto__ 프로퍼티를 살펴보았다.

prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리키고, __proto__ 프로퍼티를 통해 프로토타입 객체에 접근할 수 있다.

프로토타입 객체란 대체 무엇이고, 여기서 프로토타입은 무엇을 말하는 걸까? 프로토타입에 대해 한 번 알아보자 🧐

프로토타입과 상속

프로그래밍에서 상속(inheritance)어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.

상속을 통해 불필요하게 중복된 코드를 제거하고 기존의 코드를 재사용할 수 있게 된다. 코드의 재사용은 개발 비용을 현저히 줄일 수 있기에 매우 중요하다.

다음 예제를 보자.

// 생성자 함수
function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  }
};

const circle1 = new Circle(5); // 반지름이 5인 Circle 객체 생성
const circle2 = new Circle(10); // 반지름이 10인 Circle 객체 생성

console.log(circle1.getDiameter === circle2.getDiameter); // false

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

위의 예제는 17장 생성자 함수에 의한 객체 생성에서 다룬 생성자 함수 예제이다.

생성자 함수는 동일한 프로퍼티 구조를 갖는 객체를 여러 개 생성할 때 유용하다. 하지만 자세히 보면, 위의 Circle 생성자 함수에는 한 가지 문제가 있다.

Circle 함수는 radius 프로퍼티와 getDiameter 메서드를 갖는다. radius 프로퍼티 값은 인스턴스마다 다르지만 getDiameter 메서드는 모든 인스턴스가 동일한 메서드를 사용하기 때문에, Circle 함수는 인스턴스를 생성할 때마다 getDiameter 메서드를 중복해서 생성하게 된다.

메서드 중복 생성

이렇게 동일한 메서드를 중복해서 소유하는 것은 메모리를 불필요하게 낭비하게 된다. 이 문제를 상속을 이용해 리펙토링을 할 수 있다.

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

// Circle의 모든 인스턴스가 getDiameter를 공유해서 사용하도록 프로토타입에 추가한다.
Circle.prototype.getDiameter = function () {
  return 2 * this.radius;
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

// Circle의 모든 인스턴스는 하나의 getDiameter 메서드를 공유한다.
console.log(circle1.getDiameter === circle2.getDiameter); // true

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

프로토타입은 Circle 함수의 prototype 프로퍼티에 바인딩되어 있다.

circle1circle2 인스턴스는 자신의 부모 객체 역할을 하는 Circle.prototype 에 대한 모든 프로퍼티와 메서드를 상속받는다.

상속에 의한 메서드 공유

getDiameter 메서드는 단 하나만 생성되어 프로토타입인 Circle.prototype의 메서드로 할당되어 있다. Circle 생성자 함수가 생성하는 모든 인스턴스는 getDiameter 메서드를 상속받아 사용할 수 있다.

즉, radius 프로퍼티만 개별적으로 가지고 있고 내용이 동일한 getDiameter 메서드는 상속을 통해 공유하여 사용하는 것이다.

프로토타입은 어떤 객체의 부모 역할을 하는 객체로서, 객체 간 상속을 구현하기 위해 사용된다.

함수의 __proto__ 프로퍼티

모든 객체는 __proto__ 프로퍼티를 통해 자신의 프로토타입에 간접적으로 접근할 수 있다.

const person = { name: 'Lee' };

위의 코드를 브라우저 콘솔로 출력해보면 다음과 같다.

콘솔에서 출력한 객체 프로퍼티

빨간 박스로 표시한 것이 person 객체의 프로토타입인 Object.prototype이다. 즉, __proto__ 프로퍼티를 통해 Object.prototype에 접근한 결과가 콘솔에 표시된 것이다.

__proto__ 프로퍼티로 프로토타입에 접근하면 내부적으로 __proto__의 getter 함수인 get__proto__가 호출된다. __proto__를 통해 새로운 프로토타입을 할당하면 __proto__의 setter 함수인 set__proto__가 호출된다.

const obj = {};
const parent = { x: 1 };

// get__proto__가 호출되어 obj 객체의 프로토타입을 취득
obj.__proto__;

// set__proto가 호출되어 obj 객체의 프로토타입을 교체
obj.__proto__ = parent;

console.log(obj.x); // 1

setter가 호출된 이후 obj 를 출력하면 다음과 같다.

콘솔에서 출력한 obj의 프로퍼티

다만 __proto__ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다. 모든 객체가 __proto__ 를 사용할 수 있는 것은 아니기 때문이다.

// obj는 프로토타입 체인의 종점이다.
const obj = Object.create(null);

// obj는 Object.__proto__를 상속받을 수 없다.
console.log(obj.__proto__); // undefined

Object.create 메서드에 대한 내용은 아래의 직접 상속 파트에서 확인할 수 있다.

위와 같이 Object.prototype을 상속받지 않는 객체를 만들 수도 있기 때문에 __proto__를 사용할 수 없는 경우가 있다.

그렇기 때문에 __proto__ 프로퍼티 대신 Object.getPrototypeOf 메서드를 사용하고, 프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용할 것을 권장한다.

const obj = {};
const parent = { x: 1 };

// obj 객체의 프로토타입을 취득
Object.getPrototypeOf(obj);

// obj 객체의 프로토타입을 교체
Object.setPrototypeOf(obj, parent);

console.log(obj.x); // 1

Object.getPrototypeOfObject.setPrototypeOf 메서드는 get Object.prototype.__proto__set Object.prototype.__proto__와 같다.

함수의 prototype 프로퍼티

함수 객체만이 가지고 있는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.

여기서 화살표 함수ES6 메서드 축약 표현으로 정의한 메서드는 생성자 함수로서 호출할 수 없는 함수이기 때문에, prototype 프로퍼티를 가지지 않으며 프로토타입도 생성되지 않는다.


// 화살표 함수
const Person = (name) => {
  this.name = name;
};

console.log(Person.hasOwnProperty('prototype')); // false
console.log(Person.prototype); // undefined


const obj = {
  // ES6 메서드 축약 표현으로 정의한 메서드
  foo() {}
}; 

console.log(obj.foo.hasOwnProperty('prototype')); // false
console.log(obj.foo.prototype); // undefined

객체를 생성하지 않는 일반 함수의 prototype 프로퍼티는 아무런 의미가 없다.

📙 프로토타입의 constructor 프로퍼티

모든 프로토타입은 constructor 프로퍼티를 갖는다.

생성자 함수가 생성될 때인 함수 객체가 생성될 때, constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킨다.

// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// me 객체의 생성자 함수는 Person이다.
console.log(me.constructor === Person); // true

프로토타입의 constructor 프로퍼티

Person 생성자 함수가 me 객체를 생성할 때, me 객체는 프로토타입의 constructor 프로퍼티를 통해 생성자 함수와 연결된다.

me 객체에는 constructor 프로퍼티가 없지만 me 객체의 프로토타입인 Person.prototype에는 constructor 프로퍼티가 있다. me 객체는 Person.prototypeconstructor 프로퍼티를 상속받아 사용할 수 있다.

생성자 함수에 의해 생성된 인스턴스는 프로토타입의 constructor 프로퍼티에 의해 생성자 함수와 연결된다.

프로토타입의 생성 시점

모든 객체는 하나의 프로토타입을 가지고, 프로토타입은 생성자 함수가 생성되는 시점에 생성된다.

생성자 함수는 사용자가 정의한 생성자 함수와 자바스크립트가 기본으로 제공하는 빌트인 생성자 함수로 구분할 수 있다.

각 함수에 대한 프로토타입의 생성 시점을 알아보자.

사용자 정의 생성자 함수

console.log(Person.prototype); // { constructor: f }

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

12장의 함수 호이스팅에서 살펴봤듯이, 함수 선언문은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다. 함수 선언문으로 정의된 Person 생성자 함수는 어떤 코드보다 먼저 평가되서 함수 객체가 된다. 이 때 프로토타입도 더불어 생성된다.

Person.prototype의 프로토타입

생성된 프로토타입은 Person 생성자 함수의 prototype 프로퍼티에 바인딩이 된다.

프로토타입은 객체이고 모든 객체는 프로토타입을 가지므로 프로토타입도 자신의 프로토타입을 갖는다. 생성된 프로토타입의 프로토타입은 언제나 Object.prototype이다.

사용자 정의 생성자 함수는 함수 객체가 생성되는 시점에 프로토타입도 더불어 생성되고, 생성된 프로토타입의 프로토타입은 언제나 Object.prototype이다.

빌트인 생성자 함수

Object, String, Number, Function 등과 같은 빌트인 생성자 함수도 함수가 생성되는 시점에 프로토타입이 생성된다. 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성된다.

Object 생성자 함수와 프로토타입

전역 객체는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 생성된다.

프로토타입 체인

프로토타입 체인은 자바스크립트가 상속을 구현하는 매커니즘이다.

자바스크립트는 객체의 프로퍼티에 접근하려고 할 때 해당 프로퍼티가 없으면 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인이라 한다.

// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHello = function () {
  console.log(`Hi! My name is ${this.name}`);
};

const me = new Person('Lee');

// hasOwnProperty는 Object.prototype의 메서드다.
console.log(me.hasOwnProperty('name')); // true

Person에 의해 생성된 me 객체는 Object.prototype의 메서드인 hasOwnProperty를 호출할 수 있다. 이는 me 객체가 Person.prototype 뿐만 아니라 Object.prototype도 상속받았다는 것을 알 수 있다.

프로토타입 체인

me 객체의 프로토타입은 Person.prototype이다. 또한 Person.prototype의 프로토타입은 Object.prototype이다.

프로토타입 체인의 최상위에 위치하는 객체는 언제나 Object.prototype이다. 따라서 모든 객체는 Object.prototype을 상속받는다.

Object.prototype은 프로토타입 체인의 종점이다. Object.prototype의 프로토타입은 null이다.

오버라이딩과 프로퍼티 섀도잉

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('Lee');

// 인스턴스 메서드
me.sayHello = function () {
  console.log(`Hi!, ${this.name}`);
};

// 인스턴스 메서드 호출
me.sayHello(); // Hi!, Lee

생성자 함수로 객체를 생성한 다음, 인스턴스에 동일한 이름의 메서드를 추가했다.

프로토타입의 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 프로토타입 체인을 따라 프로토타입의 프로퍼티를 검색하여 덮어쓰는 것이 아니라, 인스턴스의 프로퍼티로 추가한다.

오버라이딩과 프로퍼티 섀도잉

이때 인스턴스 메서드 sayHello는 프로토타입 메서드 sayHello오버라이딩(overriding) 했고 프로토타입 메서드 sayHello는 가려진다.

상속 관계에 의해 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉(property shadowing) 이라 한다.

이제 추가한 인스턴스 메서드 sayHello를 삭제해보자.

delete me.sayHello;

me.sayHello(); // Hi, My name is Lee

인스턴스 메서드 sayHello가 삭제되고, 프로토타입 메서드가 호출되었다.

만약 프로토타입 메서드 sayHello를 삭제하는 경우에는 하위 객체인 인스턴스를 통해 접근하는 것이 아니라 프로토타입에 직접 접근해서 삭제해야 한다.

delete Person.prototype.sayHello;

me.sayHello(); // TypeError: me.sayHello is not a function 

프로토타입 교체

프로토타입은 임의의 다른 객체로 변경할 수 있다. 이 특징을 활용해 객체 간의 상속 관계를 동적으로 변경할 수 있다.

생성자 함수를 통한 교체

const Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }
  
  // protototype 프로퍼티를 통해 프로토타입 교체
  Person.prototype = {
    sayHello() {
      console.log(`Hi! My name is ${this.name}`);
    }
  };
  
  return Person;
}());

const me = new Person('Lee');

console.log(me.constructor === Person); // false

console.log(me.constructor === Object); // true

Person.prototype에 객체 리터럴을 할당했다. Person 생성자 함수가 생성할 객체의 프로토타입을 객체 리터럴로 교체한 것이다.

프로토타입으로 교체한 객체 리터럴에는 constructor 프로퍼티가 없다. me 객체의 생성자 함수를 검색하면 Person이 아닌 Object가 나오게 된다. 그림으로 표현하면 다음과 같다.

생성자 함수에 의한 프로토타입의 교체

위와 같은 방식으로 프로토타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴된다.

파괴된 constructor 프로퍼티와 생성자 함수 간의 연결을 되살리는 방법은 다음과 같다.

const Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }
  
  // 프로토타입 교체
  Person.prototype = {
    // constructor와 생성자 함수 간의 연결을 설정
    constructor: Person,
    sayHello() {
      console.log(`Hi! My name is ${this.name}`);
    }
  };
  
  return Person;
}());

const me = new Person('Lee');

console.log(me.constructor === Person); // true

console.log(me.constructor === Object); // false

프로토타입으로 교체한 객체 리터럴에 constructor 프로퍼티를 추가하여 프로토타입의 constructor 프로퍼티를 되살린다.

인스턴스를 통한 교체

프로토타입은 앞서 언급한 함수의 __proto__ 프로퍼티를 통해 간접적으로 접근할 수 있다. 또한 Object.getPrototypeOfObject.setPrototypeOf 메서드를 사용해 프로토타입을 교체할 수 있다.

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

const me = new Person('Lee');

// 프로토타입으로 교체할 객체
const parent = {
  sayHello() {
    console.log(`Hi! My name is ${this.name}`);
  }
};

// me 객체의 프로토타입을 parent 객체로 교체
Object.setPrototypeOf(me, parent);
// me.__proto__ = parent; 와 동일

me.sayHello(); // Hi! My name is Lee

// constructor 프로퍼티와 생성자 함수 간의 연결이 파괴된다.
console.log(me.constructor === Person); // false

// 생성자 함수의 prototype 프로퍼티가 교체된 프로토타입을 가리키지 않는다.
console.log(Person.prototype === Object.getPrototypeOf(me)); // false

me 객체의 프로토타입을 parent 객체로 교체했다. 이를 그림으로 나타내면 다음과 같다.

인스턴스에 의한 프로토타입 교체

프로토타입으로 교체한 객체에는 constructor 프로퍼티가 없어 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴될 뿐만 아니라, 생성자함수의 prototype 프로퍼티가 교체된 프로토타입을 가리키지 않는다.

constructor 프로퍼티를 추가하고 생성자 함수의 prototype 프로퍼티를 다시 설정을 해보자.

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

const me = new Person('Lee');

const parent = {
  // constructor 프로퍼티와 생성자 함수 간의 연결 설정
  constructor: Person,
  sayHello() {
    console.log(`Hi! My name is ${this.name}`);
  }
};

// 생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결 설정
Person.prototype = parent;

Object.setPrototypeOf(me, parent);


console.log(me.constructor === Person); // true

console.log(Person.prototype === Object.getPrototypeOf(me)); // true

이처럼 프로토타입 교체를 통해 객체 간의 상속 관계를 동적으로 변경하는 것은 꽤나 번거롭기에 프로토타입은 직접 교체하지 않는 것이 좋다.

상속 관계를 인위적으로 설정하려면 직접 상속이 더 편리하고 안전하다. 또는 ES6에 도입된 클래스를 사용하면 간편하고 직관적으로 상속 관계를 구현할 수 있다.

클래스는 25장에서 살펴보자.

instanceof 연산자

instanceof 연산자는 우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true로 평가되고, 그렇지 않은 경우에는 false로 평가된다.

// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// Person.prototype은 me 객체의 프로토타입 체인 상에 존재
console.log(me instanceof Person); // true

// Object.prototype은 me 객체의 프로토타입 체인 상에 존재
console.log(me instanceof Object); // true

앞서 소개한 프로토타입을 교체했을 때의 예제를 다시 보자.

const Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }
  
  // 프로토타입 교체
  Person.prototype = {
    sayHello() {
      console.log(`Hi! My name is ${this.name}`);
    }
  };
  
  return Person;
}());

const me = new Person('Lee');

console.log(me.constructor === Person); // false

console.log(me instanceof Person); // true!

프로토타입으로 교체한 객체 리터럴에는 constructor 프로퍼티가 없다.

me 객체는 프로토타입이 교체되어 프로토타입과 생성자 함수 간의 연결이 파괴되었음에도 불구하고, me instanceof Person을 실행하면 true가 나오는 것을 알 수 있다.

instanceof 연산자는 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수를 찾는 것이 아니라, 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인 상에 존재하는지 확인한다.

instanceof 연산자

즉, me instanceof Personme 객체의 프로토타입 체인 상에 Person.prototype에 바인딩된 객체가 존재하는지 확인한다.

me instanceof Object의 경우도 마찬가지로 me 객체의 프로토타입 체인 상에 Object.prototype에 바인딩된 객체가 존재하는지 확인한다.

따라서 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴되어도 생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결은 파괴되지 않으므로 instanceof는 아무런 영향을 받지 않는다.

직접 상속

Object.create 메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성한다.

// 프로토타입이 null인 객체 생성
let obj = Object.create(null);
// obj -> null. obj는 프로토타입의 체인의 종점에 위치
console.log(Object.getPrototypeOf(obj) === null); // true


// obj = {}; 와 동일
obj = Object.create(Object.prototype);
// obj -> Object.prototype -> null
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true


const myProto = { x: 10 };
obj = Object.create(myProto);
// obj -> myProto -> Object.prototype -> null
console.log(Object.getPrototypeOf(obj) === myProto); // true


function Person(name) {
  this.name = name;
}
obj = Object.create(Person.prototype);
// obj -> Person.prototype -> Object.prototype -> null
console.log(Object.getPrototypeOf(obj) === Person.portotype); // true

Object.create 메서드를 통해 객체를 생성하면서 직접적으로 상속을 구현할 수 있다.

직접 상속을 통해서 new 연산자 없이, 프로토타입을 지정하면서 객체를 생성할 수 있고, 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.

정적 프로퍼티와 정적 메서드

정적(static) 프로퍼티와 정적 메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조와 호출을 할 수 있는 프로퍼티와 메서드를 말한다.

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

Person 생성자 함수 객체가 소유한 프로퍼티와 메서드를 정적 프로퍼티와 메서드라 한다. 정적 프로퍼티나 메서드는 생성자 함수가 생성한 인스턴스로 참조와 호출을 할 수 없다.

정적 프로퍼티와 메서드

생성자 함수가 생성한 인스턴스는 자신의 프로토타입 체인에 속한 객체의 프로퍼티나 메서드에 접근할 수 있다. 하지만 정적 프로퍼티나 정적 메서드는 인스턴스의 프로토타입 체인에 속한 객체의 프로퍼티나 메서드가 아니기 때문에 인스턴스로 접근할 수 없다.

앞에서 살펴본 Object.create 메서드 또한 Object 생성자 함수의 정적 메서드로서, Object 생성자 함수가 생성한 객체로는 호출할 수 없다.

프로퍼티 존재 확인

in 연산자

in 연산자는 객체 내에 특정 프로퍼티가 존재하는 지 여부를 확인한다.

const person = {
  name: 'Lee',
  address: 'Seoul'
};

console.log('name' in person); // true
console.log('address' in person); // true
console.log('age' in person); //false

in 연산자는 객체의 프로퍼티뿐만 아니라 대상 객체가 상속받은 모든 프로토타입의 프로퍼티를 확인하기 때문에, 주의해서 사용해야 한다.

console.log('toString' in person); // true

toStringObject.prototype의 메서드이다.

hasOwnProperty()

Object.prototype.hasOwnProperty 메서드 또한 객체에 특정 프로퍼티가 존재하는지 확인할 수 있다.

const person = {
  name: 'Lee'
};

console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false

in 연산자와는 다르게, 인수로 전달받는 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true를 반환하고, 상속받은 프로토타입의 프로퍼티 키인 경우에는 false를 반환한다.

console.log(person.hasOwnProperty('toString')); // false

프로퍼티 열거

for ... in 문

for ... in문을 사용해 객체의 모든 프로퍼티를 순회하며 열거할 수 있다.

const person = {
  name: 'Lee',
  address: 'Seoul'
};

for (const key in person) {
  console.log(key + ': ' + person[key]);
}

// name: Lee
// address: Seoul

하지만 for ... in 문은 in 연산자처럼 대상 객체의 프로퍼티뿐만 아니라 상속받은 프로토타입의 프로퍼티까지 열거한다.

const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

for (const key in person) {
  console.log(key + ': ' + person[key]);
}

// name: Lee
// address: Seoul
// age: 20

따라서 Object.prototype.hasOwnProperty 메서드를 사용해서 객체 자신의 프로퍼티인지 확인하는 추가 처리가 필요하다.

for (const key in person) {
  // 객체 자신의 프로퍼티인지 확인
  if (!person.hasOwnProperty(key)) continue;
  console.log(key + ': ' + person[key]);
}

Object.keys/values/entries()

객체 자신의 고유한 프로퍼티만 열거하기 위해서는 for ... in 문을 사용하는 것보다 Object.keys/values/entries 메서드를 사용하는 것을 권장한다.

Object.keys()

객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환한다.

const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

console.log(Object.keys(person)); // ["name", "address"]

Object.values()

객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환한다.

const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

console.log(Object.values(person)); // ["Lee", "Seoul"]

Object.entries()

객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍의 배열을 배열에 담아 반환한다.

const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

console.log(Object.entries(person)); // [["name", "Lee"], ["address", "Seoul"]]

Object.entries(person).forEach(([key, value]) => console.log(key, value));

/*
 name Lee
 address Seoul
*/

0개의 댓글