通过引用替换 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)).