Copy // default values
const defaults = {x: 0, y: 0, z: 0};
// ❌ wrong way:
// -------------------------------------------------------------------
const p = {x: 1};
Object.assign(p, defaults); // overwrites p with defaults ❌
// ✅ right way
// -------------------------------------------------------------------
let q = {x: 2};
// 1. create new object 2. copy defaults 3. overwrite defaults with q
// ╰───────────── ↓ ─╯ ╰ ↓ ────────────╯ ╰─────────────────────────╯
q = Object.assign({}, defaults, q); // <─────┘
// ⭐ use ... spread operator
let r = {x: 3};
r = {...defaults, ...r};
p // { x: 0, y: 0, z: 0 } <-- ❌ p been overwritten
q // { x: 2, y: 0, z: 0 } <-- ✅ q overwrites defaults
r // { x: 3, y: 0, z: 0 } <-- ⭐ excellent!
Copy // ⭐ import
const _Object = require('./ext/Object_ext.js'); // extend Object.prototype
// ---------------------------------------------------------------------------
// ⭐ Object.assign(target, ...sources)
// ----------------------------------------------
// • only copies own enumerable properties
//
// case source target
// ----------------------------------------------
// • 1: data -> getter // ⛔ [TypeError]
// • 2: setter -> getter // ⛔ [TypeError]
// • 3: setter -> data // ✅ OK (`undefined` is copied)
// • 4: non-config -> configurable // ✅ writable/enumerable/configurable all true by default
// ⭐ targets
const t1 = {
get age() { return 18 }, // getter (accessor property)
};
const t2 = { age: 18 }; // data property
const t3 = {}; // empty object
// ⭐ sources
// s1: source with setter (only) property
const s1 = {
set age(v) {}, // setter (only)
}
// s2: source with non-configurable (enumerable) property
const s2 = {};
// define `s2.x`: own enumerable (data) property
s2.defineProperty('x', {
value: 1, enumerable: true // non-configurable by default
});
// ⭐ log
;[
// • 1: data -> getter
`t1.assign({age: 6})`, // ⛔ [TypeError]
// Cannot set property 'age' of #<Object> which has only a getter
// • 2: setter -> getter
`t1.assign(s1)`, // ⛔ [TypeError]
// Cannot set property 'age' of #<Object> which has only a getter
// • 3: setter -> data
`t2.assign(s1)`, // ✅ { age: undefined }
// • 4: non-configurable -> configurable/writable/enumerable (new) property
`t3.assign(s2)`, // ✅ { x: 1 }
`t3.propertyDescriptor('x')`,
// { value: 1, writable: true, enumerable: true, configurable: true }
].forEach(exprStr => {
try { log(eval(exprStr)) } catch(e) { logError(e) }
});
// log error
function logError(e) {
log(`⛔ [${e.constructor.name}]`, e.message);
}