Cách hiệu quả nhất để chuyển đổi HTMLCollection thành Mảng


391

Có cách nào hiệu quả hơn để chuyển đổi HTMLCollection thành Mảng, ngoài việc lặp qua nội dung của bộ sưu tập nói trên và tự đẩy từng mục vào một mảng không?


10
"Hiệu quả" nghĩa là gì? Nếu hoạt động tốt nhất, vòng lặp for thường nhanh hơn Array.prototype.slice . Một vòng lặp cũng hoạt động trong nhiều trình duyệt khác nhau (tức là tất cả), do đó, theo các tiêu chí đó, đó "cách hiệu quả nhất". Và đó là rất ít mã: for (var a=[], i=collection.length; i;) a[--i] = collection[i];vì vậy không có nhiều "con" ở đó :-)
RobG

@RobG Cảm ơn bạn - Tôi sẽ cho bạn + 59k nếu tôi có thể! ;-)
Chém ngược

1
Nhìn vào hiệu suất trình duyệt hiện tại , lát cắt hầu như đã bắt kịp các vòng lặp về hiệu suất, ngoại trừ trong Chrome. Sử dụng số lượng phần tử lớn hơn và tối ưu hóa vòng lặp một chút, kết quả gần như giống hệt nhau , ngoại trừ trong Chrome nơi vòng lặp nhanh hơn rất nhiều.
RobG

Tôi đã tạo một thử nghiệm jsperf xem xét cả hai phương pháp mà @harpo đã đề cập cũng như thử nghiệm jquery cho hiệu suất. Tôi đã tìm thấy jquery chậm hơn một chút so với cả phương pháp javascript và hiệu suất hàng đầu khác nhau giữa các trường hợp kiểm tra js. Chrome 59.0.3071 / Mac OS X 10.12.5 thích sử dụng Array.prototype.slice.callvà Brave (dựa trên Chrome 59.0.3071) hầu như không có sự khác biệt giữa hai thử nghiệm javascript qua nhiều lần chạy. Xem jsperf.com/htmlcollection-array-vs-jquery-children
NuclePeon

jsben.ch/h2IFA => kiểm tra hiệu suất cho những cách phổ biến nhất để làm điều này
EscapeNetscape

Câu trả lời:


696
var arr = Array.prototype.slice.call( htmlCollection )

sẽ có tác dụng tương tự bằng cách sử dụng mã "gốc".

Biên tập

Vì điều này nhận được rất nhiều lượt xem, lưu ý (bình luận của mỗi @ oriol) rằng biểu thức ngắn gọn hơn sau đây có hiệu quả tương đương:

var arr = [].slice.call(htmlCollection);

Nhưng lưu ý mỗi nhận xét của @ JussiR, không giống như biểu mẫu "dài dòng", nó tạo ra một thể hiện mảng trống, không sử dụng và thực sự không sử dụng được trong quy trình. Trình biên dịch làm gì về điều này nằm ngoài ken của lập trình viên.

Biên tập

Kể từ ECMAScript 2015 (ES 6) cũng có Array.from :

var arr = Array.from(htmlCollection);

Biên tập

ECMAScript 2015 cũng cung cấp toán tử trải rộng , tương đương về chức năng Array.from(mặc dù lưu ý Array.fromhỗ trợ chức năng ánh xạ làm đối số thứ hai).

var arr = [...htmlCollection];

Tôi đã xác nhận rằng cả hai công việc trên NodeList.

So sánh hiệu suất cho các phương pháp được đề cập: http://jsben.ch/h2IFA


7
Điều này không thành công trong IE6.
Heath B Border

29
Các phím tắt [].slice.call(htmlCollection)cũng hoạt động.
Oriol

1
@ChrisNielsen Có tôi đã hiểu sai về điều đó. Xin lỗi vì đã lan truyền xung quanh. Tôi đã không nhận ra rằng tôi đã nói rằng ở đây là tốt. Đã xóa nhận xét để tránh nhầm lẫn nhưng đối với ngữ cảnh tôi đã đọc (hoặc đọc sai) ở đâu đó rằng việc cắt một HTMLCollection khiến nó hoạt động giống như cả một mảng và một bộ sưu tập. Hoàn toàn không chính xác.
Erik Reppen

3
Phím tắt [] .slice không tương đương vì nó cũng tạo ra thể hiện mảng trống không sử dụng. Không chắc chắn nếu trình biên dịch có thể tối ưu hóa nó đi, mặc dù.
JussiR

3
Array.from, tức fromlà không được IE11 hỗ trợ.
Frank Conijn

86

không chắc đây có phải là hiệu quả nhất không, nhưng cú pháp ES6 ngắn gọn có thể là:

let arry = [...htmlCollection] 

Chỉnh sửa: Một số khác, từ nhận xét của Chris_F:

let arry = Array.from(htmlCollection)

9
Ngoài ra, ES6 cho biết thêmArray.from()
Chris_F

4
Cảnh giác với cái đầu tiên, có một lỗi tinh vi khi dịch mã với babel trong đó [... htmlCollection] sẽ trả về một mảng với htmlCollection như là phần tử duy nhất.
Marcel M.

3
Toán tử trải rộng mảng không hoạt động trên htmlCollection. Nó chỉ áp dụng cho NodeList.
Bobby

