𝗣𝗿𝗼𝗼𝗳𝗢𝗳𝗔𝗟𝗲𝘀𝘀𝗘𝗳𝗳𝗶𝗰𝗶𝗲𝗻𝘁𝗕𝗶𝗻𝗮𝗿𝘆𝗦𝗲𝗮𝗿𝗰𝗵
ฉันตั้งสมมติฐานว่าด้วยองค์ประกอบที่ลำดับลูก ๆ ทั้งหมดบนเอกสารตามลำดับวิธีที่เร็วที่สุดควรทำการค้นหาแบบไบนารีเปรียบเทียบตำแหน่งเอกสารขององค์ประกอบ อย่างไรก็ตามตามที่แนะนำไว้ในข้อสรุปสมมติฐานถูกปฏิเสธ ยิ่งคุณมีองค์ประกอบมากเท่าไหร่ก็ยิ่งมีศักยภาพในการทำงานมากขึ้นเท่านั้น ตัวอย่างเช่นหากคุณมี 256 องค์ประกอบ (อย่างเหมาะสมที่สุด) คุณจะต้องตรวจสอบเพียง 16 องค์ประกอบเท่านั้น! ราคา 65536 เพียง 256! ประสิทธิภาพเพิ่มขึ้นเป็น 2 เท่า! ดูตัวเลข / สถิติเพิ่มเติม เยี่ยมชมWikipedia
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndex', {
get: function() {
var searchParent = this.parentElement;
if (!searchParent) return -1;
var searchArray = searchParent.children,
thisOffset = this.offsetTop,
stop = searchArray.length,
p = 0,
delta = 0;
while (searchArray[p] !== this) {
if (searchArray[p] > this)
stop = p + 1, p -= delta;
delta = (stop - p) >>> 1;
p += delta;
}
return p;
}
});
})(window.Element || Node);
จากนั้นวิธีที่คุณใช้คือการรับคุณสมบัติ "parentIndex" ขององค์ประกอบใด ๆ ตัวอย่างเช่นดูการสาธิตต่อไปนี้
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndex', {
get: function() {
var searchParent = this.parentNode;
if (searchParent === null) return -1;
var childElements = searchParent.children,
lo = -1, mi, hi = childElements.length;
while (1 + lo !== hi) {
mi = (hi + lo) >> 1;
if (!(this.compareDocumentPosition(childElements[mi]) & 0x2)) {
hi = mi;
continue;
}
lo = mi;
}
return childElements[hi] === this ? hi : -1;
}
});
})(window.Element || Node);
output.textContent = document.body.parentIndex;
output2.textContent = document.documentElement.parentIndex;
Body parentIndex is <b id="output"></b><br />
documentElements parentIndex is <b id="output2"></b>
ข้อ จำกัด
- การใช้โซลูชันนี้จะไม่ทำงานใน IE8 และต่ำกว่า
การค้นหาแบบไบนารี VS เชิงเส้นใน 200,000 องค์ประกอบ (อาจทำให้เบราว์เซอร์มือถือบางตัวขัดข้องระวัง!):
- ในการทดสอบนี้เราจะดูว่าการค้นหาเชิงเส้นใช้เวลานานเพียงใดในการค้นหาองค์ประกอบตรงกลาง VS การค้นหาแบบไบนารี ทำไมต้องเป็นธาตุกลาง? เนื่องจากอยู่ในตำแหน่งเฉลี่ยของสถานที่อื่น ๆ ทั้งหมดดังนั้นจึงแสดงตำแหน่งที่เป็นไปได้ทั้งหมดได้ดีที่สุด
การค้นหาแบบไบนารี
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndexBinarySearch', {
get: function() {
var searchParent = this.parentNode;
if (searchParent === null) return -1;
var childElements = searchParent.children,
lo = -1, mi, hi = childElements.length;
while (1 + lo !== hi) {
mi = (hi + lo) >> 1;
if (!(this.compareDocumentPosition(childElements[mi]) & 0x2)) {
hi = mi;
continue;
}
lo = mi;
}
return childElements[hi] === this ? hi : -1;
}
});
})(window.Element || Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99.9e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=200 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99.9e+3+i+Math.random())).parentIndexBinarySearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the binary search ' + ((end-start)*10).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
}, 125);
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
ย้อนกลับ (`lastIndexOf`) การค้นหาเชิงเส้น
(function(t){"use strict";var e=Array.prototype.lastIndexOf;Object.defineProperty(t.prototype,"parentIndexLinearSearch",{get:function(){return e.call(t,this)}})})(window.Element||Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99e+3+i+Math.random())).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the backwards linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
ส่งต่อ (`indexOf`) การค้นหาเชิงเส้น
(function(t){"use strict";var e=Array.prototype.indexOf;Object.defineProperty(t.prototype,"parentIndexLinearSearch",{get:function(){return e.call(t,this)}})})(window.Element||Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99e+3+i+Math.random())).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the forwards linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
ก่อนหน้า ElementSibling Counter Search
นับจำนวน PreviousElementSiblings เพื่อรับ parentIndex
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndexSiblingSearch', {
get: function() {
var i = 0, cur = this;
do {
cur = cur.previousElementSibling;
++i;
} while (cur !== null)
return i; //Returns 3
}
});
})(window.Element || Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99.95e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=100 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99.95e+3+i+Math.random())).parentIndexSiblingSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the PreviousElementSibling search ' + ((end-start)*20).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
ไม่มีการค้นหา
สำหรับการเปรียบเทียบผลลัพธ์ของการทดสอบจะเป็นอย่างไรหากเบราว์เซอร์ปรับให้เหมาะสมกับการค้นหา
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( true );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the no search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
การถูกกระทบกระแทก
อย่างไรก็ตามหลังจากดูผลลัพธ์ใน Chrome ผลลัพธ์จะตรงกันข้ามกับที่คาดไว้ การค้นหาเชิงเส้นไปข้างหน้าโง่เง่า 187 ms, 3850% เร็วกว่าการค้นหาแบบไบนารี เห็นได้ชัดว่า Chrome ชิงไหวชิงพริบอย่างน่าอัศจรรย์console.assert
และปรับให้เหมาะสมออกไปหรือ (ในแง่ดีกว่านั้น) Chrome ใช้ระบบการจัดทำดัชนีตัวเลขสำหรับ DOM ภายในและระบบการจัดทำดัชนีภายในนี้จะเปิดเผยผ่านการเพิ่มประสิทธิภาพที่ใช้กับArray.prototype.indexOf
เมื่อใช้กับHTMLCollection
วัตถุ
parent.childNodes
แทนที่จะทำอย่างนั้นparent.children
? หลังแสดงรายการเท่านั้นElements
ยกเว้นในText
โหนดเฉพาะ... คำตอบบางส่วนที่นี่เช่นการใช้previousSibling
จะขึ้นอยู่กับการใช้โหนดลูกทั้งหมดในขณะที่คนอื่น ๆ จะใส่ใจกับเด็กที่เป็นElement
... (!)