Cách nhanh nhất để chuyển đổi JavaScript NodeList sang Array?


251

Các câu hỏi được trả lời trước đây ở đây nói rằng đây là cách nhanh nhất:

//nl is a NodeList
var arr = Array.prototype.slice.call(nl);

Khi đo điểm chuẩn trên trình duyệt của tôi, tôi thấy rằng nó chậm hơn 3 lần so với điều này:

var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);

Cả hai đều sản xuất cùng một đầu ra, nhưng tôi thấy khó tin rằng phiên bản thứ hai của tôi là cách nhanh nhất có thể, đặc biệt là vì mọi người đã nói khác ở đây.

Đây có phải là một trò chơi trong trình duyệt của tôi (Chromium 6) không? Hay là có một cách nhanh hơn?

EDIT: Đối với bất kỳ ai quan tâm, tôi đã giải quyết các vấn đề sau (dường như là nhanh nhất trong mọi trình duyệt mà tôi đã thử nghiệm):

//nl is a NodeList
var l = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; l.push(nl[i++]));

EDIT2: Tôi đã tìm thấy một cách thậm chí nhanh hơn

// nl is the nodelist
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

2
arr[arr.length] = nl[i];có thể nhanh hơn arr.push(nl[i]);vì nó tránh được một cuộc gọi chức năng.
Luc125

9
Trang jsPerf này đang theo dõi tất cả các câu trả lời trên trang này: jsperf.com/nodelist-to-array/27
pilau

Xin lưu ý rằng "EDIT2: Tôi đã tìm thấy một cách nhanh hơn" chậm hơn 92% trên IE8.
Camilo Martin

2
Vì bạn biết đã biết bạn có bao nhiêu nút:var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mems

@ Luc125 Nó phụ thuộc vào trình duyệt, vì việc triển khai đẩy có thể được tối ưu hóa, tôi nghĩ về chrome vì v8 rất tốt với loại công cụ này.
axelduch

Câu trả lời:


197

Cái thứ hai có xu hướng nhanh hơn trong một số trình duyệt, nhưng điểm chính là bạn phải sử dụng nó vì cái thứ nhất không phải là trình duyệt chéo. Mặc dù The Times They are a-Changin '

@ Khangax ( xem trước IE 9 )

Array.prototype.slice hiện có thể chuyển đổi một số đối tượng máy chủ (ví dụ: NodeList) thành mảng - điều mà phần lớn các trình duyệt hiện đại đã có thể thực hiện trong một thời gian dài.

Thí dụ:

Array.prototype.slice.call(document.childNodes);

??? Cả hai đều tương thích với nhiều trình duyệt - Javascript (ít nhất là nếu nó tuyên bố là tương thích với thông số ECMAscript) là Javascript; Mảng, nguyên mẫu, lát và cuộc gọi là tất cả các tính năng của ngôn ngữ cốt lõi + loại đối tượng.
Jason S

6
nhưng chúng không thể được sử dụng trên NodeLists trong IE (Tôi biết nó rất tệ, nhưng xem bản cập nhật của tôi)
gblazex

9
bởi vì NodeLists không phải là một phần của ngôn ngữ, chúng là một phần của API DOM, được biết là có lỗi / không thể đoán trước được, đặc biệt là trong IE
gblazex

3
Array.prototype.slice không phải là trình duyệt chéo, nếu bạn sử dụng IE8 trong tài khoản.
Lajos Meszaros

1
Vâng, đó là những gì câu trả lời của tôi về cơ bản là :) Mặc dù nó có liên quan nhiều hơn vào năm 2010 so với ngày hôm nay (2015).
gblazex

224

Với ES6, giờ đây chúng ta có một cách đơn giản để tạo Mảng từ NodeList: Array.from()hàm.

// nl is a NodeList
let myArray = Array.from(nl)

Làm thế nào để phương pháp ES6 mới này so sánh về tốc độ với các phương pháp khác đã được đề cập ở trên?
dùng5508297

10
@ user5508297 Tốt hơn so với thủ thuật gọi lát. Chậm hơn các vòng lặp, nhưng đó không chính xác là chúng ta có thể muốn có một mảng mà không đi qua nó. Và cú pháp rất đẹp, đơn giản và dễ nhớ!
webdif

Điều thú vị về Array.from là bạn có thể sử dụng đối số bản đồ:console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

Một số tối ưu hóa:

  • lưu chiều dài của NodeList trong một biến
  • thiết lập rõ ràng độ dài của mảng mới trước khi cài đặt.
  • truy cập các chỉ số, thay vì đẩy hoặc không di chuyển.

Mã ( jsPerf ):

var arr = [];
for (var i = 0, ref = arr.length = nl.length; i < ref; i++) {
 arr[i] = nl[i];
}

Bạn có thể cạo thêm một chút thời gian bằng cách sử dụng Array (length) thay vì tạo mảng và sau đó định cỡ riêng cho nó. Sau đó, nếu bạn sử dụng const để khai báo mảng và độ dài của nó, với lệnh let bên trong vòng lặp, nó kết thúc nhanh hơn khoảng 1,5% so với phương thức trên: const a = Array (nl.length), c = a.length; for (let b = 0; b <c; b ++) {a [b] = nl [b]; } xem jsperf.com/nodelist-to-array/93
BrianFreud

15

