9. Object.getOwnPropertyDescriptors()
Table of contents
Please support this book: buy it (PDF, EPUB, MOBI) or donate
(Ad, please don’t block.)

9. Object.getOwnPropertyDescriptors()

This chapter explains the ECMAScript 2017 feature “Object.getOwnPropertyDescriptors()” by Jordan Harband and Andrea Giammarchi.

9.1 Overview

Object.getOwnPropertyDescriptors(obj) returns the property descriptors of all own properties of obj, in an Array:

const obj = {
    [Symbol('foo')]: 123,
    get bar() { return 'abc' },
};
console.log(Object.getOwnPropertyDescriptors(obj));

// Output:
// { [Symbol('foo')]:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

9.2 Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors(obj) accepts an object obj and returns an object result:

Property descriptors describe the attributes of a property (its value, whether it is writable, etc.). For more information, consult Sect. “Property Attributes and Property Descriptors” in “Speaking JavaScript”.

This is an example of using Object.getOwnPropertyDescriptors():

const obj = {
    [Symbol('foo')]: 123,
    get bar() { return 'abc' },
};
console.log(Object.getOwnPropertyDescriptors(obj));

// Output:
// { [Symbol('foo')]:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

This is how you would implement Object.getOwnPropertyDescriptors():

function getOwnPropertyDescriptors(obj) {
    const result = {};
    for (let key of Reflect.ownKeys(obj)) {
        result[key] = Object.getOwnPropertyDescriptor(obj, key);
    }
    return result;
}

9.3 Use cases for Object.getOwnPropertyDescriptors()

9.3.1 Use case: copying properties into an object

Since ES6, JavaScript already has a tool method for copying properties: Object.assign(). However, this method uses simple get and set operations to copy a property whose key is key:

const value = source[key]; // get
target[key] = value; // set

That means that it doesn’t properly copy properties with non-default attributes (getters, setters, non-writable properties, etc.). The following example illustrates this limitation. The object source has a setter whose key is foo:

const source = {
    set foo(value) {
        console.log(value);
    }
};
console.log(Object.getOwnPropertyDescriptor(source, 'foo'));
// { get: undefined,
//   set: [Function: foo],
//   enumerable: true,
//   configurable: true }

Using Object.assign() to copy property foo to object target fails:

const target1 = {};
Object.assign(target1, source);
console.log(Object.getOwnPropertyDescriptor(target1, 'foo'));
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

Fortunately, using Object.getOwnPropertyDescriptors() together with Object.defineProperties() works:

const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
console.log(Object.getOwnPropertyDescriptor(target2, 'foo'));
// { get: undefined,
//   set: [Function: foo],
//   enumerable: true,
//   configurable: true }

9.3.2 Use case: cloning objects

Shallow cloning is similar to copying properties, which is why Object.getOwnPropertyDescriptors() is a good choice here, too.

This time, we use Object.create() that has two parameters:

const clone = Object.create(Object.getPrototypeOf(obj),
    Object.getOwnPropertyDescriptors(obj));

9.3.3 Use case: cross-platform object literals with arbitrary prototypes

The syntactically nicest way of using an object literal to create an object with an arbitrary prototype prot is to use the special property __proto__:

const obj = {
    __proto__: prot,
    foo: 123,
};

Alas, that feature is only guaranteed to be there in browsers. The common work-around is Object.create() and assignment:

const obj = Object.create(prot);
obj.foo = 123;

But you can also use Object.getOwnPropertyDescriptors():

const obj = Object.create(
    prot,
    Object.getOwnPropertyDescriptors({
        foo: 123,
    })
);

Another alternative is Object.assign():

const obj = Object.assign(
    Object.create(prot),
    {
        foo: 123,
    }
);

9.4 Pitfall: copying methods that use super

A method that uses super is firmly connected with its home object (the object it is stored in). There is currently no way to copy or move such a method to a different object.

Next: 10. Trailing commas in function parameter lists and calls