9 * Shorthand helper for building descriptors up from a prototype chain.
11 * @param {...any} objs
14 function createAssign(proto
, ...objs
) {
15 return Object
.assign(Object
.create(proto
), ...objs
);
19 * Defaults common to both data and accessor descriptors.
22 const defaultCommonDescriptor
= createAssign(null, {
28 * Defaults for an eventually-initialized property.
29 * Same as if a property was set normally.
31 const defaultDataDescriptor
= createAssign(defaultCommonDescriptor
, {
37 * Defaults for an interim accessor property.
39 const defaultAccessorDescriptor
= createAssign(defaultCommonDescriptor
, {
45 * Defer calling initializer to set value for name until name is read.
46 * If a lazy property is defined on an object which is used as a prototype,
47 * the objectBound flag determines whether the lazy initializer will be
48 * invoked just once and set the property value on the original prototype
49 * object, or for any inherited object and set the property on that object.
50 * The objectBound flag also controls the 'this' object of the initializer
53 * @param {String} name
54 * @param {() => {*}} initializer
55 * @param {Object=} descriptor
56 * @param {Boolean=} descriptor.configurable
57 * @param {Boolean=} descriptor.enumerable
58 * @param {Boolean=} descriptor.writable
59 * @param {Boolean=} objectBound
61 function lazy(obj
, name
, initializer
, descriptor
, objectBound
= true) {
62 if (typeof initializer
!== 'function') {
63 throw new TypeError('initializer is not callable');
66 // The descriptor for the eventually-initialized property.
67 const finalDescriptor
= createAssign(defaultDataDescriptor
, descriptor
);
69 // The descriptor defining the interim accessor property.
70 const lazyDescriptor
= createAssign(defaultAccessorDescriptor
, {
71 // The accessor replaces itself with the value returned by invoking the initializer.
73 const targetObject
= objectBound
? obj : this;
74 finalDescriptor
.value
= initializer
.apply(targetObject
);
75 Object
.defineProperty(targetObject
, name
, finalDescriptor
);
76 return finalDescriptor
.value
;
80 * Unless a non-writable descriptor was specified, an explicit set will overwrite
81 * the lazy descriptor with a default data property,
83 ...(finalDescriptor
.writable
&& {
84 set: function (value
) {
85 Object
.defineProperty(this, name
, createAssign(defaultDataDescriptor
, {
91 configurable: true, // Ensure we can replace ourself.
92 enumerable: finalDescriptor
.enumerable
, // Use requested visibility.
95 return Object
.defineProperty(obj
, name
, lazyDescriptor
);