【问题标题】:Trouble merging object with getters / setters无法将对象与 getter / setter 合并
【发布时间】:2018-10-10 14:30:05
【问题描述】:

我在将两个对象与属性 getter/setter 合并时遇到了一些问题,这些属性会降级为值。如何预防?我必须手动克隆它吗?

下面是我的 pb 的人为示例:

function Factory1() {
  const p = {};

  return {
    get prop1 () { return p.prop; },
    set prop1 (value) { p.prop = value; }
  };
}

function Factory2() {
  const p = {};

  return {
    get prop2 () { return p.prop; },
    set prop2 (value) { p.prop = value; }
  }
}


const obj1 = Factory1();
const obj2 = Factory2();

const obj3a = {...Factory1(), ...Factory2()};
const obj3b = Object.assign(Factory1(), Factory2());

console.log('obj1.prop1 =>', Object.getOwnPropertyDescriptor(obj1, 'prop1'));
console.log('obj2.prop2 =>', Object.getOwnPropertyDescriptor(obj2, 'prop2'));

console.log('spread prop1 =>', Object.getOwnPropertyDescriptor(obj3a, 'prop1'));
console.log('spread prop2 =>', Object.getOwnPropertyDescriptor(obj3a, 'prop2'));
console.log('assign prop1 =>', Object.getOwnPropertyDescriptor(obj3b, 'prop1'));
console.log('assign prop2 =>', Object.getOwnPropertyDescriptor(obj3b, 'prop2'));

2018 年 10 月 11 日更新

一个更简单的例子:

function Factory1() {
  return {
    get prop1 () { return 42; }
  };
}

const obj1 = Factory1();
const obj2 = Object.assign({}, Factory1())

console.log('obj1.prop1 =>', Object.getOwnPropertyDescriptor(obj1, 'prop1'));
// result
// obj1.prop1 => { get: [Function],
//   set: undefined,
//   enumerable: true,
//   configurable: true }

console.log('obj2.prop1 =>', Object.getOwnPropertyDescriptor(obj2, 'prop1'));
// result
// obj2.prop1 => { value: 42,
//   writable: true,
//   enumerable: true,
//   configurable: true }


一个更真实的例子

根据要求,我将尝试说明我所面临的代码类型。当然,这是在多个模块中调度的代码提取的串联。

import axios from 'axios';

function createAxios (options = {}) {
  const axiosClient = axios.create(options);
  axiosClient.defaults.withCredentials = false;
  axiosClient.defaults.headers.common['Accept'] = 'text/html';

  return {axiosClient};
}

function WithQuery ({axiosClient}) {
  // This is a sample server that supports CORS.
  var sampleUrl = 'http://html5rocks-cors.s3-website-us-east-1.amazonaws.com/index.html';  

  return {
    query (url) {
      return axiosClient.get(url || sampleUrl);
    }
  };
}

function WithLastModified (context) {
  let lastModified;

  context.axiosClient.interceptors.response.use(function (response) {
    const {headers} = response;
    console.log('header last-modified:', headers['last-modified']);
    lastModified = headers['last-modified'];
    return response;
  });

  return {
    getLastModified () {
      return lastModified;
    },
    get lastModified () {
      return lastModified;
    }
  }
}

function build (options) {
  const httpClient = createAxios(options);
  return Object.assign({},
    httpClient,
    {search: WithQuery(httpClient)},
    WithLastModified(httpClient));
}

const api = build();
api.search.query().then(() => {
  console.log('get lastModified:', api.lastModified); // not working, always undefined
  console.log('getLastModified:', api.getLastModified()); // work
});

这种类型的工厂方法用于注入依赖项并通过伪混合向最终对象添加功能。

【问题讨论】:

  • 您的用例到底是什么?如果您不想共享私有 p 对象,那么是的,您将不得不手动克隆方法。
  • 可能的解决方法here

标签: javascript


【解决方案1】:

如果您检查您的代码,您会发现即使获取描述符并将它们添加到新对象,您的访问器也将拥有对 factoryN 内部作用域的 p 对象的访问权(这些访问器形成闭包)。

也就是说,即使您将新的描述符定义到新的对象实例中,您也会改变相同的目标对象。

结论:如果你不重新表述你的问题来解释你的实际目标是什么,我会说没有办法解决这个问题。

回答一些 OP 的评论

我不确定我是否理解您关于在 2 个不同闭包上定义的不同局部变量 p 的陈述。我关心的是 obj1.prop1 和 obj2.prop2 get / set 变成了 obj3a 上的简单值

获取您自己的代码:

function Factory1() {
  // This is a function scoped reference
  const p = {};

  return {
    get prop1 () { return p.prop; }, 
    set prop1 (value) { p.prop = value; }
  };
}

p 是一个函数范围的引用,所有返回的访问器都是捕获p 的闭包。

如果您使用这些访问器并将它们添加到另一个对象,p 仍将指向捕获的引用。因此,当您在多个对象中设置prop1 时,所有对象都会设置相同的目标p 对象!

【讨论】:

  • 我不确定我是否理解您关于定义在 2 个不同闭包上的不同局部变量 p 的声明。我关心的是obj1.prop1obj2.prop2 get / set 在obj3a 上变成简单的value
  • 好的,谢谢你的澄清,我明白你的意思。但是,我的示例代码是一个人为的示例,我关心的是合并对象时的 get/set 处理。
  • @Sté 这不是一个人为的例子,它似乎是一个坏例子。我建议你应该完全改写这个问题来解释你的实际用例。如果 人为的示例 模仿了您的实际情况,那么就没有解决方案。不管怎样,我怀疑复制访问器可能是个好主意。
  • @Sté 关于行为,没有什么神秘之处:克隆对象时,您不会自动复制访问器,除非您使用Object.getOwnPropertyDescriptors 实现一些自定义功能来这样做...但是您会发现麻烦使用访问器,因为它们可能是闭包,正如我已经在我的回答中向您展示的那样......
  • 谢谢,我已经更新了我的示例。您的意思是您不鼓励将对象与访问器合并,不是吗?那么,如何将多个mixins组合成一个可用的对象,可以分享一些技巧吗?
猜你喜欢
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
  • 2023-03-29
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多