วิธีที่มีประสิทธิภาพมากที่สุดในการแปลง HTMLCollection เป็น Array


391

มีวิธีที่มีประสิทธิภาพมากขึ้นในการแปลง HTMLCollection ไปเป็น Array นอกเหนือจากการวนซ้ำผ่านเนื้อหาของคอลเล็กชั่นที่กล่าวไว้และผลักดันแต่ละรายการเป็นอาร์เรย์ด้วยตนเองหรือไม่?


10
"ประสิทธิภาพ" หมายถึงอะไร หากประสิทธิภาพที่ดีที่สุดเป็นสำหรับวงโดยทั่วไปเร็วกว่าArray.prototype.slice การวนซ้ำยังทำงานในเบราว์เซอร์ที่หลากหลาย (เช่นทั้งหมด) ดังนั้นตามเกณฑ์เหล่านั้นจึงเป็น "วิธีที่มีประสิทธิภาพมากที่สุด" และมันก็เป็นรหัสน้อยมาก: for (var a=[], i=collection.length; i;) a[--i] = collection[i];เพื่อไม่มากของ "นักโทษ" มี :-)
RobG

@RobG ขอบคุณ - ฉันจะให้ + 59k ถ้าฉันทำได้! ;-)
Slashback

1
มองไปที่ประสิทธิภาพการทำงานของเบราว์เซอร์ในปัจจุบัน , ชิ้นได้ติดส่วนใหญ่ขึ้นกับลูปในแง่ของประสิทธิภาพยกเว้นใน Chrome การใช้องค์ประกอบจำนวนมากขึ้นและการเพิ่มประสิทธิภาพของลูปเล็กน้อยผลลัพธ์เกือบจะเหมือนกันยกเว้นใน Chrome ที่มีลูปเร็วกว่ามาก
RobG

ฉันสร้างการทดสอบ jsperf ที่ดูทั้งสองวิธีที่ @harpo พูดถึงและการทดสอบ jquery เพื่อประสิทธิภาพ ฉันได้พบว่า jquery นั้นช้ากว่าวิธี javascript เล็กน้อยและประสิทธิภาพสูงสุดนั้นแตกต่างกันไปในกรณีทดสอบ js Chrome 59.0.3071 / Mac OS X 10.12.5 ชอบArray.prototype.slice.callและใช้Brave (ขึ้นอยู่กับ Chrome 59.0.3071) แทบไม่ต่างจากการทดสอบจาวาสคริปต์สองครั้งในการทดสอบหลายครั้ง ดูjsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon

jsben.ch/h2IFA => การทดสอบประสิทธิภาพสำหรับวิธีทั่วไปในการทำเช่นนี้
EscapeNetscape

คำตอบ:


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

จะมีผลเช่นเดียวกันโดยใช้รหัส "ดั้งเดิม"

แก้ไข

เนื่องจากสิ่งนี้ได้รับมุมมองจำนวนมากโปรดทราบ (ต่อความคิดเห็นของ @ oriol) ว่าการแสดงออกที่กระชับยิ่งขึ้นต่อไปนี้เทียบเท่าได้อย่างมีประสิทธิภาพ :

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

แต่ทราบตามความคิดเห็นของ @ JussiR ซึ่งแตกต่างจากรูปแบบ "verbose" มันสร้างอินสแตนซ์อาร์เรย์ที่ว่างเปล่าไม่ได้ใช้และไม่สามารถใช้งานได้จริงในกระบวนการ คอมไพเลอร์ทำอะไรเกี่ยวกับเรื่องนี้อยู่นอก ken ของโปรแกรมเมอร์

แก้ไข

ตั้งแต่ ECMAScript 2015 (ES 6) ก็มีArray ด้วยเช่นกัน:

var arr = Array.from(htmlCollection);

แก้ไข

ECMAScript 2015 ยังให้บริการตัวดำเนินการสเปรดซึ่งเทียบเท่ากับหน้าที่Array.from(แม้ว่าโปรดทราบว่าArray.fromรองรับฟังก์ชั่นการทำแผนที่เป็นอาร์กิวเมนต์ที่สอง)

var arr = [...htmlCollection];

ฉันยืนยันว่าทั้งสองข้อทำงานNodeListได้ดี

