D3 javascript Sự khác biệt giữa foreach và mỗi


88

Sự khác biệt giữa D3js forEacheachtrong D3js là gì?

Câu trả lời:


177

Đầu tiên, .forEach()không phải là một phần của d3, nó là một hàm gốc của các mảng javascript. Vì thế,

["a", "b", "c"].forEach(function(d, i) { console.log(d + " " + i); });
// Outputs:
a 0
b 1
c 2

Và điều đó hoạt động ngay cả khi d3 không được tải trên trang.

Tiếp theo, d3 .each()hoạt động trên các lựa chọn d3 (những gì bạn nhận được khi bạn d3.selectAll(...)). Về mặt kỹ thuật, bạn có thể gọi .forEach()một vùng chọn d3, vì đằng sau, vùng chọn d3 là một mảng có các chức năng phụ (một trong số chúng là .each()). Nhưng bạn không nên làm điều đó bởi vì:

  1. Làm như vậy sẽ không tạo ra hành vi mong muốn. Biết cách sử dụng .forEach()với lựa chọn d3 để tạo ra bất kỳ hành vi mong muốn nào sẽ đòi hỏi sự hiểu biết sâu sắc về hoạt động bên trong của d3. Vậy tại sao phải làm điều đó, nếu bạn chỉ có thể sử dụng phần công khai, được lập thành tài liệu của API.

  2. Khi bạn gọi .each(function(d, i) { })một lựa chọn d3, bạn không chỉ có được di: hàm được gọi sao cho thistừ khóa ở bất kỳ đâu bên trong hàm đó trỏ đến phần tử HTML DOM được liên kết với d. Nói cách khác, console.log(this)từ bên trong function(d,i) {}sẽ đăng nhập một cái gì đó giống như <div class="foo"></div>hoặc bất kỳ phần tử html nào. Và điều đó rất hữu ích, bởi vì sau đó bạn có thể gọi hàm trên thisđối tượng này để thay đổi các thuộc tính CSS, nội dung của nó hoặc bất cứ thứ gì. Thông thường, bạn sử dụng d3 để đặt các thuộc tính này, như trong d3.select(this).style('color', '#c33');.

Vấn đề chính là, sử dụng .each()bạn sẽ có được quyền truy cập vào 3 điều bạn cần: d, thisi. Với .forEach(), trên một mảng (như trong ví dụ từ đầu), bạn chỉ nhận được 2 điều ( di), và bạn sẽ phải thực hiện một loạt công việc để kết hợp một phần tử HTML với 2 điều đó. Và đó, trong số những thứ khác, là cách d3 hữu ích.


16
Cảm ơn vì đã viết một câu trả lời tuyệt vời, và để làm điều đó mà không bao gồm bất kỳ Snark không cần thiết đó là quá phổ biến trên SO ...
Kevin H. Lin

1
Cần lưu ý ở đây: khi bạn cần nhiều phạm vi khác nhau cho từ khóa 'this' nhưng bạn không cần dữ liệu trong hàm được gọi của mình, thì lựa chọn [0] .forEach (...) thuận tiện hơn nhiều so với lựa chọn.each, yêu cầu giải pháp thay thế 'self = this' trong hàm mẹ nếu 'this' có ý nghĩa bên ngoài chỉ tham chiếu đến các phần tử DOM.
sdupton

Phạm vi @sdupton thislà mối quan tâm trong nhiều trường hợp d3 nơi bạn chuyển các hàm bậc cao hơn, bao gồm cả ví dụ selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Tôi tin rằng đó là một phần lý do tại sao các lớp d3 (chẳng d3.svg.axishạn như) không sử dụng các prototypephương pháp xác định các lớp - như một cách để tránh sự phụ thuộc vào this. Nhưng tôi không thấy làm thế nào selection[0].forEach(...)để tránh vấn đề này. Nó không phải là cùng một vấn đề?
metamit

1
@meetamit bạn có thể xác định rõ ràng 'this' để sử dụng trong Array.prototype.forEach với đối số thứ hai, được truyền sau hàm được gọi trên mỗi phần tử. Khi bạn đang viết bất kỳ thứ gì giống như một trình bao bọc hướng đối tượng (tôi đang sử dụng các lớp ES6), việc mất phạm vi rõ ràng của 'cái này' có thể là một điều tồi tệ.
sdupton

2
@sdupton, thật tuyệt - Tôi không biết .forEachđã chấp nhận thông số thứ hai cho phạm vi this. Nó khiến tôi nhận ra rằng bạn có thể sử dụng thứ gì đó tương tự để đạt được hiệu quả tương tự với của d3 .each()bằng cách sử dụng .bind()phương pháp của javascript . Ví dụ, phạm vi sau đây sẽ thisđến windowvà sẽ console.log nó: selection.each(function() { console.log(this); }.bind(window)).
meetamit
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.