4 * Establish some immutable descriptor Shapes with our preferred defaults.
5 * We want normally-behaving properties unless told otherwise.
7 function defaultShape(proto
, ...objs
) {
8 const newObject
= Object
.create(proto
);
9 Object
.assign(newObject
, ...objs
);
13 const defaultDataDescriptor
= defaultShape(null, {
20 const defaultAccessorDescriptor
= defaultShape(null, {
28 * Create a new descriptor which inherits our preferred defaults.
29 * @param {Object} defaultDescriptor
30 * @param {...any} objs
33 function descriptorFromDefault(defaultDescriptor
, ...objs
) {
34 return Object
.assign(Object
.create(defaultDescriptor
), ...objs
);
38 * Defer calling initializer to set value for name until name is read.
40 * @param {String} name
41 * @param {() => {*}} initializer
42 * @param {Object=} descriptor
43 * @param {Boolean} descriptor.configurable
44 * @param {Boolean} descriptor.enumerable
45 * @param {Boolean} descriptor.writable
47 function lazy(obj
, name
, initializer
, descriptor
) {
48 if (typeof initializer
!== 'function') {
49 throw new TypeError('initialize is not callable');
52 // Establish the descriptor for the eventually-initialized property.
53 const finalDescriptor
= descriptorFromDefault(defaultDataDescriptor
, descriptor
);
55 // The descriptor defining the interim accessor property.
56 const lazyDescriptor
= descriptorFromDefault(defaultAccessorDescriptor
, {
57 // The accessor replaces itself with the value returned by invoking the initializer.
59 finalDescriptor
.value
= initializer
.apply(obj
);
60 Object
.defineProperty(obj
, name
, finalDescriptor
);
61 return finalDescriptor
.value
;
64 // An explicit set overrides the lazy descriptor with a default data property.
65 set: function (value
) {
66 Object
.defineProperty(obj
, name
, descriptorFromDefault(defaultDataDescriptor
, {
71 configurable: true, // Ensure we can replace ourself.
72 enumerable: finalDescriptor
.enumerable
, // Use requested visibility.
75 Object
.defineProperty(obj
, name
, lazyDescriptor
);