如何使用对象在javascript中保持不变的数组?

时间:2019-03-25 08:04:01

标签: javascript arrays mutability

我想基于两个数组创建一个数组-全局声明的“ ideaList”和“ endorsements”。由于ideaList和背书用于程序的其他部分,因此我需要它们是不可变的,我认为.map和.filter可以保持这种不变性。

function prepareIdeaArray(){
var preFilteredIdeas=ideaList
        .filter(hasIdeaPassedControl)
        .map(obj => {obj.count = endorsements
                                 .filter(x=>x.ideaNumber===obj.ideaNumber)
                                 .reduce((sum, x)=>sum+x.count,0); 
                     obj.like = endorsements
                                 .filter(x=>x.ideaNumber===obj.ideaNumber && x.who===activeUser)
                                 .reduce((sum, x)=>sum+x.count,0)===0?false:true
                     obj.position = generatePosition(obj.status)
                     obj.description = obj.description.replace(/\n/g, '<br>')  
                     return obj;});
preFilteredIdeas.sort(compareOn.bind(null,'count',false)).sort(compareOn.bind(null,'position',true))
return preFilteredIdeas;
}

但是,当执行此函数后,当我console.log ideaList时,我指出该数组的对象都具有带有值的“ count”,“ like”,“ position”属性,这证明该数组已经被变异。

我仅使用.map尝试过,但结果相同。

您知道我如何防止ideaList变异吗?我也想避免使用const,因为我先全局声明ideaList,然后在另一个函数中为其分配一些数据。

7 个答案:

答案 0 :(得分:2)

为帮助您牢记不变性,您可以将值视为原始值。

1 === 2 // false
'hello' === 'world' // false

您也可以将这种思维方式扩展到非原始人

[1, 2, 3] === [1, 2, 3] // false
{ username: 'hitmands' } === { username: 'hitmands' } // false

为了更好地理解它,请查看MDN - Equality Comparisons and Sameness


如何强制不变性?

总是返回给定对象的新实例!

假设我们必须设置待办事项的属性status。用旧的方式,我们只会做:

todo.status = 'new status';

但是,我们可以通过简单地复制给定对象并返回一个新对象来强制不变性。

const todo = { id: 'foo', status: 'pending' };

const newTodo = Object.assign({}, todo, { status: 'completed' });

todo === newTodo // false;

todo.status // 'pending'
newTodo.status // 'completed'

回到您的示例,而不是做obj.count = ...,我们只会做:

Object.assign({}, obj, { count: ... }) 
// or
({ ...obj, count: /* something */ })

有一些库可以帮助您实现不可变模式:

  1. Immer
  2. ImmutableJS

答案 1 :(得分:1)

您不是要更改数组本身,而是要更改数组包含的引用的对象。 public interface IPortableHttpClient { Task<HttpResponseMessage> GetAsync(Uri url); } 创建数组的副本,但其中包含的引用指向与原始对象完全相同的对象,您可以通过直接向其添加属性来对其进行突变。

您还需要制作这些对象的副本,并将属性添加到这些副本中。整齐的方法是在.map()回调中使用对象散布:

.map()

如果您的环境不支持对象传播语法,请使用 .map(({ ...obj }) => { obj.count = endorsements .filter(x=>x.ideaNumber===obj.ideaNumber) ... 克隆对象:

Object.assign()

答案 2 :(得分:1)

在JS中,引用了对象。换句话说,创建后,您将获得对象变量以指向要保留有意义值的存储位置。

var o = {foo: 'bar'}

变量o现在指向具有{foo: bar}的内存。

var p = o;

现在变量p也指向相同的存储位置。因此,如果您更改o,它也会更改p

这就是函数内部发生的事情。即使您使用不会改变其值的Array方法,但array元素本身是在函数内部进行修改的对象。它创建了一个新的数组-但元素指向的是对象的相同旧存储位置。

var a = [{foo: 1}];     //Let's create an array

//Now create another array out of it
var b = a.map(o => {
   o.foo = 2;
   return o;
})

console.log(a);   //{foo: 2}

一种解决方法是在操作期间为新数组创建一个新对象。可以使用Object.assign或最新的价差运算符来完成。

a = [{foo: 1}];

b = a.map(o => {

    var p = {...o};    //Create a new object
    p.foo = 2;
    return p;

})

console.log(a);  // {foo:1}

答案 3 :(得分:0)

您在地图函数中分配了这些属性,您需要对此进行更改。 (只需声明一个空对象,而不使用当前的obj

答案 4 :(得分:0)

您可以使用新的ES6 built-in不变性机制,或者可以像这样围绕对象包装漂亮的吸气剂

var myProvider = {}
function (context)
{
    function initializeMyObject()
    {
        return 5;
    }

    var myImmutableObject = initializeMyObject();

    context.getImmutableObject = function()
    {
        // make an in-depth copy of your object.
        var x = myImmutableObject

        return x;
    }


}(myProvider);

var x = myProvider.getImmutableObject();

这会将您的对象封闭在全局范围之外,但是getter将可在全局范围内访问。

您可以阅读有关此编码模式here

的更多信息

答案 5 :(得分:0)

制作可变对象“副本”的一种简单方法是将它们字符串化为另一个对象,然后将其解析回一个新数组。这对我有用。

function returnCopy(arrayThing) {
    let str = JSON.stringify(arrayThing);
    let arr = JSON.parse(str);
  return arr;
}

答案 6 :(得分:0)

您使用freeze方法提供要使其不可变的对象。

const person = { name: "Bob", age: 26 }
Object.freeze(person)