การเปรียบเทียบประสิทธิภาพสำหรับวิธีการที่กล่าวถึง: http://jsben.ch/h2IFA


7
สิ่งนี้ล้มเหลวใน IE6
Heath Borders

29
ทางลัด[].slice.call(htmlCollection)ยังใช้งานได้
Oriol

1
@ChrisNielsen ใช่ฉันเข้าใจผิดแล้ว ขออภัยที่กระจายสิ่งรอบตัว ฉันไม่ทราบว่าฉันระบุไว้ที่นี่เช่นกัน ลบความคิดเห็นเพื่อหลีกเลี่ยงความสับสน แต่สำหรับบริบทฉันได้อ่าน (หรืออ่านผิด) บางที่การแบ่ง HTMLCollection ทำให้มันทำงานเหมือนทั้งอาร์เรย์และคอลเลกชัน ไม่ถูกต้องทั้งหมด
Erik Reppen

3
ทางลัด [] .slice ไม่เทียบเท่ากันเนื่องจากมันจะสร้างอินสแตนซ์อาร์เรย์ที่ว่างเปล่า ไม่แน่ใจว่าคอมไพเลอร์สามารถเพิ่มประสิทธิภาพได้หรือไม่
JussiR

3
Array.fromคือfromไม่ได้รับการสนับสนุนโดย IE11
Frank Conijn

86

ไม่แน่ใจว่าสิ่งนี้มีประสิทธิภาพมากที่สุดหรือไม่ แต่ไวยากรณ์ ES6 ที่กระชับอาจเป็นดังนี้:

let arry = [...htmlCollection] 

แก้ไข: อีกความคิดเห็นหนึ่งจาก Chris_F:

let arry = Array.from(htmlCollection)

9
นอกจากนี้ ES6 เพิ่มArray.from()
Chris_F

4
ระวังสิ่งแรกมีข้อผิดพลาดที่ละเอียดอ่อนเมื่อทำการสลับกับ Babel โดยที่ [... htmlCollection] จะส่งกลับอาร์เรย์ด้วย htmlCollection เนื่องจากเป็นองค์ประกอบเท่านั้น
Marcel M.

3
โอเปอเรเตอร์การแพร่กระจายอาร์เรย์ไม่ทำงานบน htmlCollection มันใช้ได้กับ NodeList เท่านั้น
Bobby

1
Array.fromคือfromไม่ได้รับการสนับสนุนโดย IE11
Frank Conijn

เกณฑ์มาตรฐานดูเหมือนว่าตัวดำเนินการส
เปรด

20

ฉันเห็นวิธีรัดกุมมากขึ้นในการรับArray.prototypeวิธีโดยทั่วไปที่ใช้งานได้เช่นกัน การแปลงHTMLCollectionวัตถุเป็นArrayวัตถุแสดงให้เห็นด้านล่าง:

[] .slice.call (yourHTMLCollectionObject);

และตามที่ระบุไว้ในความคิดเห็นสำหรับเบราว์เซอร์เก่าเช่น IE7 และก่อนหน้านี้คุณเพียงแค่ต้องใช้ฟังก์ชั่นความเข้ากันได้เช่น:

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

    return a
}

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันรู้สึกว่าคำตอบที่ยอมรับนั้นไม่สมบูรณ์เล็กน้อย ดังนั้นฉันคิดว่าฉันจะโยนมันออกไปที่นั่น FWIW


6

สำหรับการใช้ข้ามเบราว์เซอร์ฉันขอแนะนำให้คุณดูฟังก์ชั่นprototype.js $A

คัดลอกจาก 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;
}

อาจไม่ได้ใช้Array.prototype.sliceเพราะอาจไม่สามารถใช้ได้กับทุกเบราว์เซอร์ iterableฉันกลัวประสิทธิภาพไม่ดีงามที่มีกลับฤดูใบไม้ร่วงเป็นห่วงจาวาสคริปต์มากกว่า


2
OP ถามหาวิธีอื่นที่ไม่ใช่ "วนซ้ำเนื้อหาของคอลเลคชั่นที่กล่าวไว้และผลักแต่ละรายการเข้าสู่อาเรย์ด้วยตนเอง" แต่นั่นคือสิ่งที่$Aฟังก์ชั่นใช้เวลาส่วนใหญ่อย่างแม่นยำ
Luc125

