当使用对象文字语法创建对象时,JavaScript 是否维护对现有变量的引用?

Does JavaScript maintain a reference to existing variables when objects are created using object literal syntax?

这是一个关于 JavaScript 如何添加对现有而不是创建新引用的问题。

这里有一些示例,希望足够说明,在 Redux reducer 的上下文中,因为它是 spread operatorObject.assign():

熟悉的地方

See here we are just returning an object literal with a string, so there is nothing that could drag in a reference to something that is existing elsewhere.

export default (state = {}, action) => {
    switch (action.type) {
        case SOME_ACTION:
            return {
                props: 'something arbitray'
            }
    }
}

这个是可疑问题:

We are returning an object literal but we have included reference to args[type]. First, I need to know for certain, is this returning an object that maintains a link to whatever args[type] is currently set to? If args[type] were to get mutated after, would that be reflected in this returned object?

export default (state = {}, action) => {
    switch (action.type) {
        case SOME_ACTION:
            return {
                props: args[type]
            }
    }
}

这里有两个我怀疑不会有这个问题的例子:

Do I understand that correctly? Does JavaScript copy just the property and not maintain any reference to args[type]?

export default (state = {}, action) => {
    switch (action.type) {
        case SOME_ACTION:
            return Object.assign({}, state, { props: args[type] })
    }
}

这是我最近了解到的另一个示例,可能在语法上与 Object.assign() 语法相同:

export default (state = {}, action) => {
    switch (action.type) {
        case SOME_ACTION:
            return { ...state, props: args[type] }
    }
}

问题:

  1. 扩展运算符在这种情况下是否做与 Object.assign() 完全相同的事情并创建一个全新的对象而没有由于维护对 [=20= 的引用而导致非法可变性的风险]?我需要能够依赖对象创建后的不可变状态。

  2. 我展示的第二个例子会保持对 args[type] 的实时引用吗?

我有一些代码通常会在某些东西中传播,我有一个忽略传播的用例,所以我很好奇这是否会成为问题。我如何保证 args[type] 的随机更改不会影响这个返回的对象?

这是正确答案吗?:

export default (state = {}, action) => {
    switch (action.type) {
        case SOME_ACTION:
            return Object.assign({}, { props: args[type] })
    }
}

[编辑] 我可以通过这样做来重现该问题:

const arr = ['one', 'two', 'three']

const args = {
  type: arr
}

const something = {
  props: args.type
}

arr.push('four') // Notice how this appears in something.props

console.log(something)

这修复了它(所以它似乎与基元和维护对象引用有关):

const arr = ['one', 'two', 'three']

const args = {
  type: arr[2]
}

const something = {
  props: args.type
}

arr[2] = 'what' // Notice how this doesn't appear in something.props

console.log(something)

Updated Question

有没有办法复制 non-primitive(即:object/array)以破坏此引用?

我注意到它不适用于 Object.assign()

var a = 5; var b = a;

b 是否维护对 a 的引用?不,不是的。 您正在传递一个值 reference/value 而不是父对象。

答案似乎是,如果值是原始值,这不是问题,因为 JavaScript 不存储对原始值的引用。

这里的解决方案是在对象或数组中散布为 属性:

const arr = ['one', 'two', 'three']

const args = {
  type: arr
}

const something = {
  props: [...args.type]
}

arr.push('four') // notice how this isn't included

console.log(something)

这很有帮助:Is JavaScript a pass-by-reference or pass-by-value language?

阅读浅拷贝和深拷贝也很有帮助,

这解决了目标是对象时的问题:

const obj = {
  one: 'uno',
  two: 'dos',
  three: 'tres'
}

const args = {
  type: obj
}

const something = {
  props: Object.assign({}, args.type)
}

obj.four = 'four' // notice how this isn't included

console.log(something)

我记得 Eric Elliot 在一本书中也描述了这一点。 Object.assign() 是放弃该引用的关键。 (现在是 2017 年,传播)