Как сохранить массив с неизменяемыми объектами в JavaScript? - PullRequest
2 голосов
/ 25 марта 2019

Я хочу сделать массив, основанный на двух массивах - "ideaList" и "одобрения", объявленных глобально.Так как ideaList и одобрения используются в других частях программы, мне нужно, чтобы они были неизменяемыми, и я подумал, что .map и .filter сохранят эту неизменность.после того, как эта функция была выполнена, я замечаю, что все объекты массива имеют свойства «count», «like», «position» со значениями, что доказывает, что массив был мутирован.

Я пытался использовать только .map, но результат тот же.

Не могли бы вы узнать, как я могу предотвратить мутацию ideaList?Также я хотел бы избежать использования const, так как я сначала объявляю ideaList глобально, а затем присваиваю ему некоторые данные в другой функции.

Ответы [ 5 ]

2 голосов
/ 25 марта 2019

Чтобы иметь в виду неизменность, вы можете думать о своих ценностях как о примитивах.

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

Вы можете распространить этот способ мышления и на не примитивов

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

, чтобы лучше понять это, пожалуйста, посмотрите на MDN - Сравнения равенства и одинаковость


как заставить неизменность?

Всегда возвращая новый экземпляр данного объекта!

Допустим, мы должны установить свойство 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 голос
/ 25 марта 2019

В JS на объекты ссылаются.Другими словами, при создании вы получаете переменную объекта, указывающую на область памяти, которая должна содержать значимое значение.

var o = {foo: 'bar'}

Переменная o теперь указывает на память, которая имеет {foo: bar}.

var p = o;

Теперь переменная p тоже указывает на ту же ячейку памяти.Таким образом, если вы измените o, это также изменит p.

Это то, что происходит внутри вашей функции.Даже если вы используете методы 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}
1 голос
/ 25 марта 2019

Вы изменяете не сам массив, а объекты, на которые ссылается массив. .map() создает копию массива, но содержащиеся в нем ссылки указывают на те же самые объекты, что и оригинал, который вы мутировали, добавляя свойства непосредственно к ним.

Вам также необходимо сделать копии этих объектов и добавить свойства к этим копиям. Оптимальный способ сделать это - использовать объектный разброс в .map() обратном вызове:

    .map(({ ...obj }) => {
      obj.count = endorsements
                             .filter(x=>x.ideaNumber===obj.ideaNumber)
      ...

Если ваша среда не поддерживает синтаксис распространения объекта, клонируйте объект с помощью Object.assign():

    .map(originalObj => {
      const obj = Object.assign({}, originalObj);
      obj.count = endorsements
                             .filter(x=>x.ideaNumber===obj.ideaNumber)
      ...
0 голосов
/ 25 марта 2019

Вы можете использовать новые встроенные механизмы неизменности ES6 , или вы можете просто обернуть симпатичный геттер вокруг ваших объектов, подобных этому

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();

Это будет держать ваш объект закрытымвне глобальной области видимости, но получатель будет доступен в вашей глобальной области видимости.

Подробнее об этом шаблоне кодирования можно прочитать здесь

0 голосов
/ 25 марта 2019

Вы назначаете эти свойства в своей функции карты, вам нужно изменить это. (Просто объявите пустой объект вместо использования вашего текущего obj)

...