Classes (which are explained in the next chapter) are the major new OOP feature in ECMAScript 6. However, it also includes new features for object literals and new utility methods in
Object. This chapter describes them.
Property value shorthands:
Computed property keys:
This new syntax can also be used for method definitions:
The main use case for computed property keys is to make it easy to use symbols as property keys.
The most important new method of
assign(). Traditionally, this functionality was called
Object.assign() only considers own (non-inherited) properties.
In ECMAScript 5, methods are properties whose values are functions:
In ECMAScript 6, methods are still function-valued properties, but there is now a more compact way of defining them:
Getters and setters continue to work as they did in ECMAScript 5 (note how syntactically similar they are to method definitions):
There is also a way to concisely define properties whose values are generator functions:
This code is equivalent to:
Property value shorthands let you abbreviate the definition of a property in an object literal: If the name of the variable that specifies the property value is also the property key then you can omit the key. This looks as follows.
The last line is equivalent to:
Property value shorthands work well together with destructuring:
One use case for property value shorthands are multiple return values (which are explained in the chapter on destructuring).
Remember that there are two ways of specifying a key when you set a property.
obj.foo = true;
obj['b'+'ar'] = 123;
In object literals, you only have option #1 in ECMAScript 5. ECMAScript 6 additionally provides option #2:
This new syntax can also be used for method definitions:
The main use case for computed property keys are symbols: you can define a public symbol and use it as a special property key that is always unique. One prominent example is the symbol stored in
Symbol.iterator. If an object has a method with that key, it becomes iterable: The method must return an iterator, which is used by constructs such as the
for-of loop to iterate over the object. The following code demonstrates how that works.
Line A starts a generator method definition with a computed key (the symbol stored in
Object.assign(target, source_1, source_2, ···)
This method merges the sources into the target: It modifies
target, first copies all enumerable own (non-inherited) properties of
source_1 into it, then all own properties of
source_2, etc. At the end, it returns the target.
Let’s look more closely at how
Object.assign()is aware of both strings and symbols as property keys.
Object.assign()ignores inherited properties and properties that are not enumerable.
const value = source[propKey]). That means that if the source has a getter whose key is
propKeythen it will be invoked. All properties created by
Object.assign()are data properties, it won’t transfer getters to the target.
target[propKey] = value). That means that if the target has a setter whose key is
propKeythen it will be invoked with
This is how you’d copy all properties (not just enumerable ones), while correctly transferring getters and setters, without invoking setters on the target:
Object.assign()doesn’t work well for moving methods
On one hand, you can’t move a method that uses
super: Such a method has the internal slot
[[HomeObject]] that ties it to the object it was created in. If you move it via
Object.assign(), it will continue to refer to the super-properties of the original object. Details are explained in a section in the chapter on classes.
On the other hand, enumerability is wrong if you move methods created by an object literal into the prototype of a class. The former methods are all enumerable (otherwise
Object.assign() wouldn’t see them, anyway), but the prototype only has non-enumerable methods by default.
Let’s look at a few use cases.
You can use
Object.assign() to add properties to
this in a constructor:
Object.assign() is also useful for filling in defaults for missing properties. In the following example, we have an object
DEFAULTS with default values for properties and an object
options with data.
In line A, we created a fresh object, copied the defaults into it and then copied
options into it, overriding the defaults.
Object.assign() returns the result of these operations, which we assign to
Another use case is adding methods to objects:
You could also manually assign functions, but then you don’t have the nice method definition syntax and need to mention
SomeClass.prototype each time:
One last use case for
Object.assign() is a quick way of cloning objects:
This way of cloning is also somewhat dirty, because it doesn’t preserve the property attributes of
orig. If that is what you need, you have to use property descriptors.
If you want the clone to have the same prototype as the original, you can use
Object.getOwnPropertySymbols(obj) retrieves all own (non-inherited) symbol-valued property keys of
obj. It complements
Object.getOwnPropertyNames(), which retrieves all string-valued own property keys. Consult a later section for more details on traversing properties.
The strict equals operator (
===) treats two values differently than one might expect.
NaN is not equal to itself.
That is unfortunate, because it often prevents us from detecting
Doing this is normally a good thing.
Object.is() provides a way of comparing values that is a bit more precise than
===. It works as follows:
Everything else is compared as with
Object.is()to find Array elements
If we combine
Object.is() with the new ES6 Array method
findIndex(), we can find
NaN in Arrays:
indexOf() does not handle
This method sets the prototype of
proto. The non-standard way of doing so in ECMAScript 5, that is supported by many engines, is via assigning to the special property
__proto__. The recommended way of setting the prototype remains the same as in ECMAScript 5: during the creation of an object, via
Object.create(). That will always be faster than first creating an object and then setting its prototype. Obviously, it doesn’t work if you want to change the prototype of an existing object.
In ECMAScript 6, the key of a property can be either a string or a symbol. The following are five operations that traverse the property keys of an object
Object.keys(obj) : Array<string>
Object.getOwnPropertyNames(obj) : Array<string>
Object.getOwnPropertySymbols(obj) : Array<symbol>
Reflect.ownKeys(obj) : Array<string|symbol>
for (const key in obj)
ES6 defines two traversal orders for properties.
Own Property Keys:
Enumerable Own Names:
The order in which
for-in traverses properties is not defined. Quoting Allen Wirfs-Brock:
for-inorder was not defined and there has been variation among browser implementations in the order they produce (and other specifics). ES5 added
Object.keysand the requirement that it should order the keys identically to
for-in. During development of both ES5 and ES6, the possibility of defining a specific
for-inorder was considered but not adopted because of web legacy compatibility concerns and uncertainty about the willingness of browsers to make changes in the ordering they currently produce.
Many engines treat integer indices specially, even though they are still strings (at least as far as the ES6 spec is concerned). Therefore, it makes sense to treat them as a separate category of keys.
Roughly, an integer index is a string that, if converted to a 53-bit non-negative integer and back is the same value. Therefore:
'2'are integer indices.
'02'is not an integer index. Converting it to an integer and back results in the different string
'3.141'is not an integer index, because 3.141 is not an integer.
In ES6, instances of
The following code demonstrates the traversal order “Own Property Keys”:
'10'are integer indices, come first and are sorted numerically (not in the order in which they were added).
'01'are normal string keys, come next and appear in the order in which they were added to
Symbol('second')are symbols and come last, in the order in which they were added to
Answer by Tab Atkins Jr.:
Because, for objects at least, all implementations used approximately the same order (matching the current spec), and lots of code was inadvertently written that depended on that ordering, and would break if you enumerated it in a different order. Since browsers have to implement this particular ordering to be web-compatible, it was specced as a requirement.
There was some discussion about breaking from this in Maps/Sets, but doing so would require us to specify an order that is impossible for code to depend on; in other words, we’d have to mandate that the ordering be random, not just unspecified. This was deemed too much effort, and creation-order is reasonable valuable (see OrderedDict in Python, for example), so it was decided to have Maps and Sets match Objects.
The following parts of the spec are relevant for this section:
[[OwnPropertyKeys]]is used by
EnumerableOwnNamesis used by
[[Enumerate]]is used by
There are two similar ways of adding a property
prop to an object
obj.prop = 123
Object.defineProperty(obj, 'prop', 123)
There are three cases in which assigning does not create an own property
prop, even if it doesn’t exist, yet:
propexists in the prototype chain. Then the assignment causes a
TypeErrorin strict mode.
propexists in the prototype chain. Then that setter is called.
propwithout a setter exists in the prototype chain. Then a
TypeErroris thrown in strict mode. This case is similar to the first one.
None of these cases prevent
Object.defineProperty() from creating an own property. The next section looks at case #3 in more detail.
If an object
obj inherits a property
prop that is read-only then you can’t assign to that property:
This is similar to how an inherited property works that has a getter, but no setter. It is in line with viewing assignment as changing the value of an inherited property. It does so non-destructively: the original is not modified, but overridden by a newly created own property. Therefore, an inherited read-only property and an inherited setter-less property both prevent changes via assignment. You can, however, force the creation of an own property by defining a property:
__proto__in ECMAScript 6
__proto__prior to ECMAScript 6
[[Prototype]] (which is
p of an object
There is no standard way to change the prototype of an existing object, but you can create a new object
obj that has the given prototype
A long time ago, Firefox got the non-standard property
__proto__. Other browsers eventually copied that feature, due to its popularity.
Prior to ECMAScript 6,
__proto__ worked in obscure ways:
The main reason why
__proto__ became popular was because it enabled the only way to create a subclass
Array in ES5: Array instances were exotic objects that couldn’t be created by ordinary constructors. Therefore, the following trick was used:
Subclassing in ES6 works differently than in ES5 and supports subclassing builtins out of the box.
__proto__is problematic in ES5
The main problem is that
__proto__ mixes two levels: the object level (normal properties, holding data) and the meta level.
If you accidentally use
__proto__ as a normal property (object level!), to store data, you get into trouble, because the two levels clash. The situation is compounded by the fact that you have to abuse objects as maps in ES5, because it has no built-in data structure for that purpose. Maps should be able to hold arbitrary keys, but you can’t use the key
'__proto__' with objects-as-maps.
In theory, one could fix the problem by using a symbol instead of the special name
__proto__, but keeping meta-operations completely separate (as done via
Object.getPrototypeOf()) is the best approach.
__proto__in ECMAScript 6
__proto__ was so widely supported, it was decided that its behavior should be standardized for ECMAScript 6. However, due to its problematic nature, it was added as a deprecated feature. These features reside in Annex B in the ECMAScript specification, which is described as follows:
The ECMAScript language syntax and semantics defined in this annex are required when the ECMAScript host is a web browser. The content of this annex is normative but optional if the ECMAScript host is not a web browser.
In order to explain the magic behind
__proto__, two mechanisms were introduced in ES6:
'__proto__'a special operator for specifying the prototype of the created objects.
ECMAScript 6 enables getting and setting the property
__proto__ via a getter and a setter stored in
Object.prototype. If you were to implement them manually, this is roughly what it would look like:
The getter and the setter for
__proto__ in the ES6 spec:
__proto__as an operator in an object literal
__proto__ appears as an unquoted or quoted property key in an object literal, the prototype of the object created by that literal is set to the property value:
Using the string value
'__proto__' as a computed property key does not change the prototype, it creates an own property:
The special property key
'__proto__' in the ES6 spec:
In ECMAScript 6, if you define the own property
__proto__, no special functionality is triggered and the getter/setter
Object.prototype.__proto__ is overridden:
Object.prototypeas a prototype
__proto__ getter/setter is provided via
Object.prototype. Therefore, an object without
Object.prototype in its prototype chain doesn’t have the getter/setter, either. In the following code,
dict is an example of such an object – it does not have a prototype. As a result,
__proto__ now works like any other property:
__proto__and dict objects
If you want to use an object as a dictionary then it is best if it doesn’t have a prototype. That’s why prototype-less objects are also called dict objects. In ES6, you don’t even have to escape the property key
'__proto__' for dict objects, because it doesn’t trigger any special functionality.
__proto__ as an operator in an object literal lets you create dict objects more concisely:
Note that in ES6, you should normally prefer the built-in data structure
Map to dict objects, especially if keys are not fixed.
__proto__ being a getter/setter in ES6,
JSON.parse() works fine, because it defines properties, it doesn’t assign them (if implemented properly, an older version of V8 did assign).
JSON.stringify() isn’t affected by
__proto__, either, because it only considers own properties. Objects that have an own property whose name is
__proto__ work fine:
Support for ES6-style
__proto__ varies from engine to engine. Consult kangax’ ECMAScript 6 compatibility table for information on the status quo:
The following two sections describe how you can programmatically detect whether an engine supports either of the two kinds of
A simple check for the getter/setter:
A more sophisticated check:
__proto__as an operator in an object literal
You can use the following check:
__proto__is pronounced “dunder proto”
Bracketing names with double underscores is a common practice in Python to avoid name clashes between meta-data (such as
The following pronunciation has been suggested by Ned Batchelder:
An awkward thing about programming in Python: there are lots of double underscores. For example, the standard method names beneath the syntactic sugar have names like
__getattr__, constructors are
__init__, built-in operators can be overloaded with
__add__, and so on. […]
My problem with the double underscore is that it’s hard to say. How do you pronounce
__init__? “underscore underscore init underscore underscore”? “under under init under under”? Just plain “init” seems to leave out something important.
I have a solution: double underscore should be pronounced “dunder”. So
__init__is “dunder init dunder”, or just “dunder init”.
It is nice how well ES6 turns
__proto__ from something obscure into something that is easy to understand.
However, I still recommend not to use it. It is effectively a deprecated feature and not part of the core standard. You can’t rely on it being there for code that must run on all engines.
Object.getPrototypeOf()to get the prototype of an object.
Object.create()to create a new object with a given prototype. Avoid
Object.setPrototypeOf(), which hampers performance on many engines.
__proto__as an operator in an object literal. It is useful for demonstrating prototypal inheritance and for creating dict objects. However, the previously mentioned caveats do apply.
Enumerability is an attribute of object properties. This section explains how it works in ECMAScript 6. Let’s first explore what attributes are.
ECMAScript 6 supports the following attributes (as does ES5):
enumerable: Setting this attribute to
falsehides the property from some operations.
configurable: Setting this attribute to
falseprevents several changes to a property (attributes except
valuecan’t be change, property can’t be deleted, etc.).
value: holds the value of the property.
writable: controls whether the property’s value can be changed.
get: holds the getter (a function).
set: holds the setter (a function).
You can retrieve the attributes of a property via
This section explains how the attribute
for-inloop: traverses the string keys of own and inherited enumerable properties.
Object.keys(): returns the string keys of enumerable own properties.
JSON.stringify(): only stringifies enumerable own properties with string keys.
Object.assign(): only copies enumerable own properties (both string keys and symbol keys are considered).
for-in is the only built-in operations where enumerability matters for inherited properties. All other operations only work with own properties.
Unfortunately, enumerability is quite an idiosyncratic feature. This section presents several use cases for it and argues that, apart from protecting legacy code from breaking, its usefulness is limited.
for-in loop traverses all enumerable properties of an object, own and inherited ones. Therefore, the attribute
enumerable is used to hide properties that should not be traversed. That was the reason for introducing enumerability in ECMAScript 1.
Non-enumerable properties occur in the following locations in the language:
prototypeproperties of built-in classes are non-enumerable:
prototypeproperties of classes are non-enumerable:
lengthis not enumerable, which means that
for-inonly traverses indices. (However, that can easily change if you add a property via assignment, which is makes it enumerable.)
The main reason for making all of these properties non-enumerable is to hide them (especially the inherited ones) from legacy code that uses the
for-in loop or
$.extend() (and similar operations that copy both inherited and own properties; see next section). Both operations should be avoided in ES6. Hiding them ensures that the legacy code doesn’t break.
When it comes to copying properties, there are two important historical precedents that take enumerability into consideration:
$.extend(target, source1, source2, ···)copies all enumerable own and inherited properties of
source1etc. into own properties of
Problems with this way of copying properties:
The only instance property that is non-enumerable in the standard library is property
length of Arrays. However, that property only needs to be hidden due to it magically updating itself via other properties. You can’t create that kind of magic property for your own objects (short of using a Proxy).
Object.assign(target, source_1, source_2, ···) can be used to merge the sources into the target. All own enumerable properties of the sources are considered (that is, keys can be either strings or symbols).
Object.assign() uses a “get” operation to read a value from a source and a “set” operation to write a value to the target.
With regard to enumerability,
Object.assign() continues the tradition of
$.extend(). Quoting Yehuda Katz:
Object.assign would pave the cowpath of all of the extend() APIs already in circulation. We thought the precedent of not copying enumerable methods in those cases was enough reason for Object.assign to have this behavior.
In other words:
Object.assign() was created with an upgrade path from
$.extend() (and similar) in mind. Its approach is cleaner than
$.extend’s, because it ignores inherited properties.
If you make a property non-enumerable, it can’t by seen by
Object.keys() and the
for-in loop, anymore. With regard to those mechanisms, the property is private.
However, there are several problems with this approach:
JSON.stringify() does not include properties in its output that are non-enumerable. You can therefore use enumerability to determine which own properties should be exported to JSON. This use case is similar to marking properties as private, the previous use case. But it is also different, because this is more about exporting and slightly different considerations apply. For example: Can an object be completely reconstructed from JSON?
An alternative for specifying how an object should be converted to JSON is to use
toJSON() cleaner than enumerability for the current use case. It also gives you more control, because you can export properties that don’t exist on the object.
In general, a shorter name means that only enumerable properties are considered:
Object.keys()ignores non-enumerable properties
Object.getOwnPropertyNames()lists all property names
Reflect.ownKeys() deviates from that rule, it ignores enumerability and returns the keys of all properties. Additionally, starting with ES6, the following distinction is made:
Therefore, a better name for
Object.keys() would now be
It seems to me that enumerability is only suited for hiding properties from the
for-in loop and
$.extend() (and similar operations). Both are legacy features, you should avoid them in new code. As for the other use cases:
toJSON()method is more powerful and explicit than enumerability when it comes to controlling how to convert an object to JSON.
I’m not sure what the best strategy is for enumerability going forward. If, with ES6, we had started to pretend that it didn’t exist (except for making prototype properties non-enumerable so that old code doesn’t break), we might eventually have been able to deprecate enumerability. However,
Object.assign() considering enumerability runs counter that strategy (but it does so for a valid reason, backward compatibility).
In my own ES6 code, I’m not using enumerability, except (implicitly) for classes whose
prototype methods are non-enumerable.
Lastly, when using an interactive command line, I occasionally miss an operation that returns all property keys of an object, not just the own ones (
Reflect.ownKeys). Such an operation would provide a nice overview of the contents of an object.
This section explains how you can customize basic language operations by using the following well-known symbols as property keys:
Ccustomize the behavior of
x instanceof C.
Object.prototype.toString()to compute the default string description of an object
obj: ‘[object ‘+obj[Symbol.toStringTag]+’]’.
C can customize the behavior of the
instanceof operator via a method with the key
Symbol.hasInstance that has the following signature:
x instanceof C works as follows in ES6:
Cis not an object, throw a
C[Symbol.hasInstance](x), coerce the result to boolean and return it.
Cmust be callable,
C.prototypein the prototype chain of
The only method in the standard library that has this key is:
This is the implementation of
instanceof that all functions (including classes) use by default. Quoting the spec:
This property is non-writable and non-configurable to prevent tampering that could be used to globally expose the target function of a bound function.
The tampering is possible because the traditional
instanceof to the target function if it encounters a bound function.
Given that this property is read-only, you can’t use assignment to override it, as mentioned earlier.
As an example, let’s implement an object
ReferenceType whose “instances” are all objects, not just objects that are instances of
Object (and therefore have
Object.prototype in their prototype chains).
Symbol.toPrimitive lets an object customize how it is coerced (converted automatically) to a primitive value.
*) coerces its operands to numbers.
new Date(year, month, date)coerces its parameters to numbers.
parseInt(string , radix)coerces its first parameter to a string.
The following are the most common types that values are coerced to:
truefor truthy values,
falsefor falsy values. Objects are always truthy (even
new Boolean(b), numbers
new Number(n), etc.).
Thus, for numbers and strings, the first step is to ensure that a value is any kind of primitive. That is handled by the spec-internal operation
ToPrimitive(), which has three modes:
The default mode is only used by:
new Date(value)(exactly one parameter!)
If the value is a primitive then
ToPrimitive() is already done. Otherwise, the value is an object
obj, which is converted to a primitive as follows:
obj.valueOf()if it is primitive. Otherwise, return the result of
obj.toString()if it is primitive. Otherwise, throw a
toString()is called first,
This normal algorithm can be overridden by giving an object a method with the following signature:
In the standard library, there are two such methods:
toString()from being called (which throws an exception).
Date.prototype[Symbol.toPrimitive](hint)This method implements behavior that deviates from the default algorithm. Quoting the specification: “Date objects are unique among built-in ECMAScript object in that they treat
'default'as being equivalent to
'string'. All other built-in ECMAScript objects treat
'default'as being equivalent to
The following code demonstrates how coercion affects the object
In ES5 and earlier, each object had the internal own property
[[Class]] whose value hinted at its type. You could not access it directly, but its value was part of the string returned by
Object.prototype.toString(), which is why that method was used for type checks, as an alternative to
In ES6, there is no internal slot
[[Class]], anymore, and using
Object.prototype.toString() for type checks is discouraged. In order to ensure the backwards-compatibility of that method, the public property with the key
Symbol.toStringTag was introduced. You could say that it replaces
Object.prototype.toString() now works as follows:
thisto an object
'[object ' + tst + ']'.
The default values for various kinds of objects are shown in the following table.
|An Array object||
|A string object||
|An error object||
|A boolean object||
|A number object||
|A date object||
|A regular expression object||
Most of the checks in the left column are performed by looking at internal slots. For example, if an object has the internal slot
[[Call]], it is callable.
The following interaction demonstrates the default toString tags.
If an object has an (own or inherited) property whose key is
Symbol.toStringTag then its value overrides the default toString tag. For example:
Instances of user-defined classes get the default toString tag (of objects):
One option for overriding the default is via a getter:
All of the built-in properties whose keys are
Symbol.toStringTag have the following property descriptor:
As mentioned earlier, you can’t use assignment to override those properties, because they are read-only.
Symbol.unscopables lets an object hide some properties from the
The reason for doing so is that it allows TC39 to add new methods to
Array.prototype without breaking old code. Note that current code rarely uses
with, which is forbidden in strict mode and therefore ES6 modules (which are implicitly in strict mode).
Why would adding methods to
Array.prototype break code that uses
with (such as the widely deployed Ext JS 4.2.1)? Take a look at the following code. The existence of a property
foo(), if you call it with an Array:
with statement, all properties of
values become local variables, shadowing even
values itself. Therefore, if
values has a property
values then the statement in line * logs
values.values.length and not
Symbol.unscopables is used only once in the standard library:
superin object literals?
Yes you can! Details are explained in the chapter on classes.