以函数式的方式修改对象的属性

3

在JavaScript编程中,以函数式方式编程是一个巨大的优势。我正在尝试以函数式方式修改包含在对象数组中的对象的属性,这意味着传递给map函数的对象项不能被修改。如果我这样做:

const modObjects = objects.map((item) => {

   item.foo = "foo" + 3;

   return item;
});

这段代码出现问题是因为在函数内部修改了item,导致该功能无法正常使用。你知道其他解决这个问题的方法吗?


你正在改变原始对象。map 方法是错误的。 - Nina Scholz
复制item,更改属性,返回副本。 - thefourtheye
5个回答

5

一种新的(ES6)方式,非常不可变且符合函数式编程的精神:

// A. Map function that sets obj[prop] to a fixed value
const propSet = prop => value => obj => ({...obj, [prop]: value})

// B. Map function that modifies obj.foo2 only if it exists
const foo2Modify = obj =>
    obj.hasOwnProperty('foo2') ? {...obj, foo2: 'foo ' + obj.foo2} : obj

// Usage examples of A and B
const initialData = [{'foo': 'one'}, {'foo2': 'two'}, {'foo3': 'three'}]
const newData1 = initialData.map(propSet('foo2')('bar')) // Set value
const newData2 = initialData.map(foo2Modify) // Use a modify function 
console.log(initialData) // Initial data should not change
console.log(newData1) // Each object should contain the new fixed foo2
console.log(newData2) // Modify foo2 only if it exists in initial data


我在哪里可以找到关于第一行语法的文档:{...obj, [prop]: value}?我对方括号感到困惑。 - Michael Dorst
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax - Dimitrios Tsalkakis

1
你可以使用Object.assign来创建一个item对象的副本,并从map回调函数中返回该副本。

Object.assign()

Object.assign()方法用于将一个或多个源对象的所有可枚举自有属性的值复制到目标对象中,它将返回目标对象。

下面是一个例子:

let data = [
  {"foo": "one"},
  {"foo": "two"},
  {"foo": "three"}
]

let newData = data.map( item => {
  let itemCopy = Object.assign({}, item);
  itemCopy.foo = "foo " + item.foo;
  return itemCopy;
})

console.log(data)
console.log(newData)


如果data中有对可变对象的引用,那么newData的引用透明性将会被破坏。 - etherealite

1
在现代JavaScript中,您可以在对象字面量内部使用扩展运算符处理对象中的对象:
const modObjects = objects.map(
  item => ({...item, foo: item.foo + 3})
)

注意对象字面量周围的括号(),它们需要消除lambda中代码块{}与对象字面量{}的歧义。另一种方法是使用带有return的代码块:
const modObjects = objects.map(
  item => { return {...item, foo: item.foo + 3} }
)

1
你也可以像这样做:

const modObjects = objects.map((item) => {

   return { ...objects, foo: "foo" + 3; };

});

这段代码:objects.map((item) => { ...destSchema, foo: "foo" + 3; });无法正常工作的原因是为了让JS解释器明白它是一个作用域还是一个对象而特意这样设计的。你必须使用return

1
或者你可以在大括号周围添加圆括号(...),这样至少有一种替代方案。 - shitpoet

0
我扩展了@Dimitrios Tsalkakis的答案,以使用回调函数更改属性。
示例:https://repl.it/@robie2011/ts-set-property-functional TypeScript:
function mapProperty<T>(prop: string){
    return (cb: (propValue: any) => any) => (obj: any) => ({...obj, [prop]: cb(obj[prop])}) as (T)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接