New Methods

One of the design goals of ECMAScript beginning with ECMAScript 5 was to avoid creating new global functions or methods on Object.prototype, and instead try to find objects on which new methods should be available. As a result, the Object global has received an increasing number of methods when no other objects are more appropriate. ECMAScript 6 introduces a couple new methods on the Object global that are designed to make certain tasks easier.

The Object.is() Method

When you want to compare two values in JavaScript, you’re probably used to using either the equals operator (==) or the identically equals operator (===). Many developers prefer the latter, to avoid type coercion during comparison. But even the identically equals operator isn’t entirely accurate. For example, the values +0 and -0 are considered equal by === even though they are represented differently in the JavaScript engine. Also NaN === NaN returns false, which necessitates using isNaN() to detect NaN properly.

ECMAScript 6 introduces the Object.is() method to make up for the remaining quirks of the identically equals operator. This method accepts two arguments and returns true if the values are equivalent. Two values are considered equivalent when they are of the same type and have the same value. Here are some examples:

  1. console.log(+0 == -0); // true
  2. console.log(+0 === -0); // true
  3. console.log(Object.is(+0, -0)); // false
  4. console.log(NaN == NaN); // false
  5. console.log(NaN === NaN); // false
  6. console.log(Object.is(NaN, NaN)); // true
  7. console.log(5 == 5); // true
  8. console.log(5 == "5"); // true
  9. console.log(5 === 5); // true
  10. console.log(5 === "5"); // false
  11. console.log(Object.is(5, 5)); // true
  12. console.log(Object.is(5, "5")); // false

In many cases, Object.is() works the same as the === operator. The only differences are that +0 and -0 are considered not equivalent and NaN is considered equivalent to NaN. But there’s no need to stop using equality operators altogether. Choose whether to use Object.is() instead of == or === based on how those special cases affect your code.

The Object.assign() Method

Mixins are among the most popular patterns for object composition in JavaScript. In a mixin, one object receives properties and methods from another object. Many JavaScript libraries have a mixin method similar to this:

  1. function mixin(receiver, supplier) {
  2. Object.keys(supplier).forEach(function(key) {
  3. receiver[key] = supplier[key];
  4. });
  5. return receiver;
  6. }

The mixin() function iterates over the own properties of supplier and copies them onto receiver (a shallow copy, where object references are shared when property values are objects). This allows the receiver to gain new properties without inheritance, as in this code:

  1. function EventTarget() { /*...*/ }
  2. EventTarget.prototype = {
  3. constructor: EventTarget,
  4. emit: function() { /*...*/ },
  5. on: function() { /*...*/ }
  6. };
  7. var myObject = {};
  8. mixin(myObject, EventTarget.prototype);
  9. myObject.emit("somethingChanged");

Here, myObject receives behavior from the EventTarget.prototype object. This gives myObject the ability to publish events and subscribe to them using the emit() and on() methods, respectively.

This pattern became popular enough that ECMAScript 6 added the Object.assign() method, which behaves the same way, accepting a receiver and any number of suppliers, and then returning the receiver. The name change from mixin() to assign() reflects the actual operation that occurs. Since the mixin() function uses the assignment operator (=), it cannot copy accessor properties to the receiver as accessor properties. The name Object.assign() was chosen to reflect this distinction.

I> Similar methods in various libraries may have other names for the same basic functionality; popular alternates include the extend() and mix() methods. There was also, briefly, an Object.mixin() method in ECMAScript 6 in addition to the Object.assign() method. The primary difference was that Object.mixin() also copied over accessor properties, but the method was removed due to concerns over the use of super (discussed in the “Easy Prototype Access with Super References” section of this chapter).

You can use Object.assign() anywhere the mixin() function would have been used. Here’s an example:

  1. function EventTarget() { /*...*/ }
  2. EventTarget.prototype = {
  3. constructor: EventTarget,
  4. emit: function() { /*...*/ },
  5. on: function() { /*...*/ }
  6. }
  7. var myObject = {}
  8. Object.assign(myObject, EventTarget.prototype);
  9. myObject.emit("somethingChanged");

The Object.assign() method accepts any number of suppliers, and the receiver receives the properties in the order in which the suppliers are specified. That means the second supplier might overwrite a value from the first supplier on the receiver, which is what happens in this snippet:

  1. var receiver = {};
  2. Object.assign(receiver,
  3. {
  4. type: "js",
  5. name: "file.js"
  6. },
  7. {
  8. type: "css"
  9. }
  10. );
  11. console.log(receiver.type); // "css"
  12. console.log(receiver.name); // "file.js"

The value of receiver.type is "css" because the second supplier overwrote the value of the first.

The Object.assign() method isn’t a big addition to ECMAScript 6, but it does formalize a common function found in many JavaScript libraries.

A> ### Working with Accessor Properties A> A> Keep in mind that Object.assign() doesn’t create accessor properties on the receiver when a supplier has accessor properties. Since Object.assign() uses the assignment operator, an accessor property on a supplier will become a data property on the receiver. For example: A> A> js A> var receiver = {}, A> supplier = { A> get name() { A> return "file.js" A> } A> }; A> A> Object.assign(receiver, supplier); A> A> var descriptor = Object.getOwnPropertyDescriptor(receiver, "name"); A> A> console.log(descriptor.value); // "file.js" A> console.log(descriptor.get); // undefined A> A> A> In this code, the supplier has an accessor property called name. After using the Object.assign() method, receiver.name exists as a data property with a value of "file.js" because supplier.name returned "file.js" when Object.assign() was called.