最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why Object.assign({}, ...) with an empty object literal when other objects are also passed in? - Stack Overflow

programmeradmin3浏览0评论

I'm reading an introduction to Redux reducers () which contains the following example of a reducer:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          pleted: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            pleted: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

It seems from its documentation () that Object.assign() will 'merge together' all the objects passed into it. In this case, however, todo and {pleted: true} are already objects, so I don't see the point of passing an empty object literal, {}, as the first argument to Object.assign(). Can anybody clarify this?

I'm reading an introduction to Redux reducers (https://redux.js/introduction/three-principles) which contains the following example of a reducer:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          pleted: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            pleted: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

It seems from its documentation (https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) that Object.assign() will 'merge together' all the objects passed into it. In this case, however, todo and {pleted: true} are already objects, so I don't see the point of passing an empty object literal, {}, as the first argument to Object.assign(). Can anybody clarify this?

Share Improve this question asked Jul 13, 2018 at 21:37 Kurt PeekKurt Peek 57.6k102 gold badges345 silver badges564 bronze badges 1
  • 1 Having absolutely no knowledge of Redux, I assume it's to avoid mutating the original objects and to return a new object. – juanpa.arrivillaga Commented Jul 13, 2018 at 21:41
Add a ment  | 

4 Answers 4

Reset to default 9

When you use Object.assign, the first object you give it will have all the rest of the objects merged into it. That is to say, the first object will be mutated.

If you want to avoid mutating the objects you're merging, it's helpful to pass in the empty object as the first parameter to prevent any of the ponent objects from changing.

Here's an example demonstrating the difference:

const obj1 = {
  foo: "bar"
}

const obj2 = {
  key: "value"
}

// Here, obj1 is the same after the Object.assign call
console.log(Object.assign({}, obj1, obj2));
console.log(obj1)
console.log(obj2)

console.log("\n\n")

// Note that after this call, obj1 holds both keys. So this will mutate it:
console.log(Object.assign(obj1, obj2));
console.log(obj1) // This is different now
console.log(obj2)

If you don't pass an empty object in, the original todo object will be modified. This may be what you want, but more often than not it isn't.

This is due to the way objects are all references, and are not cloned by default.

Short answer: Objects and Arrays are assignment by reference.

In this example, changing one will change the other, they are not immutable:

let x = {param:1}

const foo = (a) => {
  a.param +=1;
  console.log('response', x, a)
}
foo(x);

To fix that, we use Object.assign()

let x = {param:1}

const foo = (a) => {
  let b = Object.assign({}, a);
  b.param +=1;
  console.log('response', b, x)
}
foo(x);

As CRice pointed out in their answer, this syntax is useful for "dumping"/copying all properties from multiple objects into one output without modifying the original objects.

I wanted to elaborate on an ES6 equivalent for readers who may be finding this from researching Object.assign after seeing it laying around your codebase.

Background on References

First and foremost, it's important to understand that JavaScript Objects are passed by reference (technically referred to as passed by "sharing," but we can think of them the same for the purposes of this example). The 30,000-foot idea is that if the typeof yourVariable === 'object', any function that modifies yourVariable (even if passed in as an argument) will also affect the outside world's copy. The reference is shared.

In the following code, let's create an object of some primitive types and an object of objects (remember, arrays also have typeof 'object').

const values = {
  a: 0,
  b: true,
  c: 'abc',
  d: undefined,
  e: null,
};

const references = {
  obj: {
    f: "hello world"
  },
  arr: [1, 2, 3]
};

If we want to merge the properties of both values and references into one output object, we can use Object.assign with the first argument (the target) as an empty object:

const assign = Object.assign({}, values, references);
console.log(assign);
// Object { 
//  a: 0, 
//  b: true, 
//  c: "abc", 
//  d: undefined, 
//  e: null, 
//  obj: Object {
//   f: "hello world"
//  }, 
//  arr: Array [1, 2, 3]
//}

Note that Object.assign is not suitable for doing a deep merge as it only copies the topmost properties. That is, the first level of properties are copied while any nested properties that have a value that is a reference type are shared with the original object. We can more easily see this by modifying the original nested object and paring its reference with our assigned object reference. Note the unmodified array is also a shared reference:

references.obj.f = 'something else';
console.log(references.obj === assign.obj, references.arr === assign.arr);
// true, true

ES6 Version

For those of you familiar with ES6, you may already realize that the spread syntax does exactly what assign did in the above example, including the same limitation of only copying top level properties. We can take the same two objects, and do a shallow clone of them using the spread syntax as follows:

const spread = { ...values, ...references };
console.log(spread);
// Object { 
//  a: 0, 
//  b: true, 
//  c: "abc", 
//  d: undefined, 
//  e: null, 
//  obj: Object {
//   f: "something else"
//  }, 
//  arr: Array [1, 2, 3]
//}

And when updating the original reference, we still have a shared reference between the objects:

references.obj.f = 'another edit';
console.log(references.obj === spread.obj, references.arr === spread.arr);
// true, true
发布评论

评论列表(0)

  1. 暂无评论