Sao chép mảng theo giá trị


1745

Khi sao chép một mảng trong JavaScript sang mảng khác:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

Tôi nhận ra rằng arr2đề cập đến cùng một mảng arr1, chứ không phải là một mảng mới, độc lập. Làm thế nào tôi có thể sao chép mảng để có được hai mảng độc lập?


3
Có vẻ như hiện tại trong Chrome 53 và Firefox 48, chúng tôi có hiệu suất slicesplicehoạt động tuyệt vời và toán tử lây lan mới và Array.fromcó triển khai chậm hơn nhiều. Nhìn vào perfjs.fnfo
Pencroff

jsben.ch/#/wQ9RU <= điểm chuẩn này cung cấp tổng quan về các cách khác nhau để sao chép một mảng
EscapeNetscape


Đối với mảng này (một có chứa chuỗi nguyên thủy), bạn có thể sử dụng var arr2 = arr1.splice();để sao chép sâu, nhưng kỹ thuật này sẽ không làm việc nếu các yếu tố trong mảng của bạn có chứa các cấu trúc chữ (ví dụ []hay {}) hoặc hiện vật nguyên mẫu (ví dụ function () {}, new, vv). Xem câu trả lời của tôi dưới đây để biết thêm giải pháp.
tfmontague

16
Đó là năm 2017, vì vậy bạn có thể cân nhắc sử dụng các tính năng ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Hinrich

Câu trả lời:


2701

Dùng cái này:

var newArray = oldArray.slice();

Về cơ bản, slice()hoạt động nhân bản mảng và trả về một tham chiếu đến một mảng mới.

Cũng lưu ý rằng:

Đối với tham chiếu, chuỗi và số (chứ không phải đối tượng thực tế), slice()sao chép tham chiếu đối tượng vào mảng mới. Cả mảng ban đầu và mảng mới đều tham chiếu đến cùng một đối tượng. Nếu một đối tượng được tham chiếu thay đổi, các thay đổi sẽ hiển thị cho cả mảng mới và mảng gốc.

Các nguyên thủy như chuỗi và số là bất biến, vì vậy thay đổi đối với chuỗi hoặc số là không thể.


9
Về hiệu suất, các bài kiểm tra jsPerf sau đây thực sự cho thấy var Array2 = Array1.slice () cũng nhanh như var Array2 = Array1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5jsperf.com/copy-simple-array . Kết quả của loại jsperf.com/array-copy/5 làm tôi ngạc nhiên đến mức tôi tự hỏi liệu mã kiểm tra có hợp lệ không.
Cohen

94
Mặc dù điều này đã nhận được rất nhiều upvote, nhưng nó xứng đáng với cái khác bởi vì nó mô tả đúng các tài liệu tham khảo trong JS, rất hiếm khi xảy ra.
Wayne

34
@ GáborImre bạn có thể thêm toàn bộ thư viện để dễ đọc không? Có thật không? Tôi chỉ cần thêm một nhận xét nếu tôi quan tâm đến khả năng đọc. Xem: var newArray = oldArray.slice (); // Sao chép oldArray sang newArray
dudewad 8/2/2016

12
@ GáborImre Tôi hiểu điều đó, chắc chắn. Nhưng theo tôi, việc trả lời một vấn đề kỹ thuật cụ thể bằng cách bao gồm toàn bộ thư viện là không hữu ích, đó là thiết kế phình to. Tôi thấy các nhà phát triển làm điều đó rất nhiều, và sau đó bạn kết thúc với một dự án bao gồm toàn bộ khung để thay thế việc phải viết một hàm duy nhất. Chỉ là MO của tôi, mặc dù.
dudewad

5
Bài học rút ra: Đừng nhầm lẫn .slice()với .splice(), nó cung cấp cho bạn một mảng trống. Sự khác biệt lớn.
crazypeter

531

Trong Javascript, các kỹ thuật sao chép sâu phụ thuộc vào các thành phần trong một mảng. Hãy bắt đầu từ đó.

Ba loại yếu tố

