writable
: When true
, can update the value.enumerable
: When true
, is visited in for in
loop.configurable
: When true
, ok to delete
and to modify these three attributes.true
in object literals or dynamically added properties, false
in calls to defineProperty
/defineProperties
.defineProperty
to set the attributes:
Object.defineProperty(james, 'id', { value: '007', enumerable: true, writable: false, configurable: true })
defineProperty
get
and set
attributes hold getter/setter functions.Object.defineProperties(james, { id: { ... }, age: { ... }})
const sym = Symbol('label')
sym !== Symbol('label')
typeof sym
is 'symbol'
obj[sym] = value {[sym]: value}
Symbol.iterator
, Symbol.species
, ...Symbol.for('com.horstmann.label')
Object.getOwnPropertyDescriptor(harry, 'id') → { value: 42, enumerable: true, writable: false, configurable: true }
obj.hasOwnProperty(stringOrSymbol)
is true
if obj
has a property with the given key.
obj[stringOrSymbol]
is undefined
, the property might be absent or have the value undefined
.obj.propertyIsEnumerable(stringOrSymbol)
is true
if obj
has an enumerable property with the given key.Object.keys(obj)
, Object.values(obj)
, Object.entries(obj)
[key, value]
pairs.Object.getOwnPropertyNames(obj)
, Object.getOwnPropertySymbols(obj)
Object.getOwnPropertyDescriptors(obj)
gets all property descriptors.Object.create(prototype, propertyDescriptors)
creates an object with the given prototype and properties.Object.fromEntries
constructs an object from an iterable of key/value pair arrays:
let james = Object.fromEntries([['name', 'James Bond'], ['id', '007']])
Object.assign(target, ...source)
copies all enumerable own properties from the sources into the target and returns the updated target:
james = Object.assign(james, { salary: 300000}, { buddy: 'Harry Smith'})
Object.preventExtensions(obj)
: Can't add properties.Object.seal(obj)
: Can't add or delete properties, can't configure existing properties. Object.freeze(obj)
: In addition, can't set properties, can't change prototype.return Object.freeze({ ... })
Object.isExtensible(obj)
, Object.isSealed(obj)
, Object.isFrozen(obj)
Object.create(proto)
creates a new object and sets its [[Prototype]] to proto
.Object.getPrototypeOf(obj)
gets the [[Prototype]].proto.isPrototypeOf(obj)
is true
if proto
is in the prototype chain of obj
.
obj instanceof C
is the same as C.prototype.isPrototypeOf(obj)
Object.setPrototypeOf(obj, proto)
updates the prototype.
Object.create
method.prototype
property.{ constructor: function }
when function is defined.
constructor
property not enumerable.obj = new function
, the [[Prototype]] of obj
is set to function.prototype
. constructor
Property, new.target
class
syntax, subclasses set constructor
manually.Object.getPrototypeOf(obj)
safer than obj.constructor.prototype
Object.create(null)
does not have constructor
.constructor
obj = new function
, new.target
is set to function
new
(new.target === undefined
)new.target === function
) Object
MethodstoString
called whenever an object must be turned into a string:
'Hello ' + obj `Hello ${obj}` String(obj)
obj.toString()
returns [object Constructor name]
toString
to produce a better description.toLocaleString
calls toString
, can be overridden to produce a locale-specific description.valueOf
if you want to produce a primitive type value that is used in arithmetic expressions.
Date
overrides valueOf
to return the number of milliseconds since the epoch:
new Date(2525,0,1) - new Date(2000,0,1) → 16567459200000
Object.is(x, y)
is almost the same as x === y
, except that Object.is(+0, -0)
is false
and Object.is(NaN, NaN)
is true
.name
of a Function
object is the name with which the function was defined.length
is the number of arguments (not counting a rest argument)bind
lets you “curry” a function by specifying its initial arguments:
function multiply(x, y) { return x * y } const double = multiply.bind(null, 2) double(42) → 84
this
const isPet = Array.prototype.includes.bind(['cat', 'dog', 'fish'])
// Same as x => ['cat', 'dog', 'fish'].includes(x)
this
in a callback to another method of the same class:
button.onclick = this.handleClick.bind(this)
button.onclick = (...args) => this.handleClick(...args)
call
and apply
invoke functions and methods programmatically.f(args)
or obj.f(args)
?call
calls a method with a different this
:
while (Array.prototype.includes.call('aeiou', str[i])) i++ // Can't call'aeiou'.includes(str[i])
—includes
isn't defined for strings!
apply
is like call
, but the arguments other than this
are in an array.const proxy = new Proxy(obj, handler)
: Operations on obj
are trapped by the handler.{ get: (target, key, receiver) => { ... }, set: (target, key, value, receiver) => { ... } ... }
target
is obj
, receiver
is the object whose property was accessed (proxy
unless the proxy is in the prototype chain).const logHandler = { ... set: (target, key, value, receiver) => { console.log(`set '${key}' to ${value}`) target[key] = value } }
const values = new Proxy([1, 2, 3], logHandler)
values[2] = 4 // Console logs set '2' to 4
Proxy.revocable(obj, handler)
returns object with proxy
object and revoke
function:
const mySomewhatSensitiveObject = ...
const p = Proxy.revocable(mySomewhatSensitiveObject, {})
doSomethingWith(p.proxy)
p.revoke()
// p.proxy
is no longer usable
get(target, key, receiver) |
receiver[key] , receiver.key |
set(target, key, value, receiver) |
receiver[key] = value , receiver.key = value |
deleteProperty(target, property) |
delete proxy[key] , delete proxy.key |
has(target, key) |
key in target |
getPrototypeOf(target) |
Object.getPrototypeOf(proxy) |
setPrototypeOf(target, proto) |
Object.setPrototypeOf(proxy, proto) |
isExtensible(target) |
Object.isExtensible(proxy) |
preventExtensions(target) |
Object.preventExtensions(proxy) |
getOwnPropertyDescriptor(target, key) |
Object.getOwnPropertyDescriptor(proxy, key) |
ownKeys(target) |
Object.keys(proxy) (also calling getOwnPropertyDescriptor ), Object.getOwnPropertyProperty (Names |Symbols )(proxy) |
defineProperty(target, key, descriptor) |
Object.defineProperty(proxy, key, descriptor) |
apply(target, thisArg, args) |
thisArg.proxy(...args) , proxy(...args) , proxy.apply(thisArg, args) , proxy.call(thisArg, ...args) |
construct(target, args, newTarget) |
new proxy(args) , or invocation through super |
Reflect
implements the twelve trap operations.const logHandler = { ... set: (target, key, value, receiver) => { console.log(`set '${key}' to ${value}`) Reflect.set(target, key, value, receiver) } }
const logHandler = new Proxy({}, { get(target, trapKey, receiver) { return (...args) => { console.log(`${trapKey} ${args}`) return Reflect[trapKey](...args); } } })
Reflect
operations are more convenient than their explicit analogs.
Reflect.deleteProperty
returns a boolean
to tell whether deletion was successful, delete
operator doesn't.Reflect.defineProperty
returns a boolean
, Object.defineProperty
throws an exception upon failure.Reflect.apply(f, thisArg, args)
calls Function.apply
, f.apply(thisArg, args)
might not (if f.apply
was redefined).construct
must return an object.getOwnPropertyDescriptor
must return a descriptor object or undefined
getPrototypeOf
must return an object or null
get
operation of a proxy must yield the actual value.TypeError
is thrown.has
cannot hide it.getPrototypeOf
operation must yield the actual prototype, and has
an getOwnPropertyDescriptor
must report the actual properties.toString
, Object.toString
returns '[object Object]'
Object.toString
by defining a property with key Symbol.toStringTag
class Employee { get [Symbol.toStringTag]() { return 'Employee' } }
Object.toString
yields '[object Employee]'
get [Symbol.toStringTag]() { return this.constructor.name }
get [Symbol.toStringTag]() { return JSON.stringify(this) }
toString
/valueOf
:
class Percent { constructor(rate) { this.rate = rate } toString() { return `${this.rate}%` } valueOf() { return this.rate * 0.01 } }
'' + new Percent(99.44) → '0.9944'
'99.44%'
?Symbol.toPrimitive
[Symbol.toPrimitive](hint) { if (hint === 'number') return this.rate * 0.01 else return `${this.rate}%` }
'number'
with arithmetic other than +
and comparisons'string'
with ` ${...} `
or String(...)
'default'
with +
or ==
Array
method map
produces the same collection that it received:
class MyArray extends Array {}
let myValues = new MyArray(1, 2, 7, 9)
myValues.map(x => x * x) // Yields a MyArray
class Range extends Array
Symbol.species
key:
class Range extends Array { static get [Symbol.species]() { return Array } ... }
Symbol.iterator
, Symbol.asyncIterator
—see the lessons on Iterators/Asynchronous Programminginstanceof
with Symbol.hasInstance
class Iterable { static [Symbol.hasInstance](obj) { return Symbol.iterator in obj } } [1, 2, 3] instanceof Iterable
Symbol.isConcatSpreadable
is used in the concat
method of Array
const a = [1, 2] const b = [3, 4] a[Symbol.isConcatSpreadable] = false [].concat(a, b) ⇒ [[1, 2], 3, 4]
Symbol.match
, Symbol.replace
, Symbol.search
, Symbol.split
are called from the String
methods with the same name.