array.prototype.map(在 RxJS 管道中)返回副本或操作原始对象之间的区别
Difference between returning a copy or manipulating original objects in array.prototype.map (In RxJS pipe)
我正在开发一个 Angular 9,RxJS 6 应用程序,并且有一个关于管道主题值的不同结果和在该管道中进行单位转换的问题。
请看this stackblitz。在 backend.service.ts
文件中,创建了一个可观察对象,它执行一些 "unit conversion" 和 returns 发送给 _commodities Subject 的所有事情。如果您查看 convertCommodityUnits
函数,请注意我注释掉了工作示例,而是采用了我最初解决它的方法。
我的问题:当您使用屏幕上的取消订阅按钮并再次订阅时,当使用 "conversion solution" 只是覆盖对象而不进行复制时,HTML 中的值被转换多次,因此管道不使用主题提供的原始数据。如果您使用其他代码,那么在 convertCommodityUnits
中创建商品对象的克隆,它会像预期的那样工作。
现在,我不明白为什么这两种转换数据的方式表现如此不同。我得到一个直接操作数据,因为 js Call by sharing 和一个 returns 一个新对象。但是传递给 convertCommodityUnits
函数的对象是由 array.prototype.map
函数创建的,所以它不应该覆盖任何东西,对吧?我希望 RxJS 使用发送给主题的原始、最后数据传递给 pipe/map 运算符,但示例中似乎并非如此,对吗?
How/Why这里的值是不是转换了多次?
这或多或少是关于此的后续问题:,因此设置相同。
当您使用 map
时,您获得了数组的新引用。 但是您没有在新生成的数组中获得新对象(数组的浅表副本),因此您正在改变元素内的数据。
在解构解决方案中,因为数组中的每个对象只有原始类型,所以每次转换方法是时,您都会为数组生成全新的元素调用(这很重要:不仅是一个新数组,还有数组中的新元素=>你已经执行了数组的深度复制)。所以你不会连续累积每个订阅中的值。
这并不意味着您在提供的 stackblitz 演示中使用的 1 级解构解决方案在所有情况下都适用。我已经看到这个错误在那里犯了很多,特别是在需要你不改变存储数据的 redux 模式框架中,比如 ngrx、ngxs 等。如果你的数组中有复杂的对象,1 级解构会'我们保持数组每个元素中的所有嵌入对象不变。我认为用例子来描述这种行为更容易:
const obj1 = {a: 1};
const array = [{b: 2, obj: obj1}];
// after every newArray assignment in the below code,
// console.log(newArray === array) prints false to the console
let newArray = [...array];
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => item);
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => ({...item}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // true
newArray = array.map(item => ({
...item,
obj: {...item.obj}
}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // false
我正在开发一个 Angular 9,RxJS 6 应用程序,并且有一个关于管道主题值的不同结果和在该管道中进行单位转换的问题。
请看this stackblitz。在 backend.service.ts
文件中,创建了一个可观察对象,它执行一些 "unit conversion" 和 returns 发送给 _commodities Subject 的所有事情。如果您查看 convertCommodityUnits
函数,请注意我注释掉了工作示例,而是采用了我最初解决它的方法。
我的问题:当您使用屏幕上的取消订阅按钮并再次订阅时,当使用 "conversion solution" 只是覆盖对象而不进行复制时,HTML 中的值被转换多次,因此管道不使用主题提供的原始数据。如果您使用其他代码,那么在 convertCommodityUnits
中创建商品对象的克隆,它会像预期的那样工作。
现在,我不明白为什么这两种转换数据的方式表现如此不同。我得到一个直接操作数据,因为 js Call by sharing 和一个 returns 一个新对象。但是传递给 convertCommodityUnits
函数的对象是由 array.prototype.map
函数创建的,所以它不应该覆盖任何东西,对吧?我希望 RxJS 使用发送给主题的原始、最后数据传递给 pipe/map 运算符,但示例中似乎并非如此,对吗?
How/Why这里的值是不是转换了多次?
这或多或少是关于此的后续问题:
当您使用 map
时,您获得了数组的新引用。 但是您没有在新生成的数组中获得新对象(数组的浅表副本),因此您正在改变元素内的数据。
在解构解决方案中,因为数组中的每个对象只有原始类型,所以每次转换方法是时,您都会为数组生成全新的元素调用(这很重要:不仅是一个新数组,还有数组中的新元素=>你已经执行了数组的深度复制)。所以你不会连续累积每个订阅中的值。
这并不意味着您在提供的 stackblitz 演示中使用的 1 级解构解决方案在所有情况下都适用。我已经看到这个错误在那里犯了很多,特别是在需要你不改变存储数据的 redux 模式框架中,比如 ngrx、ngxs 等。如果你的数组中有复杂的对象,1 级解构会'我们保持数组每个元素中的所有嵌入对象不变。我认为用例子来描述这种行为更容易:
const obj1 = {a: 1};
const array = [{b: 2, obj: obj1}];
// after every newArray assignment in the below code,
// console.log(newArray === array) prints false to the console
let newArray = [...array];
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => item);
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => ({...item}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // true
newArray = array.map(item => ({
...item,
obj: {...item.obj}
}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // false