2014-01-08

ECMAScript 6: merging objects via Object.assign()

Copying all properties of one object to another one is a common operation in JavaScript. This blog post explains ECMAScript 6’s implementation of it, which is called Object.assign().

This merging operation has a name in the JavaScript ecosystem that is only used there (and, unfortunately, clashes with classic OOP terminology): “extend”. Two examples of “extend” being provided by libraries:

Object.assign()

For merging objects, ECMAScript 6 will have the function
    Object.assign(target, source_1, ..., source_n)
This function modifies target and returns it: it first copies all enumerable [1] own operties of source_1 to target, then those of source_2, etc.

Property keys: either strings or symbols

In ECMAScript 6, property keys can be either strings or symbols. The latter are a new kind of unique identifier; there are never any name clashes with symbols as keys. Object.assign() supports both strings and symbols as keys.

Copying versus assignment

Properties in the target object are created via assignment (internal operation [[Put]]). That means that if target has (own or inherited) setters [1], those will be invoked during copying. An alternative would have been to define new properties [2], an operation which always creates new own properties and never invokes setters. There originally was a proposal for a variant of Object.assign() that uses definition instead of assignment. That proposal has been rejected for ECMAScript 6 (but may be reconsidered for later editions).

Use cases for Object.assign()

Setting up instance properties

The task of a constructor is to set up instance properties. Alas, variable names are always mentioned redundantly for this task:
    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    }
My favorite would have been the following syntax to completely remove that redundancy (CoffeeScript and TypeScript both have ways of doing this, but I prefer this syntax):
    class Point {
        constructor(this.x, this.y) {
        }
    }
Object.assign() at least enables you to avoid some of the redundancy:
    class Point {
        constructor(x, y) {
            Object.assign(this, { x, y });
        }
    }
In ECMAScript 6, { x, y } is an abbreviation for { x: x, y: y }.

Adding a method to an object

ECMAScript 5, you use a function expression to add a method to an object:
    MyClass.prototype.foo = function (arg1, arg2) {
        ...
    };
ECMAScript 6 has a more concise syntax for methods [3]. Thanks to Object.assign(), you don’t have to abandon that syntax:
    Object.assign(MyClass.prototype, {
        foo(arg1, arg2) {
            ...
        }
    });

Cloning an object

You can also use Object.assign() to (shallowly) clone an object:
    var copy = Object.assign({ __proto__: obj.__proto__ }, obj);
If you are only interested in an object’s own properties, things become simpler:
    var copy = Object.assign({}, obj);

References

  1. Object properties in JavaScript
  2. Properties in JavaScript: definition versus assignment
  3. Callable entities in ECMAScript 6

3 comments:

Axel Rauschmayer said...

Thanks for excellent differentiation of eval and new Function().
As per your Best practice suggestion, I wanted to use new Function() for dynamically evaluation code instead of eval. But I want the dynamic code to access my surrounding scope.

Since new Function() works as indirect eval(executing in global scope), I think, I have to stick with eval itself (though it is evil)

Axel Rauschmayer said...

Nice.I really really REALLY wished javascript had trully scoped ans safe evals,where one can restrict what object from the global scope an "evaled" string could use.It would be so great! Imagine a totally safe eval feature with which one could allow his users to write bits of js code,without any security risk.

Axel Rauschmayer said...

Are worker totally safe and sandboxed ? IE isnt it possible to access js files from the same domain with workers ? thus load js scripts?