赋值ーー重写嵌套属性

我有一个这样的对象 a:

const a = {
user: {
…
groups: […]
…
}
}

a.user中有更多的属性

我只想改变 a.user.groups的值,如果我这样做:

const b = Object.assign({}, a, {
user: {
groups: {}
}
});

除了 b.user.groupsb没有任何其他属性,所有其他属性都被删除。是否有任何 ES6方法只改变嵌套的属性,而不失去所有其他的,与 Object.assign

75955 次浏览

你可以这样改变,

const b = Object.assign({}, a, {
user: Object.assign({}, a.user, {
groups: {}
})
});

经过一番努力,我终于找到了一个看起来非常不错的解决方案:

const b = Object.assign({}, a, {
user: {
...a.user,
groups: 'some changed value'
}
});

为了让这个答案更加完整,这里有一个小小的提示:

const b = Object.assign({}, a)

基本上等同于:

const b = { ...a }

因为它只是将 a(...a)的所有属性复制到一个新的 Object 中:

 const b = {
...a,          //copy everything from a
user: {        //override the user property
...a.user,  //same sane: copy the everything from a.user
groups: 'some changes value'  //override a.user.group
}
}

分配()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

方法将通过从源对象复制来创建一个新对象,如果您稍后在源对象中更改任何方法,这个问题将不会反映到新对象中。

使用 create ()

Create ()方法使用指定的原型对象和属性创建一个新对象。

const b = Object.create(a)
b.user.groups = {}
// if you don't want the prototype link add this
// b.prototype = Object.prototype

这样你就可以通过原型把 b 链接到 a 上,如果你在 a 上做了任何改变,它就会反映在 b 上,而 b 上的任何改变都不会影响 a

您特别要求使用 Object.sign 实现 ES6方式,但也许其他人会更喜欢我的更“一般”的答案——您可以使用 loash 轻松实现这一点,而且我个人认为这个解决方案更具可读性。

import * as _ from 'lodash';
_.set(a, 'user.groups', newGroupsValue);

它会改变对象。

菲利普斯的小调第一个回答。

const object1 = {
abc: {
a: 1
}
};


const object2 = {
abc: {
b: 2
}
};


Object.assign(object1, {
abc: {
...object1.abc,
...object2.abc
}
});


console.log(object1);
// Object { abc: Object { a: 1, b: 2 } }

下面是更一般的解决方案。它仅在存在冲突(相同的键名)时才进行递归赋值。它解决了分配复杂对象的问题,这些对象有许多参考点。 例如,nested-object-assign,一个在 npm 中用于嵌套对象分配的现有包,坚持使用复杂对象分配,因为它递归地遍历所有键。

var objecttarget = {
a: 'somestring',
b: {x:2,y:{k:1,l:1},z:{j:3,l:4}},
c: false,
targetonly: true
};


var objectsource = {
a: 'somestring else',
b: {k:1,x:2,y:{k:2,p:1},z:{}},
c: true,
sourceonly: true
};


function nestedassign(target, source) {
Object.keys(source).forEach(sourcekey=>{
if (Object.keys(source).find(targetkey=>targetkey===sourcekey) !== undefined && typeof source[sourcekey] === "object") {
target[sourcekey]=nestedassign(target[sourcekey],source[sourcekey]);
} else {
target[sourcekey]=source[sourcekey];
}
});
return target;
}


// It will lose objecttarget.b.y.l and objecttarget.b.z
console.log(Object.assign(Object.create(objecttarget),objectsource));


// Improved object assign
console.log(nestedassign(Object.create(objecttarget),objectsource));

下面是一个名为 Object_assign的小函数(如果需要嵌套赋值,只需用 _替换 .)

该函数通过直接粘贴源值来设置所有目标值,或者当目标值和源值都是非 null对象时再次递归调用 Object_assign来设置所有目标值。

const target = {
a: { x: 0 },
b: { y: { m: 0, n: 1      } },
c: { z: { i: 0, j: 1      } },
d: null
}


const source1 = {
a: {},
b: { y: {       n: 0      } },
e: null
}


const source2 = {
c: { z: {            k: 2 } },
d: {}
}


function Object_assign (target, ...sources) {
sources.forEach(source => {
Object.keys(source).forEach(key => {
const s_val = source[key]
const t_val = target[key]
target[key] = t_val && s_val && typeof t_val === 'object' && typeof s_val === 'object'
? Object_assign(t_val, s_val)
: s_val
})
})
return target
}


console.log(Object_assign(Object.create(target), source1, source2))

一个工作版本,其中 支持嵌套数组以及

const target = {
var1: "...",
var2: {
nestVar1: "...",
nestVar2: [...]
}
}


const source = {
var2: {
nestVar3: { ... }
nestVar4: "...",
nestVar5: [...]
},
var3: "...",
var4: [...]
}

功能:

function objectAssign(target, source) {
Object.keys(source).forEach(sourceKey => {
if(Object.keys(source).find(targetKey => targetKey === sourceKey) !== undefined && typeof source[sourceKey] === "object") {
target[sourceKey] = objectAssign(target[sourceKey], source[sourceKey]);
} else if(!Array.isArray(source)) {
target[sourceKey] = source[sourceKey];
} else {
target = source;
}
});
return target;
}

那么

let newObj = objectAssign({}, target, source);

这里是我的 NestedObjectAssign ()贡献,因为我看到一个函数在不同的文章之间被复制,它不能很好地工作。

这个版本正如预期的那样工作,如下例所示:

function NestedObjectAssign(target, source)
{
Object.keys(source).forEach(sourceKey =>
{
if (typeof source[sourceKey] === "object")
{
target[sourceKey] = NestedObjectAssign(target[sourceKey] || {}, source[sourceKey])
}
else
{
target[sourceKey] = source[sourceKey]
}
})


return target
}


var target =
{
stringTarget: "target",
stringCommon: "targetCommon",
nestedCommon:
{
target: 1,
common: 1,
nestedTarget:
{
target: 1,
common: 1
}
}
}


var source =
{
stringSource: "source",
stringCommon: "sourceCommon",
nestedCommon:
{
source: 2,
common: 3,
nestedSource:
{
source: 2,
common: 3
}
}
}


var t = NestedObjectAssign({}, target)
var s = NestedObjectAssign({}, source)
var targetSource = NestedObjectAssign(t, s)


console.log({ target })
console.log({ source })
console.log({ targetSource })