通过引用替换 JS 对象 [重构工作代码]
Replace JS object by reference [refactor working code]
tl;dr 工作代码在最下面,能不能写得更优雅点。
我正在构建一个 metalsmith(静态站点生成器)插件。 Metalsmith 插件总是采用以下形式:
const myPlugin = options => (files, metalsmith, done) => {
// Mutate `files` or `metalsmith` in place.
done()
}
我以函数式(不可变)风格(使用 Ramda.js)编写了我的插件,并希望用新值完全覆盖 files
。以下是 概念上 我想要的,但不会工作,因为它正在将 files
重新分配给 updated
而不是在堆上操作 files
对象.
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
files = updated
done()
}
我已经实现了想要的功能,有下面的,但是看起来不够优雅。
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
deleteMissingKeys(old, updated)
Object.assign(old, updated)
done()
}
const deleteMissingKeys = (old, updated) => {
Object.keys(old).forEach(key => {
if (!updated.hasOwnProperty(key)) {
delete old[key]
}
})
}
是否有更好的方法来实现这些目标?
在JavaScript中没有超级优雅的方法来做到这一点,但这并不是一件坏事。事实上,well-written JavaScript 不需要这样的行为。 JavaScript中没有"pass-by-reference",所以像你这样的尝试自然是不雅的。
图书馆是否需要这样的行为,而不是试图计算出 "hack" 到 pass-by-reference,还有更多的 "javascriptonic" 方法可以做到这一点,那就是传递所需对象的包装器对象:
// instead of trying to use a "hack" to pass-by-reference
var myObj = { /* ... */ };
function myFunc(obj) {
// your hack here to modify obj, since
// obj = { /* ... */ }
// won't work, of course
}
myFunc(myObj);
// you should use a wrapper object
var myWrapper = {
myObj: { /* ... */ }
}
function myFunc(wrapper) {
wrapper.myObj = { /* ... */ };
}
myFunc(myWrapper);
我强烈建议你重新考虑一下你当初为什么真的要这样做。
但如果你坚持,你的解决方案还不错,我喜欢你如何使用 Object.assign()
而不是笨拙的 for 循环来添加字段。
不过,我要补充一点,根据具体情况,您可能还想将对象的原型设置为预期值(例如,如果旧对象是一个 Date 实例,而您想让它成为一个普通的对象,你当然需要调用 Object.setPrototypeOf(old, Object.prototype)
).
tl;dr 工作代码在最下面,能不能写得更优雅点。
我正在构建一个 metalsmith(静态站点生成器)插件。 Metalsmith 插件总是采用以下形式:
const myPlugin = options => (files, metalsmith, done) => {
// Mutate `files` or `metalsmith` in place.
done()
}
我以函数式(不可变)风格(使用 Ramda.js)编写了我的插件,并希望用新值完全覆盖 files
。以下是 概念上 我想要的,但不会工作,因为它正在将 files
重新分配给 updated
而不是在堆上操作 files
对象.
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
files = updated
done()
}
我已经实现了想要的功能,有下面的,但是看起来不够优雅。
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
deleteMissingKeys(old, updated)
Object.assign(old, updated)
done()
}
const deleteMissingKeys = (old, updated) => {
Object.keys(old).forEach(key => {
if (!updated.hasOwnProperty(key)) {
delete old[key]
}
})
}
是否有更好的方法来实现这些目标?
在JavaScript中没有超级优雅的方法来做到这一点,但这并不是一件坏事。事实上,well-written JavaScript 不需要这样的行为。 JavaScript中没有"pass-by-reference",所以像你这样的尝试自然是不雅的。
图书馆是否需要这样的行为,而不是试图计算出 "hack" 到 pass-by-reference,还有更多的 "javascriptonic" 方法可以做到这一点,那就是传递所需对象的包装器对象:
// instead of trying to use a "hack" to pass-by-reference
var myObj = { /* ... */ };
function myFunc(obj) {
// your hack here to modify obj, since
// obj = { /* ... */ }
// won't work, of course
}
myFunc(myObj);
// you should use a wrapper object
var myWrapper = {
myObj: { /* ... */ }
}
function myFunc(wrapper) {
wrapper.myObj = { /* ... */ };
}
myFunc(myWrapper);
我强烈建议你重新考虑一下你当初为什么真的要这样做。
但如果你坚持,你的解决方案还不错,我喜欢你如何使用 Object.assign()
而不是笨拙的 for 循环来添加字段。
不过,我要补充一点,根据具体情况,您可能还想将对象的原型设置为预期值(例如,如果旧对象是一个 Date 实例,而您想让它成为一个普通的对象,你当然需要调用 Object.setPrototypeOf(old, Object.prototype)
).