Почему первый объект в _.merge обновляется с текущим значением? - PullRequest
1 голос
/ 02 июля 2019

Скажите, у меня есть три объекта.

const x = { val: 1 };
const y = { val: 2 };
const z = { val: 3 };

const mergedObj = _.merge(x,y,z);
// x = { val: 3 };
// y = { val: 2 };
// z = { val: 3 };
// mergedObj = { val: 3 };

Почему первый объект в функции слияния lodash изменяет объект x на значение 3, а объект y остается на 2? Я думаю, что все объекты будут сохранять одинаковые пары ключ-значение, так как функция _.merge создаст новый объект? Я ошибаюсь в этом предположении?

Ответы [ 3 ]

2 голосов
/ 02 июля 2019

При слиянии lodash первым аргументом является целевой объект. Это объект, который будет видоизменяться. Это также объект, который будет возвращен (см. Ниже для понимания).

Lodash объединяет тех, кто не создает новый объект. Новый объект не создан. Вместо этого он изменяет (изменяет значение) первый объект.

Остальные аргументы являются объектом для объединения. Они не будут мутировать. Слияния применяются последовательно.

ref: https://lodash.com/docs/4.17.11#merge

Таким образом, здесь объект x будет видоизменен на x.val=2, а затем x.val=3. Окончательное значение x.val будет равно 3. y, и z не изменится. Результатом функции слияния будет х.

Вот как об этом думать

const x = { val: 1 };
const y = { val: 2 };
const z = { val: 3 };

const refToX = _.merge(x,y,z);

// Since the result is a ref to x varaiable, this means
refToX.val=5;
console.log(x.val) //will return 5

2 голосов
/ 02 июля 2019

Лодаш изменяет первый аргумент, а не создает совершенно новый объект.Каждый из последующих аргументов применяется поверх первого аргумента по порядку, но они сами не затрагиваются.

Мы можем продемонстрировать это, создав функцию установки JavaScript.Это вызывается каждый раз, когда свойство установлено (таким образом, это имя).

let merge1 = { val: 1 };
let merge2 = { val: 2 };
let merge3 = { val: 3 };

let mergeTarget = {
	val:0,
  set val(name) {
    console.log("Setting with:" + name);
  }
}

_.merge(mergeTarget,merge1,merge2, merge3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Вы увидите, что установщик на mergeTarget вызывается не один, а три раза с каждым из следующих свойств в следующем порядке:

Setting with:1
Setting with:2
Setting with:3

Чтобы быть понятным, Lodash действительно возвращает ссылку на конечное значение, что может быть полезно в таких случаях, как при использовании встроенного слияния.Например (для использования надуманного примера):

someArray.map(_.merge(mappingFunction, properties))

Однако, как вы заметили, это возвращаемое значение является просто ссылкой на первый аргумент, который был видоизменен.

Почему?

Что касается более глубокого вопроса , почему lodash делает это так ... Это хороший вопрос, поскольку, как правило, обычно имеет смысл избегать нечистых функций дляпростая служебная функция.На самом деле нужно было бы спросить разработчиков lodash, а также разработчиков подчеркивания, которые устанавливали эту практику.

Но мы можем довольно легко придумать несколько веских причин, почему это было реализовано таким образом и почему оно остается.

1.Простота использования / стиль.

Частью привлекательности Lodash / подчеркивания является то, что они предоставляют удобные и четко определенные служебные функции, которыми легко пользоваться свободно.В той степени, в которой они могут чувствовать себя синтаксическим сахаром для самого JavaScript.В случае, когда вам просто нужно объединить объект, вам нужно беспокоиться или помешать коду, чтобы сделать это следующим образом.

Например, вы можете сделать это:

myComponent.updateConfig = function(update){
   _.merge(myComponent.config, update)
}

Какойпросто немного аккуратнее, чем беспокоиться о возвращаемом значении.А учитывая, что побочные эффекты ясны и хорошо определены, использовать его вот так неплохо.

Однако это, пожалуй, наименее важная причина.

2.Производительность.

Разумно по умолчанию избегать создания лишних объектов без необходимости.

Поведение JavaScript по умолчанию состоит в том, чтобы передавать объекты по ссылке, и это не требует какого-либо умного способа сделать мутацию копирования при записи только части объекта.Если вам нужно создать измененную копию объекта, оставив оригинал нетронутым, единственный способ сделать это - скопировать весь объект, а затем внести изменения в копию.

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

3.Легко избежать.

Бывают ситуации, когда нежелательно мутировать объект, и мы предпочли бы чисто функциональное поведение.Однако это легко создать, просто передав в качестве первого параметра пустой объект.

const x = { val: 1 };
const y = { val: 2 };
const z = { val: 3 };

const result = _.merge({},x,y,z);
console.log(x.val) //will return 1
result.val = 5
console.log(x.val) //will still return 1
2 голосов
/ 02 июля 2019

Согласно документации этот метод изменяет исходный объект.Так что x будет видоизменяться.Также обратите внимание, что mergedObj и x одинаковы.Это работает так же, как Object.assign

const x = { val: 1 };
const y = { val: 2 };
const z = { val: 3 };

const mergedObj = _.merge(x,y,z);
console.log(mergedObj === x)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

почему объект y остается в 2?

В этом методе lodash первый передаваемый аргумент элемента считаетсяцелевой объект.Это будет объект (x), которому будут переданы все свойства следующих переданных объектов (y and z).Поэтому следующие объекты не будут видоизменяться.

Если вы хотите создать новый объект.Затем используйте пустой объект {} в качестве целевого объекта (первый параметр)

const x = { val: 1 };
const y = { val: 2 };
const z = { val: 3 };
let merged = _.merge({},x,y,z);
console.log(merged);
console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
...