Kết quả sẽ hoàn toàn phụ thuộc vào trình duyệt, để đưa ra phán quyết khách quan, chúng tôi phải thực hiện một số thử nghiệm hiệu suất, đây là một số kết quả, bạn có thể chạy chúng ở đây :

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

Xem trước nền tảng IE9 3:


1
Tôi tự hỏi làm thế nào mà vòng lặp ngược lại chống lại những điều này ... for (var i=o.length; i--;)... liệu 'vòng lặp for' trong các thử nghiệm này có đánh giá lại thuộc tính độ dài trên mỗi lần lặp không?
Dagg Nợi

14

Trình duyệt nhanh nhất và nhanh nhất là

for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);

Như tôi đã so sánh trong

http://jsbin.com/oqeda/98/edit

* Cảm ơn @CMS vì ý tưởng!

Chromium (Tương tự Google Chrome) Firefox Opera


1
liên kết dường như sai, nên là 91, thay vì 89 để bao gồm bài kiểm tra mà bạn đề cập. Và 98 dường như là một trong những đầy đủ nhất.
Yar Tư Yakovlev

9

Trong ES6, bạn có thể sử dụng:

  • Array.from

    let array = Array.from(nodelist)

  • Toán tử trải

    let array = [...nodelist]


Không thêm bất cứ điều gì mới những gì đã được viết trong các câu trả lời trước đó.
Stefan Becker

7
NodeList.prototype.forEach = Array.prototype.forEach;

Bây giờ bạn có thể làm document.querySelector ALL ('div'). ForEach (function () ...)


Ý kiến ​​hay, cảm ơn @John! Tuy nhiên, NodeListkhông hoạt động mà Objectlà: Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
Đừng sử dụng Object.prototype: nó phá vỡ JQuery và rất nhiều thứ như cú pháp từ điển.
Symate Nate

Chắc chắn, tránh để mở rộng các chức năng tích hợp sẵn.
roland

5

nhanh hơn và ngắn hơn:

// nl is the nodelist
var a=[], l=nl.length>>>0;
for( ; l--; a[l]=nl[l] );

3
Tại sao >>>0? Và tại sao không đặt các bài tập vào phần đầu tiên của vòng lặp for?
Camilo Martin

5
Ngoài ra, đây là lỗi. Khi l0, vòng lặp sẽ kết thúc, do đó 0yếu tố thứ sẽ không được sao chép (hãy nhớ có một phần tử ở index 0)
Camilo Martin

1
Thích câu trả lời này, nhưng ... Bất cứ ai thắc mắc: >>> có thể không cần thiết ở đây nhưng được sử dụng để đảm bảo độ dài của nút bấm tuân thủ thông số mảng; nó đảm bảo rằng nó là một số nguyên 32 bit không dấu. Kiểm tra nó ở đây ecma-i Intl.org/ecma-262/5.1/#sec-15.4 Nếu bạn thích mã không thể đọc được, hãy sử dụng phương pháp này với các đề xuất của @ CamiloMartin!
Todd

Trả lời @CamiloMartin - Sẽ rất rủi ro khi đặt varvào phần đầu tiên của forvòng lặp vì 'cẩu biến'. Việc khai báo varsẽ được 'kéo' lên trên cùng của hàm, ngay cả khi vardòng xuất hiện ở đâu đó thấp hơn và điều này có thể gây ra các tác dụng phụ không rõ ràng từ chuỗi mã. Ví dụ một số mã trong chức năng tương tự xảy ra trước khi các vòng lặp for có thể phụ thuộc vào albị không khai báo. Do đó, để có khả năng dự đoán cao hơn, hãy khai báo các vars của bạn ở đầu hàm (hoặc nếu trên ES6, sử dụng consthoặc letthay vào đó, không cần nâng).
brennanyoung

3

Kiểm tra bài viết blog này ở đây nói về điều tương tự. Từ những gì tôi thu thập được, thời gian thêm có thể phải làm với việc đi lên chuỗi phạm vi.


Hấp dẫn. Tôi vừa mới thực hiện một số thử nghiệm tương tự và Firefox 3.6.3 cho thấy tốc độ không tăng theo cách nào, trong khi Opera 10.6 tăng 20% ​​và Chrome 6 tăng 230% (!) Khi thực hiện theo cách lặp thủ công.
jairajs89

@ jairajs89 khá lạ. Có vẻ như Array.prototype.slicelà phụ thuộc vào trình duyệt. Tôi tự hỏi thuật toán nào mỗi trình duyệt đang sử dụng.
Vivin Paliath

3

Đây là chức năng tôi sử dụng trong JS của mình:

function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}

1

Dưới đây là các biểu đồ được cập nhật kể từ ngày đăng bài này (biểu đồ "nền tảng không xác định" là Internet Explorer 11.15.16299.0):

Safari 11.1.2 Firefox 61.0 Chrome 68.0.3440,75 Internet Explorer 11.15.16299.0

Từ những kết quả này, có vẻ như phương pháp preallocate 1 là đặt cược nhiều trình duyệt an toàn nhất.


1

Giả sử nodeList = document.querySelectorAll("div"), đây là một hình thức ngắn gọn nodelistđể chuyển đổi thành mảng.

var nodeArray = [].slice.call(nodeList);

Xem tôi sử dụng nó ở đây .

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.