Почему изменение массива в JavaScript влияет на копии массива?
Я написал следующий JavaScript:
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['b','c']
alert(copyOfMyArray); // alerts ['b','c']
var myNumber = 5;
var copyOfMyNumber = myNumber;
copyOfMyNumber = copyOfMyNumber - 1;
alert(myNumber); // alerts 5
alert(copyOfMyNumber); // alerts 4
Этот код объявляет переменную myArray
и устанавливает ее в значение массива. Затем он объявляет вторую переменную copyOfMyArray
и устанавливает ее в myArray
.
Он выполняет операцию на copyOfMyArray
, а затем предупреждает как myArray
, так и copyOfMyArray
. Так или иначе, когда я выполняю операцию на copyOfMyArray
, кажется, что та же операция выполняется на myArray
.
Затем код выполняет одно и то же с числовым значением: он объявляет переменную myNumber
и устанавливает ее в числовое значение. Затем он объявляет вторую переменную copyOfMyNumber
и устанавливает ее в myNumber
. Он выполняет операцию на copyOfMyNumber
, а затем предупреждает как myNumber
, так и copyOfMyNumber
. Здесь я получаю ожидаемое поведение: разные значения для myNumber
и copyOfMyNumber
.
В чем разница между массивом и числом в JavaScript, который, по-видимому, меняет массив, изменяет значение копии массива, где, когда изменение числа не изменяет значение копии числа?
Я предполагаю, что по какой-то причине массив ссылается по ссылке и номеру по значению, но почему? Как я могу узнать, какое поведение ожидать от других объектов?
Ответы
Ответ 1
Массив в JavaScript также является объектом, а переменные содержат только ссылку на объект, а не на сам объект. Таким образом, обе переменные имеют ссылку на один и тот же объект.
Ваше сравнение с примером номера неверно. Вы назначаете новое значение copyOfMyNumber
. Если вы присвойте новое значение copyOfMyArray
, он не изменит значение myArray
.
Вы можете создать копию массива с помощью slice
[docs]:
var copyOfMyArray = myArray.slice(0);
Но обратите внимание, что это возвращает только мелкую копию, т.е. объекты внутри массива не будут клонированы.
Ответ 2
Ну, единственный возможный ответ - и правильный; что вы на самом деле не копируете массив. Когда вы пишете
var copyOfArray = array;
вы назначаете ссылку на тот же массив в другую переменную. Другими словами, они указывают на один и тот же объект. Другими словами,
Ответ 3
Таким образом, все здесь проделали большую работу, объясняя , почему это происходит - я просто хотел подвести черту и рассказать, как мне удалось это исправить - довольно легко:
thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
var copyOfThingArray = [...thingArray]
copyOfThingArray.shift();
return copyOfThingArray;
}
Это использует... синтаксис распространения.
Распространение источника синтаксиса
ОБНОВЛЕНИЕ: Как, почему это, и ответить на ваш вопрос:
В чем разница между массивом и числом в JavaScript в том, что изменение массива, по-видимому, приводит к изменению значения копии массива, тогда как при изменении числа не изменяется значение копии числа?
Ответ заключается в том, что в JavaScript массивы и объекты являются изменяемыми, а строки и числа и другие примитивы неизменными. Когда мы делаем назначение, как:
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray - это просто ссылка на myArray, а не фактическая копия.
Я бы порекомендовал эту статью Что такое неизменяемые и изменяемые структуры данных?, чтобы углубиться в тему.
Глоссарий MDN: Изменчивый
Ответ 4
Я считаю, что это самый простой способ сделать глубокий клон объекта или массива:
const objectThatIWantToClone = { foo: 'bar'};
const clone = JSON.parse(JSON.stringify(objectThatIWantToClone));
Стригируя его, мы создаем его неизменную копию, которую затем можем преобразовать обратно в JSON.
https://codepen.io/Buts/pen/zWdVyv
Ответ 5
Объекты клонирования -
A loop / array.push
дает аналогичный результат для array.slice(0)
или array.clone()
. Значения передаются по ссылке, но поскольку большинство примитивных типов данных неизменяемы, последующие операции дают желаемый результат - "клон". Это, конечно, не относится к объектам и массивам, которые позволяют изменять исходную ссылку (они являются изменяемыми типами).
Возьмем следующий пример:
const originalArray = [1, 'a', false, {foor: 'bar'}]
const newArray = [];
originalArray.forEach((v, i) => {
newArray.push(originalArray[i]);
});
newArray[0] = newArray[0] + 1;
newArray[1] = 'b';
newArray[2] = true;
newArray[3] = Object.assign(newArray[3], {bar: 'foo'});
Операции, выполняемые на индексах newArray, производят желаемый результат, за исключением конечного (объекта), который, поскольку он копируется по ссылке, также будет мутировать исходный массив [3].
https://jsfiddle.net/7ajz2m6w/
Обратите внимание, что array.slice(0)
and array.clone()
страдает от этого же ограничения.
Один из способов решения этой проблемы - эффективно клонировать объект во время последовательности нажатия:
originalArray.forEach((v, i) => {
const val = (typeof v === 'object') ? Object.assign({}, v) : v;
newArray.push(val);
});
https://jsfiddle.net/e5hmnjp0/
веселит
Ответ 6
В JS оператор "=" скопирует указатель на область памяти массива.
Если вы хотите скопировать массив в другой, вы должны использовать функцию Clone.
Для целых чисел разные, потому что они являются примитивными типами.
S.
Ответ 7
Все копируется по ссылке, кроме примитивных типов данных (строки и числа IIRC).
Ответ 8
Создайте фильтр исходного массива в arrayCopy. Так что изменения в новом массиве не будут влиять на исходный массив.
var myArray = ['a', 'b', 'c'];
var arrayCopy = myArray.filter(function(f){return f;})
arrayCopy.splice(0, 1);
alert(myArray); // alerts ['a','b','c']
alert(arrayCopy); // alerts ['b','c']
Надеюсь, что это поможет.
Ответ 9
У вас нет никаких копий.
У вас есть несколько переменных, содержащих один и тот же массив.
Аналогично, у вас есть несколько переменных, содержащих один и тот же номер.
Когда вы пишете copyOfMyNumber = ...
, вы помещаете новое число в переменную.
Это похоже на запись copyOfMyArray = ...
.
Когда вы пишете copyOfMyArray.splice
, вы изменяете исходный массив.
Это невозможно с числами, потому что числа неизменяемы и не могут быть изменены,
Ответ 10
Вы можете добавить некоторую обработку ошибок в зависимости от ваших случаев и использовать что-то похожее на следующую функцию для решения проблемы. Прокомментируйте любые ошибки/проблемы/идеи эффективности.
function CopyAnArray (ari1) {
var mxx4 = [];
for (var i=0;i<ari1.length;i++) {
var nads2 = [];
for (var j=0;j<ari1[0].length;j++) {
nads2.push(ari1[i][j]);
}
mxx4.push(nads2);
}
return mxx4;
}
Ответ 11
Другой подход для копирования массива во временную переменную может быть типизированием/изменением вашего массива в строку, а затем его извлечением.
Например
var a = [1,2,3];
typeof(a) (this will give "object")
var b = JSON.stringify(a);
typeof(b) (this will give "string");
b = JSON.parse(b);
typeOf(b) (this will give "object")
и теперь chnage в значении b не будет отражаться на
Ответ 12
Массив, или объект в javascript всегда содержит одну и ту же ссылку, если вы не клонируете или не копируете. Вот пример:
http://plnkr.co/edit/Bqvsiddke27w9nLwYhcl?p=preview
// for showing that objects in javascript shares the same reference
var obj = {
"name": "a"
}
var arr = [];
//we push the same object
arr.push(obj);
arr.push(obj);
//if we change the value for one object
arr[0].name = "b";
//the other object also changes
alert(arr[1].name);
Для клонирования объектов мы можем использовать .clone() в jquery и angular.copy(), эти функции создадут новый объект с другой ссылкой. Если вы знаете больше функций для этого, скажите, пожалуйста, спасибо!
Ответ 13
Ваш ответ здесь
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray.slice();
В принципе, операция slice() клонирует массив и возвращает неглубокую копию.
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['a', 'b', 'c']
alert(copyOfMyArray); // alerts ['b','c']
Четкую документацию можно найти по следующей ссылке: Array.prototype.slice()
Ответ 14
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = JSON.parse(JSON.stringify(myArray));
copyOfMyArray.splice(0,1);
console.log('orginal Array',myArray)
console.log('After Splic of copyOfMyArray',copyOfMyArray);
//If not using the JSON.parse(JSON.stringify (array)) at the time of assign the array change of the one array will affect another because of the reference.
Ответ 15
В JavaScript копирование массивов и объектов изменяет исходные значения, поэтому Deep Copy является решением для этого.
Глубокая копия означает на самом деле создание нового массива и копирование значений, поскольку все, что происходит с ним, никогда не повлияет на исходный.
JSON.parse
и JSON.stringify
- лучший и простой способ глубокого копирования. Метод JSON.stringify()
преобразует значение JavaScript в строку JSON. Метод JSON.parse()
анализирует строку JSON, создавая значение JavaScript или объект, описываемый строкой.
//Глубокий клон
let a = [{ x:{z:1} , y: 2}];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z=0
console.log(JSON.stringify(a)); //[{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}]
Для более подробной информации: читайте здесь
Ответ 16
Проблема с мелким копированием состоит в том, что все объекты не клонируются, вместо этого он получает ссылку. Поэтому array.slice(0) будет нормально работать только с литеральным массивом, но он не будет делать мелкое копирование с массивом объектов. В этом случае один из способов...
var firstArray = [{name: 'foo', id: 121}, {name: 'zoo', id: 321}];
var clonedArray = firstArray.map((_arrayElement) => Object.assign({}, _arrayElement));
console.log(clonedArray);
// [{name: 'foo', id: 121}, {name: 'zoo', id: 321}] // shallow copy