1
ฉันคิดว่าประเด็นที่ฉันพยายามทำคือไม่มีวิธีที่ดีที่จะทำมันรหัส prototype.js แสดงให้เห็นว่าคุณสามารถมองหาวิธี 'toArray' แต่ไม่สามารถทำซ้ำเส้นทางที่ปลอดภัยที่สุด
Gareth Davis

1
สิ่งนี้จะสร้างสมาชิกใหม่ที่ไม่ได้กำหนดในอาร์เรย์เบาบาง ควรมีการทดสอบhasOwnPropertyก่อนการมอบหมาย
RobG

3

นี่คือโซลูชันส่วนบุคคลของฉันตามข้อมูลที่นี่ (หัวข้อนี้):

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

Gareth Davis ได้อธิบาย $ ไว้ที่ไหนในโพสต์ของเขา

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;
}

หากเบราว์เซอร์รองรับวิธีที่ดีที่สุดไม่เช่นนั้นจะใช้ข้ามเบราว์เซอร์


โดยทั่วไปแล้วฉันไม่คาดหวังว่าลอง / จับเป็นวิธีที่มีประสิทธิภาพในการจัดการโฟลว์ควบคุม คุณสามารถตรวจสอบว่าฟังก์ชั่นนั้นมีอยู่ก่อนจากนั้นเรียกใช้ฟังก์ชั่นหนึ่งหรืออีกอันหนึ่งถูกกว่า
แพทริค

2
เช่นเดียวกับคำตอบของแกเร็ ธ เดวิสนี้จะสร้างใหม่สมาชิกที่ไม่ได้กำหนดในอาร์เรย์เบาบางจึงกลายเป็น[,,] [undefined, undefined]
RobG

ฉันยังไม่ได้รับปัญหาเช่นนี้ มันรวบรวม 3 องค์ประกอบผลลัพธ์ในอาร์เรย์ที่มี 2 องค์ประกอบ สำหรับความว่างเปล่านั้นไม่ได้ถูกนิยามไว้มันมีข้อ จำกัด เล็กน้อยของ JavaScript ฉันว่าคุณคาดหวังว่าเป็นโมฆะแทนที่จะเป็นไม่ได้ถูกต้องใช่ไหม
Gustavo

3

ใช้ได้กับเบราว์เซอร์ทุกรุ่นรวมถึง IE เวอร์ชันก่อนหน้า

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

เนื่องจาก jsperf ยังคงไม่ทำงานในขณะนี้นี่คือ jsfiddle ที่เปรียบเทียบประสิทธิภาพของวิธีการที่แตกต่างกัน https://jsfiddle.net/qw9qf48j/


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

3

ในการแปลงอาเรย์ให้เป็นอาเรย์อย่างมีประสิทธิภาพเราสามารถใช้ประโยชน์จากjQuery makeArray :

makeArray: แปลงวัตถุคล้ายอาร์เรย์เป็นอาร์เรย์ JavaScript จริง

การใช้งาน:

var domArray = jQuery.makeArray(htmlCollection);

พิเศษเล็กน้อย:

หากคุณไม่ต้องการที่จะอ้างอิงถึงวัตถุอาร์เรย์ (ส่วนใหญ่เวลา HTMLCollections มีการเปลี่ยนแปลงแบบไดนามิกดังนั้นจึงดีกว่าที่จะคัดลอกลงในอาร์เรย์อื่นตัวอย่างนี้ใส่ใจกับประสิทธิภาพ:

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.
}

อาเรย์เป็นอย่างไร

HTMLCollectionเป็น"array-like"วัตถุที่อาร์เรย์เหมือนวัตถุที่มีความคล้ายคลึงกับวัตถุอาร์เรย์ แต่หายไปจำนวนมากของการกำหนดหน้าที่ของมัน:

วัตถุที่มีลักษณะคล้ายอาร์เรย์ดูเหมือนอาร์เรย์ พวกเขามีองค์ประกอบหมายเลขต่างๆและคุณสมบัติความยาว แต่นั่นคือสิ่งที่ความคล้ายคลึงกันหยุด วัตถุที่เหมือนอาร์เรย์ไม่มีฟังก์ชั่นใด ๆ ของอาเรย์และลูปในตัวไม่ทำงานแม้แต่อย่างเดียว!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.