1
Array.from, tức fromlà không được IE11 hỗ trợ.
Frank Conijn

Điểm chuẩn Có vẻ như toán tử lây lan nhanh hơn trong số 2.
RedSparr0w

20

Tôi thấy một phương pháp ngắn gọn hơn để có được Array.prototypecác phương thức nói chung cũng hoạt động tốt. Chuyển đổi một HTMLCollectionđối tượng thành một Arrayđối tượng được thể hiện dưới đây:

[] .slice.call (yourHTMLCollectionObject);

Và, như đã đề cập trong các nhận xét, đối với các trình duyệt cũ như IE7 trở về trước, bạn chỉ cần sử dụng chức năng tương thích, như:

function toArray(x) {
    for(var i = 0, a = []; i < x.length; i++)
        a.push(x[i]);

    return a
}

Tôi biết đây là một câu hỏi cũ, nhưng tôi cảm thấy câu trả lời được chấp nhận là một chút không đầy đủ; Vì vậy, tôi nghĩ rằng tôi sẽ ném nó ra khỏi đó FWIW.


6

Đối với việc thực hiện trình duyệt chéo Tôi muốn sugguest bạn nhìn vào prototype.js $A chức năng

được sao chép từ 1.6.1 :

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Nó không sử dụng Array.prototype.slicecó lẽ vì nó không có sẵn trên mọi trình duyệt. Tôi e rằng hiệu suất khá tệ vì có một điểm yếu là vòng lặp javascript iterable.


2
OP đã yêu cầu một cách khác ngoài "lặp lại nội dung của bộ sưu tập đã nói và đẩy thủ công từng mục vào một mảng", nhưng đó chính xác là những gì $Achức năng thực hiện hầu hết thời gian.
Luc125

1
Tôi nghĩ rằng điểm mà tôi đã cố gắng thực hiện là không có cách nào hay để làm điều đó, mã nguyên mẫu cho thấy rằng bạn có thể tìm kiếm một phương pháp 'toArray' nhưng thất bại trong việc lặp lại con đường an toàn nhất
Gareth Davis

1
Điều này sẽ tạo ra các thành viên mới, không xác định trong các mảng thưa thớt. Cần có một bài kiểm tra hasOwnProperty trước khi chuyển nhượng.
RobG

3

Đây là giải pháp cá nhân của tôi, dựa trên thông tin ở đây (chủ đề này):

var Divs = new Array();    
var Elemns = document.getElementsByClassName("divisao");
    try {
        Divs = Elemns.prototype.slice.call(Elemns);
    } catch(e) {
        Divs = $A(Elemns);
    }

Trong đó $ A được mô tả bởi Gareth Davis trong bài đăng của mình:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Nếu trình duyệt hỗ trợ cách tốt nhất, ok, nếu không sẽ sử dụng trình duyệt chéo.


Nói chung, tôi không mong đợi thử / bắt là một cách hiệu quả để quản lý luồng kiểm soát. Bạn có thể kiểm tra xem hàm có tồn tại trước không, sau đó chạy cái này hoặc cái kia rẻ hơn một chút.
Patrick

2
Như với câu trả lời của Gareth Davis, điều này tạo ra các thành viên mới, không xác định trong các mảng thưa thớt, do đó [,,]trở thành [undefined, undefined].
RobG

Tôi chưa gặp loại rắc rối này. Nó kết hợp bộ sưu tập 3 phần tử trong một mảng có 2 phần tử. Khi trống rỗng không được xác định, đó là một chút hạn chế của JavaScript, tôi cho rằng bạn đang mong đợi null thay vì không xác định, phải không?
Gustavo

3

Điều này hoạt động trong tất cả các trình duyệt bao gồm các phiên bản IE trước đó.

var arr = [];
[].push.apply(arr, htmlCollection);

Vì jsperf vẫn chưa hoạt động vào lúc này, đây là một jsfiddle so sánh hiệu suất của các phương thức khác nhau. https://jsfiddle.net/qw9qf48j/


thửvar args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Shahar Shokrani

3

Để chuyển đổi mảng giống như mảng thành mảng theo cách hiệu quả, chúng ta có thể sử dụng jQuery makeArray :

makeArray: Chuyển đổi một đối tượng giống như mảng thành một mảng JavaScript thực sự.

Sử dụng:

var domArray = jQuery.makeArray(htmlCollection);

Phần thưởng nhỏ nhoi:

Nếu bạn không muốn giữ tham chiếu đến đối tượng mảng (phần lớn thời gian HTMLCollections thay đổi động để sao chép chúng vào mảng khác, ví dụ này rất chú ý đến hiệu suất:

var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.

for (var i = 0 ; i < domDataLength ; i++) {
    resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}

Giống như mảng là gì?

HTMLCollection là một "array-like"đối tượng, mảng giống như đối tượng tương tự như đối tượng mảng nhưng thiếu rất nhiều định nghĩa về mặt chức năng của nó:

Các đối tượng giống như mảng trông giống như mảng. Họ có các yếu tố được đánh số khác nhau và một thuộc tính chiều dài. Nhưng đó là nơi tương tự dừng lại. Các đối tượng giống như mảng không có bất kỳ chức năng nào của Array và các vòng lặp for-in thậm chí không hoạt động!

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.