Có thể thay đổi giá trị của mảng khi thực hiện foreach trong javascript không?


300

thí dụ:

var arr = ["one","two","three"];

arr.forEach(function(part){
  part = "four";
  return "four";
})

alert(arr);

Mảng vẫn với các giá trị ban đầu của nó, có cách nào để có quyền truy cập bằng văn bản vào các phần tử của mảng từ hàm lặp không?



Hãy thử bản đồ ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ mẹo ):x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]
Justin

Câu trả lời:


468

Cuộc gọi lại được thông qua phần tử, chỉ mục và mảng.

arr.forEach(function(part, index, theArray) {
  theArray[index] = "hello world";
});

chỉnh sửa - như đã lưu ý trong một nhận xét, .forEach()hàm có thể lấy một đối số thứ hai, sẽ được sử dụng làm giá trị của thismỗi cuộc gọi đến cuộc gọi lại:

arr.forEach(function(part, index) {
  this[index] = "hello world";
}, arr); // use arr as this

Ví dụ thứ hai đó cho thấy arrchính nó được thiết lập như thistrong cuộc gọi lại. Một số người có thể nghĩ rằng mảng liên quan đến .forEach()cuộc gọi có thể là giá trị mặc định của this, nhưng vì lý do nào đó thì không; thissẽ là undefinednếu đối số thứ hai không được cung cấp.

(Lưu ý: những điều trên về thiskhông áp dụng nếu gọi lại là một =>chức năng, bởi vì thiskhông bao giờ bị ràng buộc với bất cứ điều gì khi các chức năng đó được gọi.)

Ngoài ra, điều quan trọng cần nhớ là có cả một nhóm các tiện ích tương tự được cung cấp trên nguyên mẫu Array và nhiều câu hỏi xuất hiện trên Stackoverflow về chức năng này hay chức năng khác sao cho giải pháp tốt nhất là chỉ cần chọn một công cụ khác. Bạn đã có:

  • forEach để làm một điều với hoặc cho mỗi mục trong một mảng;
  • filter để sản xuất một mảng mới chỉ chứa các mục đủ điều kiện;
  • map để tạo một mảng mới một-một bằng cách chuyển đổi một mảng hiện có;
  • some để kiểm tra xem ít nhất một phần tử trong một mảng có phù hợp với một số mô tả hay không;
  • everyđể kiểm tra xem tất cả các mục trong một mảng có khớp với mô tả hay không;
  • find để tìm kiếm một giá trị trong một mảng

và như thế. Liên kết MDN


34
Cảm ơn! ES6: Array.forEach ((o, i, a) => a [i] = myNewVal)
DiegoRBaquero

Tại sao part(hoặc thậm chí otrong es6) không được xác định? Làm thế nào để có được giá trị lặp?
Daniil Mashkin

@DaniilMashkin partsẽ là undefinednếu một phần tử của mảng đã được gán undefinedrõ ràng. Các vị trí "trống" của một mảng (một mục mảng chưa bao giờ được gán bất kỳ giá trị nào) được bỏ qua .forEach()và hầu hết các phương thức lặp mảng khác.
Mũi nhọn

2
Để hoàn thiện, .forEach()cũng cần một đối số thứ hai thisArg, mà bạn có thể sử dụng như thisbên trong cuộc gọi lại. LƯU Ý: đây là đối số .forEachKHÔNG phải là đối số của cuộc gọi lại.
đà Moha toàn năng

3
Để sử dụng thisđối số được truyền dưới dạng đối số thứ hai .forEach(), bạn cần truyền hàm gọi lại bằng cú pháp function(), vì sử dụng hàm mũi tên của ES6 () => {}không liên kết ngữ cảnh.
jpenna

131

Hãy cố gắng làm cho nó đơn giản và thảo luận về cách nó thực sự hoạt động. Nó phải làm với các loại biến và tham số chức năng.

Đây là mã của bạn, chúng tôi đang nói về:

var arr = ["one","two","three"];

arr.forEach(function(part) {
  part = "four";
  return "four";
})

alert(arr);

Trước hết, đây là nơi bạn nên đọc về Array.prototype.forEach ():

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Thứ hai, hãy nói ngắn gọn về các loại giá trị trong JavaScript.

Các nguyên hàm (không xác định, null, String, Boolean, Number) lưu trữ một giá trị thực.

Ví dụ: var x = 5;

Các kiểu tham chiếu (đối tượng tùy chỉnh) lưu trữ vị trí bộ nhớ của đối tượng.

Ví dụ: var xObj = { x : 5 };

Và thứ ba, làm thế nào các tham số chức năng làm việc.

Trong các hàm, tham số luôn được truyền theo giá trị.

Bởi vì arrlà một mảng của Chuỗi, nó là một mảng các đối tượng nguyên thủy , có nghĩa là chúng được lưu trữ theo giá trị.

Vì vậy, đối với mã của bạn ở trên, điều này có nghĩa là mỗi lần lặp forEach (), partcó cùng giá trị với arr[index], nhưng không phải cùng một đối tượng .

part = "four";sẽ thay đổi partbiến, nhưng sẽ để lại arrmột mình.

Đoạn mã sau sẽ thay đổi các giá trị bạn mong muốn:

var arr = ["one","two","three"];

arr.forEach(function(part, index) {
  arr[index] = "four";
});

alert(arr);

Bây giờ nếu mảng arrlà một mảng các kiểu tham chiếu , đoạn mã sau sẽ hoạt động vì các kiểu tham chiếu lưu trữ một vị trí bộ nhớ của một đối tượng thay vì đối tượng thực tế.

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // part and arr[index] point to the same object
  // so changing the object that part points to changes the object that arr[index] points to

  part.num = "four";
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

Sau đây minh họa rằng bạn có thể thay đổi partđể trỏ đến một đối tượng mới trong khi để các đối tượng được lưu trữ arrmột mình:

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // the following will not change the object that arr[index] points to because part now points at a new object
  part = 5;
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

2
Thật vậy, lời giải thích tuyệt vời! Sẽ tốt hơn nữa khi mở rộng lời giải thích ở các phương pháp lặp khác, cho ... của, bản đồ, ... Cái mới cho ... của các tác phẩm theo cách rất giống với forEach cho các trường hợp như vậy, ngoại trừ việc " chỉ mục "bị thiếu để thay đổi mảng ban đầu (như trong forEach). Ngoài ra bản đồ ES5 có vẻ tương tự như forEach, có thể chỉ là khả năng trả về các giá trị từ bản đồ giúp mọi thứ rõ ràng hơn từ góc độ cú pháp hơn là sử dụng chỉ mục.
Barshe Vidal

1
Giải thích tuyệt vời. Cảm ơn bạn. Sau tất cả, tôi không thể nhận được tuyên bố này: In functions, parameters are always passed by value. Còn ví dụ thứ hai thì sao?
Alex

@ 7 mặt, Câu lệnh đó mô tả rằng các tham số hàm sẽ luôn được truyền theo giá trị. Vì vậy, đối với người nguyên thủy, nó sẽ là giá trị mà người nguyên thủy hướng đến. Đối với các đối tượng, nó sẽ là vị trí mà đối tượng đang trỏ đến. trang w3schools này có một lời giải thích tốt. Xem phần Đối số được truyền theo giá trịĐối tượng được truyền bằng tham chiếu .
Dave

Trong các hàm, tham số luôn được truyền theo giá trị. BAM. cám ơn. +1
radarbob

92

Mảng: [1, 2, 3, 4]
Kết quả:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Giữ mảng ban đầu

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
const modifiedArr = originalArr.map(name => `${name}man`);

console.log( "Original: %s", originalArr );
console.log( "Modified: %s", modifiedArr );

Array.prototype.forEach() Ghi đè mảng ban đầu

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);

console.log( "Overridden: %s", originalArr );


3
"Array.prototype.map () Sửa đổi mảng ban đầu, đạt được bằng cách gán lại biến mảng" .map () không bao giờ sửa đổi mảng ban đầu, nó tạo ra một mảng mới. Tái chỉ định biến không thay đổi đối tượng ban đầu.
vad Khẩu

1
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map () Không bao giờ Sửa đổi mảng gốc, forEach biến đổi.
Anupam Maurya

@AnupamMaurya Điều đó không đúng chút nào. mapchắc chắn có thể đột biến mảng của nó và forEach, nói chung, không.
dùng4642212

@SebastianSimon, cảm ơn bạn đã trả lời. Tất cả những gì tôi muốn thêm, forEach sẽ ghi đè dữ liệu, nhưng bản đồ thì không thể, nó tạo ra một bản sao mới.
Anupam Maurya

14

Javascript được truyền theo giá trị và về cơ bản có nghĩa partlà một bản sao của giá trị trong mảng.

Để thay đổi giá trị, truy cập vào chính mảng trong vòng lặp của bạn.

arr[index] = 'new value';


Nó phụ thuộc vào loại partliệu nó có được sao chép hay không - mặc dù bạn đúng rằng biến không phải là một con trỏ, mà là một giá trị
Bergi

8
Nói "JavaScript được truyền theo giá trị" là một tổng quát hóa. Có các loại tham chiếu trong JavaScript. Các loại giá trị được truyền bằng giá trị.
Alex Turpin

9
Không phải là một khái quát chung. Một tuyên bố hoàn toàn chính xác và tuyệt đối. Javascript vượt qua các tham chiếu theo giá trị. Javascript luôn luôn vượt qua giá trị.
hvgotcodes

1
@Bergi: Không, không quan trọng là loại. Tất cả các giá trị được sao chép khi gán - các giá trị duy nhất trong JavaScript là nguyên hàm và tham chiếu. Cả nguyên thủy và tài liệu tham khảo được sao chép trên bài tập.
newacct

6
@Bergi chỉ vì một ngôn ngữ có một thứ gọi là tham chiếu, không có nghĩa là nó sử dụng thông qua tham chiếu. Các tham chiếu có thể được (và) được truyền theo giá trị, tức là giá trị của tham chiếu được sao chép và bản sao đó được sử dụng làm đối số.
hvgotcodes


4

Đây là một câu trả lời tương tự bằng cách sử dụng một =>hàm kiểu:

var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );

đưa ra kết quả:

[11,12,13,14]

Các selftham số là không thực sự cần thiết với chức năng mũi tên phong cách, vì vậy

data.forEach( (item,i) => data[i] = item + 10);

cũng hoạt động.


3

Hàm .forEach có thể có chức năng gọi lại (từng phần, phần tử Index) Vì vậy, về cơ bản những gì bạn cần làm là:

arr.forEach(function(element,index){
    arr[index] = "four";   //set the value  
});
console.log(arr); //the array has been overwritten.

Hoặc nếu bạn muốn giữ mảng ban đầu, bạn có thể tạo một bản sao của nó trước khi thực hiện quy trình trên. Để tạo một bản sao, bạn có thể sử dụng:

var copy = arr.slice();

3
Nếu bạn muốn tạo một bản sao của mảng, sử dụng map()thay vì forEach(). map()Lặp lại trên mảng nguồn và trả về một mảng mới chứa bản sao [đã sửa đổi] của bản gốc: mảng nguồn được giữ nguyên.
Nicholas Carey

2

Với các phương thức đối tượng Array, bạn có thể sửa đổi nội dung Array so với cơ sở cho các vòng lặp, các phương thức này thiếu một chức năng quan trọng. Bạn không thể sửa đổi chỉ mục khi chạy.

Ví dụ: nếu bạn sẽ loại bỏ phần tử hiện tại và đặt nó vào vị trí chỉ mục khác trong cùng một mảng, bạn có thể dễ dàng thực hiện việc này. Nếu bạn di chuyển phần tử hiện tại sang vị trí trước đó, không có vấn đề gì trong lần lặp tiếp theo, bạn sẽ nhận được mục tiếp theo giống như khi bạn chưa làm gì cả.

Xem xét mã này nơi chúng tôi di chuyển mục ở vị trí chỉ mục 5 đến vị trí chỉ mục 2 khi chỉ mục đếm đến 5.

var ar = [0,1,2,3,4,5,6,7,8,9];
ar.forEach((e,i,a) => {
i == 5 && a.splice(2,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 6 - 7 7 - 8 8 - 9 9

Tuy nhiên, nếu chúng ta di chuyển phần tử hiện tại đến một nơi nào đó ngoài vị trí chỉ mục hiện tại, mọi thứ sẽ hơi lộn xộn. Sau đó, mục tiếp theo sẽ chuyển sang vị trí mục được di chuyển và trong lần lặp tiếp theo, chúng tôi sẽ không thể xem hoặc đánh giá nó.

Xem xét mã này nơi chúng tôi di chuyển mục ở vị trí chỉ mục 5 đến vị trí chỉ mục 7 khi chỉ mục đếm đến 5.

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && a.splice(7,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 7 - 7 5 - 8 8 - 9 9

Vì vậy, chúng tôi chưa bao giờ gặp 6 trong vòng lặp. Thông thường trong một vòng lặp for, bạn dự kiến ​​sẽ giảm giá trị chỉ mục khi bạn di chuyển mục mảng về phía trước để chỉ mục của bạn giữ nguyên vị trí trong lần chạy tiếp theo và bạn vẫn có thể đánh giá mục được chuyển vào vị trí của mục bị xóa. Điều này là không thể với các phương thức mảng. Bạn không thể thay đổi chỉ mục. Kiểm tra mã sau

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && (a.splice(7,0,a.splice(i,1)[0]), i--);
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 4 5 - 6 7 - 7 5 - 8 8 - 9 9

Như bạn thấy khi chúng tôi giảm giá, inó sẽ không tiếp tục từ 5 mà là 6, từ nơi nó được để lại.

Vì vậy, hãy ghi nhớ điều này.


1

Để thêm hoặc xóa hoàn toàn các phần tử sẽ thay đổi chỉ mục, bằng cách mở rộng đề xuất zhujy_8833 của lát () để lặp lại trên một bản sao, chỉ cần đếm số phần tử bạn đã xóa hoặc thêm và thay đổi chỉ mục tương ứng. Ví dụ: để xóa các thành phần:

let values = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"];
let count = 0;
values.slice().forEach((value, index) => {
    if (value === "A2" || value === "A5") {
        values.splice(index - count++, 1);
    };
});
console.log(values);

// Expected: [ 'A0', 'A1', 'A3', 'A4', 'A6', 'A7', 'A8' ]

Để chèn các phần tử trước:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - count--, 0, 'newVal');
};

// Expected: ['newVal', A0, 'A1', 'A2', 'A3', 'A4', 'A5', 'newVal', 'A6', 'A7', 'newVal', 'A8' ]

Để chèn các phần tử sau:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - --count, 0, 'newVal');
};

// Expected: ['A0', 'newVal', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'newVal', 'A7', 'A8', 'newVal']

Để thay thế một yếu tố:

if (value === "A3" || value === "A4" || value === "A7") {
    values.splice(index, 1, 'newVal');
};

// Expected: [ 'A0', 'A1', 'A2', 'newVal', 'newVal', 'A5', 'A6', 'newVal', 'A8' ]

Lưu ý: nếu thực hiện chèn cả 'trước' và 'sau', mã sẽ xử lý 'trước' chèn trước, cách khác sẽ không như mong đợi


0

Bạn có thể thử điều này nếu bạn muốn ghi đè

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
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.