Các yếu tố có thể là: giá trị theo nghĩa đen, cấu trúc theo nghĩa đen hoặc nguyên mẫu.

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}`

Từ các yếu tố này, chúng ta có thể tạo ra ba loại mảng.

// 1) Array of literal-values (boolean, number, string) 
const type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
const type2 = [[], {}];

// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];

Kỹ thuật sao chép sâu phụ thuộc vào ba loại mảng

Dựa trên các loại phần tử trong mảng, chúng ta có thể sử dụng các kỹ thuật khác nhau để sao chép sâu.

Kỹ thuật sao chép sâu Javascript theo các loại phần tử

  • Mảng của đen-giá trị (type1)
    Các [...myArray], myArray.splice(0), myArray.slice(), và myArray.concat()kỹ thuật có thể được sử dụng để sao chép các mảng sâu với các giá trị văn chương (boolean, số, và string) chỉ; trong đó toán tử lây lan [...myArray]có hiệu suất tốt nhất ( https://measurethat.net/Benchmark/Show/4281/0/s lây -array- performance- vs-slice-splice-concat ).

  • Mảng của đen-giá trị (type1) và đen-cấu trúc (type2)
    Các JSON.parse(JSON.stringify(myArray))kỹ thuật có thể được sử dụng để các giá trị bản sao sâu đen (boolean, số, chuỗi) và cấu trúc chữ (mảng, đối tượng), nhưng không phải nguyên mẫu đối tượng.

  • Tất cả các mảng (type1, type2, type3) Kỹ thuật
    jQuery $.extend(myArray)có thể được sử dụng để sao chép sâu tất cả các kiểu mảng. Các thư viện như UnderscoreLo-dash cung cấp các hàm sao chép sâu tương tự như jQuery $.extend() , nhưng có hiệu suất thấp hơn. Đáng ngạc nhiên hơn, $.extend()có hiệu suất cao hơn so với JSON.parse(JSON.stringify(myArray))kỹ thuật http://jsperf.com/js-deep-copy/15 .
    Và đối với những nhà phát triển né tránh các thư viện của bên thứ ba (như jQuery), bạn có thể sử dụng chức năng tùy chỉnh sau; có hiệu suất cao hơn $ .extend và sao chép sâu tất cả các mảng.

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

Vì vậy, để trả lời câu hỏi ...

Câu hỏi

var arr1 = ['a','b','c'];
var arr2 = arr1;

Tôi nhận ra rằng Array2 đề cập đến cùng một mảng là Array1, chứ không phải là một mảng độc lập mới. Làm thế nào tôi có thể sao chép mảng để có được hai mảng độc lập?

Câu trả lời

arr1là một mảng các giá trị bằng chữ (boolean, số hoặc chuỗi), bạn có thể sử dụng bất kỳ kỹ thuật sao chép sâu nào được thảo luận ở trên, trong đó toán tử trải ...có hiệu suất cao nhất.

// Highest performance for deep copying literal values
arr2 = [...arr1];

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above

1
Nhiều cách tiếp cận này không hoạt động tốt. Sử dụng toán tử gán có nghĩa là bạn phải gán lại giá trị bằng chữ gốc của arr1. Rất hiếm khi đó là trường hợp. Sử dụng spliceobliterates arr1, vì vậy đó không phải là một bản sao nào cả. Việc sử dụng JSONsẽ thất bại nếu bất kỳ giá trị nào trong mảng là Hàm hoặc có nguyên mẫu (chẳng hạn như a Date).
Dancrumb

Sử dụng mối nối là một giải pháp một phần. Nó sẽ thất bại trong nhiều trường hợp hơn JSON. Splice tạo ra một bản sao sâu của chuỗi và số, khi nó di chuyển các giá trị - không bao giờ nói nó trả về một bản sao.
tfmontague

1
Tại sao mối nối (0)? Không nên là lát ()? Tôi nghĩ rằng nó không nên sửa đổi mảng ban đầu, mà mối nối nào. @JamesMontagne
trợ giúp

2
mối nối sẽ tạo con trỏ tới các phần tử trong mảng ban đầu (bản sao nông). splice (0) sẽ phân bổ bộ nhớ mới (bản sao sâu) cho các phần tử trong mảng là số hoặc chuỗi và tạo con trỏ cho tất cả các loại phần tử khác (bản sao nông). Bằng cách chuyển giá trị bắt đầu bằng 0 cho phương thức hàm splice, nó sẽ không ghép bất kỳ phần tử nào từ mảng ban đầu, và do đó nó không sửa đổi nó.
tfmontague

1
Trên thực tế, chỉ có một loại mảng: một mảng "đôi khi". Không có sự khác biệt giữa [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]và bất kỳ mảng nào khác.
wizzwizz4

189

Bạn có thể sử dụng chênh lệch mảng ...để sao chép mảng.

const itemsCopy = [...items];

Ngoài ra nếu muốn tạo một mảng mới với mảng hiện có là một phần của nó:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Hiện tại, mảng trải rộng được hỗ trợ trong tất cả các trình duyệt chính nhưng nếu bạn cần hỗ trợ cũ hơn, hãy sử dụng bản in hoặc babel và biên dịch sang ES5.

Thông tin thêm về chênh lệch


1
Điều này sẽ không làm việc cho bản sao sâu. Deep Clone một mảng trong JavaScript .
SNag

151

Không cần jQuery ... Ví dụ làm việc

var arr2 = arr1.slice()

Điều này sao chép mảng từ vị trí bắt đầu cho 0đến hết mảng.

Điều quan trọng cần lưu ý là nó sẽ hoạt động như mong đợi đối với các loại nguyên thủy (chuỗi, số, v.v.) và cũng để giải thích hành vi dự kiến ​​cho các loại tham chiếu ...

Nếu bạn có một loạt các loại Tham chiếu, hãy nói về loại Object. Mảng sẽ được sao chép, nhưng cả hai mảng sẽ chứa các tham chiếu đến cùng một Object. Vì vậy, trong trường hợp này có vẻ như mảng được sao chép bằng tham chiếu mặc dù mảng thực sự được sao chép.


12
Không, đây sẽ không phải là một bản sao sâu sắc.
jondavidjohn

Thử cái này; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
Sự khác biệt giữa câu trả lời này và câu trả lời được chấp nhận là gì?
Isaac Pak

gặp lỗi trong bảng điều khiển cho ví dụ đã cho của bạn "TypeError: window.addEvent không phải là một chức năng"
Ravi Sharma

72

Một cách khác sliceconcat, có thể được sử dụng theo 2 cách. Đầu tiên trong số này có lẽ dễ đọc hơn vì hành vi dự định rất rõ ràng:

var array2 = [].concat(array1);

Phương pháp thứ hai là:

var array2 = array1.concat();

Cohen (trong các ý kiến) chỉ ra rằng phương pháp sau này có hiệu suất tốt hơn .

Cách thức hoạt động này là concatphương thức tạo ra một mảng mới bao gồm các phần tử trong đối tượng mà nó được gọi theo sau là các phần tử của bất kỳ mảng nào được truyền cho nó dưới dạng đối số. Vì vậy, khi không có đối số được thông qua, nó chỉ đơn giản là sao chép mảng.

Lee Penkman, cũng trong các ý kiến, chỉ ra rằng nếu có một cơ hội array1undefined, bạn có thể trả về một mảng trống rỗng như sau:

var array2 = [].concat(array1 || []);

Hoặc, đối với phương pháp thứ hai:

var array2 = (array1 || []).concat();

Lưu ý rằng bạn cũng có thể làm điều này với slice: var array2 = (array1 || []).slice();.


31
Trên thực tế bạn cũng có thể làm: var Array2 = Array1.concat (); Nó nhanh hơn rất nhiều về hiệu suất. (JSPerf: jsperf.com/copy-simple-arrayjsperf.com/copy-array-slice-vs-concat/5
Cohen

5
Cần lưu ý rằng nếu mảng1 không phải là một mảng thì [].concat(array1)trả về, [array1]ví dụ nếu không xác định được thì bạn sẽ nhận được [undefined]. Đôi khi tôi làmvar array2 = [].concat(array1 || []);
lee penkman

60

Đây là cách tôi đã thực hiện sau khi thử nhiều cách tiếp cận:

var newArray = JSON.parse(JSON.stringify(orgArray));

Điều này sẽ tạo ra một bản sao sâu mới không liên quan đến bản đầu tiên (không phải bản sao nông).

Ngoài ra, điều này rõ ràng sẽ không sao chép các sự kiện và chức năng, nhưng điều tốt là bạn có thể làm điều đó trong một dòng và nó có thể được sử dụng cho bất kỳ loại đối tượng nào (mảng, chuỗi, số, đối tượng ...)


4
Đây là điều tốt nhất. Tôi đã sử dụng phương pháp tương tự từ lâu và nghĩ rằng không còn ý nghĩa nào trong các vòng lặp đệ quy cũ của trường
Vladimir Kharlampidi

1
Xin lưu ý rằng tùy chọn này không xử lý tốt các cấu trúc giống như biểu đồ: sự cố khi có chu kỳ và không bảo tồn các tham chiếu được chia sẻ.
Ruben

1
Điều này cũng thất bại đối với những thứ như Date, hoặc thực sự, bất cứ thứ gì có nguyên mẫu. Ngoài ra, undefineds được chuyển đổi thành nulls.
Dancrumb

7
Có ai đủ can đảm để nhận xét về sự kém hiệu quả trong cả CPU và bộ nhớ của việc tuần tự hóa thành văn bản và sau đó phân tích lại một đối tượng không?
Lawrence Dol

3
Giải pháp này là duy nhất đã làm việc. Sử dụng lát () thực sự là một giải pháp giả mạo.

20

Một số phương thức được đề cập hoạt động tốt khi làm việc với các kiểu dữ liệu đơn giản như số hoặc chuỗi, nhưng khi mảng chứa các đối tượng khác, các phương thức này không thành công. Khi chúng ta cố gắng truyền bất kỳ đối tượng nào từ mảng này sang mảng khác, nó được truyền dưới dạng tham chiếu, không phải đối tượng.

Thêm mã sau vào tệp JavaScript của bạn:

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

Và chỉ cần sử dụng

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

Nó sẽ làm việc.


2
tôi gặp lỗi này khi tôi thêm mã này vào trang của mình 'Uncaught RangeError: Vượt quá kích thước ngăn xếp cuộc gọi tối đa'
sawe

1
Tôi xin lỗi, lỗi này xảy ra trong chrome nếu mảng1 không được khai báo. Vì vậy, tôi đã sao chép-dán đoạn mã trên và tôi gặp lỗi, tuy nhiên, nếu tôi khai báo mảng Array1, thì tôi không nhận được lỗi. Bạn có thể cải thiện câu trả lời bằng cách khai báo mảng1 ngay trên mảng 2, tôi thấy có khá nhiều 'chúng tôi' ngoài kia không nhận ra rằng chúng tôi phải khai báo mảng1 (một phần vì khi tôi đang đánh giá câu trả lời của bạn, tôi đã vội vàng và cần một cái gì đó "chỉ hoạt động")
sawe

.slice()vẫn hoạt động tốt ngay cả khi bạn có các đối tượng trong mảng của mình: jsfiddle.net/edelman/k525g
Jason

7
@Jason nhưng các đối tượng vẫn đang trỏ đến cùng một đối tượng nên việc thay đổi cái này sẽ thay đổi cái kia. jsfiddle.net/k525g/1
Samuel

Mã tuyệt vời. Một câu hỏi tôi có, tôi thực sự đã cố gắng sao chép một mảng vào một mảng khác như var Array1 = new Array () và sau đó var Array2 = Array1; Nếu tôi thay đổi một cái gì đó trong mảng 2, sự thay đổi cũng xảy ra với mảng1. Tuy nhiên, nếu tôi sử dụng nguyên mẫu nhân bản mà bạn đã tạo, nó thực sự tạo ra một phiên bản hoàn toàn mới của mảng đó hay nói cách khác là nó sao chép nó. Vì vậy, đây có phải là một vấn đề trình duyệt? hoặc javascript theo mặc định đặt hai biến bằng một biến trỏ đến một biến khác bằng cách sử dụng các con trỏ khi ai đó thực hiện var Array2 = Array1 và tại sao điều đó không xảy ra với các biến số nguyên? xem jsfiddle.net/themhz/HbhtA
themhz


17

Cá nhân tôi nghĩ Array.from là một giải pháp dễ đọc hơn. Nhân tiện, chỉ cần cẩn thận với sự hỗ trợ trình duyệt của nó.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);

1
Vâng, điều này rất dễ đọc. Các .slice()giải pháp hoàn toàn unintuitive. Cám ơn vì cái này.
Banago

15

Quan trọng!

Hầu hết các câu trả lời ở đây hoạt động cho các trường hợp cụ thể .

Nếu bạn không quan tâm đến các đối tượng và đạo cụ sâu / lồng nhau, hãy sử dụng ( ES6 ):

let clonedArray = [...array]

nhưng nếu bạn muốn làm bản sao sâu, hãy sử dụng cái này thay thế:

let cloneArray = JSON.parse(JSON.stringify(array))


Đối với người dùng lodash:

let clonedArray = _.clone(array) tài liệu

let clonedArray = _.cloneDeep(array) tài liệu



9

Thêm vào giải pháp của mảng.slice (); lưu ý rằng nếu bạn có các mảng con mảng đa chiều sẽ được sao chép bởi các tham chiếu. Những gì bạn có thể làm là lặp và cắt () từng mảng con riêng lẻ

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

những thứ tương tự đi vào mảng các đối tượng, chúng sẽ được sao chép bằng cách tham chiếu, bạn phải sao chép chúng theo cách thủ công


Câu trả lời này xứng đáng một vị trí gần đầu trang! Tôi đã làm việc với các mảng con đa chiều và không thể hiểu tại sao các mảng bên trong luôn được sao chép bởi ref chứ không phải bởi val. Logic đơn giản này đã giải quyết vấn đề của tôi. Tôi sẽ cung cấp cho bạn +100 nếu có thể!
Mac

8

Các giá trị nguyên thủy luôn được truyền bằng giá trị của nó (sao chép). Giá trị hợp chất tuy nhiên được thông qua tham chiếu.

Vậy làm thế nào để chúng tôi sao chép mảng này?

let arr = [1,2,3,4,5];

Sao chép một mảng trong ES6

let arrCopy = [...arr]; 

Sao chép n Mảng trong ES5

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

Tại sao `let ArrayCopy = Array` không truyền theo giá trị?

Truyền một biến cho một biến khác trên các giá trị Hợp chất, chẳng hạn như Object / Array hoạt động khác nhau. Sử dụng toán tử asign trên các giá trị copand, chúng ta chuyển tham chiếu đến một đối tượng. Đây là lý do tại sao giá trị của cả hai mảng thay đổi khi loại bỏ / thêm các phần tử mảng.

Ngoại lệ:

arrCopy[1] = 'adding new value this way will unreference';

Khi bạn gán một giá trị mới cho biến, bạn đang thay đổi chính tham chiếu và nó không ảnh hưởng đến Object / Array ban đầu.

đọc thêm


6

Như chúng ta biết trong mảng Javascript và các đối tượng là do tham chiếu, nhưng những cách nào chúng ta có thể sao chép mảng mà không thay đổi mảng ban đầu sau đó?

Dưới đây là một số cách để làm điều đó:

Hãy tưởng tượng chúng tôi có mảng này trong mã của bạn:

var arr = [1, 2, 3, 4, 5];

1) Vòng qua mảng trong một hàm và trả về một mảng mới, như thế này:

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }

2) Sử dụng phương thức lát, lát là để cắt một phần của mảng, nó sẽ cắt một phần của mảng mà không chạm vào bản gốc, trong lát, nếu không chỉ định bắt đầu và kết thúc của mảng, nó sẽ cắt toàn bộ mảng mảng và về cơ bản tạo một bản sao đầy đủ của mảng, vì vậy chúng ta có thể dễ dàng nói:

var arr2 = arr.slice(); // make a copy of the original array

3) Cũng là phương thức liên hệ, đây là để hợp nhất hai mảng, nhưng chúng ta chỉ có thể chỉ định một trong các mảng và sau đó điều này về cơ bản tạo một bản sao của các giá trị trong mảng được liên hệ mới:

var arr2 = arr.concat();

4) Cũng là phương thức xâu chuỗi và phân tích cú pháp, nó không được khuyến khích, nhưng có thể là một cách dễ dàng để sao chép Mảng và Đối tượng:

var arr2 = JSON.parse(JSON.stringify(arr));

5) Phương thức Array.from, điều này không được hỗ trợ rộng rãi, trước khi sử dụng kiểm tra sự hỗ trợ trong các trình duyệt khác nhau:

const arr2 = Array.from(arr);

6) Cách ECMA6, cũng không được hỗ trợ đầy đủ, nhưng babelJs có thể giúp bạn nếu bạn muốn phiên dịch:

const arr2 = [...arr];

6
let a = [1,2,3];

Bây giờ bạn có thể thực hiện bất kỳ một trong những điều sau đây để tạo một bản sao của một mảng.

let b = Array.from(a); 

HOẶC LÀ

let b = [...a];

HOẶC LÀ

let b = new Array(...a); 

HOẶC LÀ

let b = a.slice(); 

HOẶC LÀ

let b = a.map(e => e);

Bây giờ, nếu tôi thay đổi một,

a.push(5); 

Khi đó, a là [1,2,3,5] nhưng b vẫn là [1,2,3] vì nó có tham chiếu khác nhau.

Nhưng tôi nghĩ rằng, trong tất cả các phương thức trên Array.from là tốt hơn và chủ yếu được thực hiện để sao chép một mảng.


1
cái nào nhanh nhất
Khung Marc


4

Trong trường hợp cụ thể của tôi, tôi cần đảm bảo mảng vẫn còn nguyên để điều này hoạt động với tôi:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}

2
+1 để không thêm sự tối nghĩa vào mã của bạn bằng cách gọi một hàm thực hiện chính xác điều tương tự, nhưng theo một cách ít rõ ràng hơn. lát có thể hiệu quả hơn dưới mui xe, nhưng với bất kỳ ai làm việc với mã, điều này cho thấy ý định của bạn. cộng với nó giúp dễ dàng tối ưu hóa sau này, nếu bạn muốn (ví dụ) lọc những gì bạn đang sao chép. lưu ý tuy nhiên điều này không xử lý sao chép sâu và các đối tượng bên trong tương tự được chuyển sang mảng mới, bằng cách tham chiếu. Đây có thể là những gì bạn muốn làm, nó có thể không.
không đồng bộ

4

Tạo bản sao của mảng / đối tượng đa chiều:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

Cảm ơn James Padolsey cho chức năng này.

Nguồn: Tại đây


3

Dan, không cần phải sử dụng thủ thuật ưa thích. Tất cả những gì bạn cần làm là tạo bản sao của mảng 1 bằng cách này.

var arr2 = new Array(arr1);

Bây giờ arr1arr2là hai biến mảng khác nhau được lưu trữ trong các ngăn xếp riêng biệt. Kiểm tra này trên jsfiddle .


Điều này không sao chép mảng. Nó tạo ra một mảng với một phần tử tham chiếu đến bản gốc (tức là var arr2 = [arr1];).
Timothy003

3

Khi chúng ta muốn sao chép một mảng bằng toán tử gán ( =) nó không tạo một bản sao, nó chỉ sao chép con trỏ / tham chiếu đến mảng. Ví dụ:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

Thông thường khi chúng tôi chuyển đổi dữ liệu, chúng tôi muốn giữ nguyên cơ sở hạ tầng ban đầu (ví dụ Mảng). Chúng tôi làm điều này bằng cách tạo một bản sao chính xác của mảng của chúng tôi để cái này có thể được chuyển đổi trong khi cái ban đầu vẫn còn nguyên.

Các cách sao chép một mảng:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
	const newArray = [];
    for(let i= 0; i < base.length; i++) {
	    newArray[i] = base[i];
	}
	return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

Hãy cẩn thận khi các mảng hoặc các đối tượng được lồng nhau!:

Khi các mảng được lồng nhau, các giá trị được sao chép bằng tham chiếu. Dưới đây là một ví dụ về cách điều này có thể dẫn đến các vấn đề:

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

Vì vậy, không sử dụng các phương thức này khi có các đối tượng hoặc mảng bên trong mảng bạn muốn sao chép. tức là chỉ sử dụng các phương thức này trên các mảng nguyên thủy.

Nếu bạn muốn xóa sâu một mảng javascript sử dụng JSON.parsekết hợp với JSON.stringify, như thế này:

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

Hiệu suất sao chép:

Vì vậy, cái nào chúng ta chọn cho hiệu suất tối ưu. Nó chỉ ra rằng phương pháp dài dòng nhất, forvòng lặp có hiệu suất cao nhất. Sử dụngfor vòng lặp để sao chép thực sự chuyên sâu CPU (lớn / nhiều mảng).

Sau đó, .slice()phương thức này cũng có hiệu năng tốt và cũng ít dài dòng hơn và dễ dàng hơn cho người lập trình thực hiện. Tôi đề nghị sử dụng .slice()để sao chép hàng ngày các mảng không tốn nhiều CPU. Ngoài ra, tránh sử dụng JSON.parse(JSON.stringify(arr))(rất nhiều chi phí) nếu không cần bản sao sâu và hiệu suất là một vấn đề.

Kiểm tra hiệu suất nguồn


3

Nếu mảng của bạn chứa các phần tử của kiểu dữ liệu nguyên thủy như int, char hoặc chuỗi, v.v. thì bạn có thể sử dụng một trong các phương thức đó trả về một bản sao của mảng ban đầu, chẳng hạn như .slice () hoặc .map () hoặc toán tử trải ( cảm ơn ES6).

new_array = old_array.slice()

hoặc là

new_array = old_array.map((elem) => elem)

hoặc là

const new_array = new Array(...old_array);

NHƯNG nếu mảng của bạn chứa các phần tử phức tạp như các đối tượng (hoặc mảng) hoặc các đối tượng lồng nhau hơn , thì bạn sẽ phải đảm bảo rằng bạn đang tạo một bản sao của tất cả các phần tử từ cấp cao nhất đến tham chiếu khác ở cấp độ cuối cùng của bên trong các đối tượng sẽ được sử dụng và điều đó có nghĩa là thay đổi giá trị trong object_elements trong new_array vẫn sẽ ảnh hưởng đến old_array. Bạn có thể gọi phương thức sao chép này ở mỗi cấp là tạo một bản sao DEEP QUYỀN của old_array.

Để sao chép sâu, bạn có thể sử dụng các phương pháp được đề cập ở trên cho các loại dữ liệu nguyên thủy ở mỗi cấp tùy thuộc vào loại dữ liệu hoặc bạn có thể sử dụng phương pháp tốn kém này (được đề cập dưới đây) để tạo một bản sao sâu mà không phải thực hiện nhiều công việc.

var new_array = JSON.parse(JSON.stringify(old_array));

Có rất nhiều phương pháp khác mà bạn có thể sử dụng tùy theo yêu cầu của bạn. Tôi đã chỉ đề cập đến một số trong những người đưa ra ý tưởng chung về những gì xảy ra khi chúng ta cố gắng sao chép một mảng sang mảng khác theo giá trị .


Cảm ơn rất nhiều, câu trả lời của bạn là người duy nhất làm việc cho kịch bản của tôi,
albert sh

2

Nếu bạn muốn tạo một bản sao mới của một đối tượng hoặc mảng, bạn phải sao chép rõ ràng các thuộc tính của đối tượng hoặc các thành phần của mảng, ví dụ:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

Bạn có thể tìm kiếm thêm thông tin trên Google về các giá trị nguyên thủy bất biến và các tham chiếu đối tượng có thể thay đổi.


1
Bạn không cần phải sao chép rõ ràng các thuộc tính của các đối tượng của mảng. Xem câu trả lời của Chtiwi Malek.
Magne

2

Sử dụng jQuery sao chép sâu có thể được thực hiện như sau:

var arr2 = $.extend(true, [], arr1);


2

Dưới đây là một vài cách để sao chép:

const array = [1,2,3,4];

const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );



2

Ví dụ nhanh:

  1. Nếu các phần tử trong mảng là các kiểu nguyên thủy (chuỗi, số, v.v.)

var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in 
// different places in the memory
var arr2 = arr1.slice(); 
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]

  1. Nếu các phần tử trong mảng là các đối tượng bằng chữ, thì một mảng khác ({}, [])

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice(); 
arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                 // deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                 // pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9;	 // not OK - same above reason

console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

  1. Giải pháp cho 2 : Sao chép sâu theo yếu tố theo yếu tố

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop();	  // OK - don't affect arr1
arr2[0].x = 'z';  // OK - don't affect arr1
arr2[1][0] = 9;	  // OK - don't affect arr1

console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]


1

Đây là một biến thể:

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */

không phải là một ý tưởng tồi, mặc dù tôi tốt hơn nên sử dụng JSON stringify / parse thay vì eval và một so sánh jsPerf khác sẽ rất tốt để kiểm tra, toSourceví dụ , lưu ý là không chuẩn và sẽ không hoạt động trong Chrome.
dmi3y

1

Có một điều mới được giới thiệu Array.from, nhưng thật không may, tại thời điểm viết bài này, nó chỉ được hỗ trợ trên các phiên bản Firefox gần đây (32 trở lên). Nó có thể được sử dụng đơn giản như sau:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

Tham khảo: Tại đây

Hoặc Array.prototype.mapcó thể được sử dụng với chức năng nhận dạng:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Tham khảo: Tại đây


+1 để đề cập Array.from, hiện được hỗ trợ trên tất cả các trình duyệt chính trừ Internet Explorer ( nguồn ). .
mgthomas99

Hãy nhận biết Array.fromsẽ làm thay đổi dữ liệu, không cung cấp sao chép sâu / nhân bản sâu.
Sudhansu Choudhary

1

Đối với mảng ES6 chứa các đối tượng

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.