第' + i + '段開頭文字'
+ '粗體巢狀斜體內容與'
+ '雙層透明codeSample片段'
+ '及標記字又'
+ '含 Latin word 與 supercalifragilistic 長詞,'
+ '句內點號、頓號;冒號:「引號」(括號)測試。末尾實義字甲乙丙丁。
';
}
/** 深巢狀塊(測 orphan S2 之逐塊 querySelector 掃子樹):div 套 depth 層,內含葉段落。 */
function deepBlock(depth, inner)
{
let s = inner;
for (let d = 0; d < depth; d += 1) { s = '
' + s + '
'; }
return s;
}
/** 深巢狀透明行內(測 insidePill/祖先游走:每文本節點沿祖先鏈走 depth 層)。 */
function deepInline(depth, i)
{
let s = '這是深層巢狀行內的文字內容甲乙丙丁。'
+ 'codeX中間再一段實義字戊己庚辛。';
for (let d = 0; d < depth; d += 1) { s = '' + s + ''; }
return '
段' + i + s + '
';
}
function buildHtml()
{
let html = '';
// 寬:scale 段普通段落。
for (let i = 0; i < scale; i += 1) { html += para(i); }
// 深塊:若干「深 8 層 div 包一葉段落」的容器(放大 S2 的 O(Σ塊子樹))。
for (let i = 0; i < Math.max(4, scale / 6); i += 1)
{
html += deepBlock(8, para(1000 + i));
}
// 深行內:若干「深 10 層 span」的段落(放大 insidePill/祖先游走)。
for (let i = 0; i < Math.max(4, scale / 6); i += 1)
{
html += deepInline(10, 2000 + i);
}
html += '';
return html;
}
const HTML = buildHtml();
function freshDom()
{
const dom = new JSDOM("" + HTML + "");
globalThis.document = dom.window.document;
globalThis.NodeFilter = dom.window.NodeFilter;
globalThis.Node = dom.window.Node;
globalThis.window = dom.window;
return dom;
}
/** 找到 matches 所在原型(Element.prototype),返回 { proto, restore }。 */
function patchMatches(dom, counter)
{
let p = dom.window.document.querySelector("strong");
let proto = Object.getPrototypeOf(p);
while (proto && !Object.prototype.hasOwnProperty.call(proto, "matches"))
{
proto = Object.getPrototypeOf(proto);
}
const orig = proto.matches;
proto.matches = function (s) { counter.n += 1; return orig.call(this, s); };
return () => { proto.matches = orig; };
}
function median(arr)
{
const s = arr.slice().sort((a, b) => a - b);
return s[Math.floor(s.length / 2)];
}
// ---- 計數(單次,確定性)----
{
const dom = freshDom();
const counter = { n: 0 };
const restore = patchMatches(dom, counter);
const jz = createJuzhen();
jz.render(dom.window.document.body);
restore();
const doc = dom.window.document;
console.log("結構規模:");
console.log(" 文本節點掃描後 jz-char:", doc.querySelectorAll("jz-char").length);
console.log(" jz-hws:", doc.querySelectorAll("jz-hws").length,
"| jz-jinze:", doc.querySelectorAll("jz-jinze").length,
"| jz-orphan:", doc.querySelectorAll("jz-orphan").length);
console.log(" DOM 元素總數:", doc.querySelectorAll("*").length);
console.log("計數(確定性):");
console.log(" render 期間 el.matches() 呼叫數:", counter.n);
}
// ---- 計時(render 中位數)----
{
const times = [];
for (let k = 0; k < iters; k += 1)
{
const dom = freshDom();
const jz = createJuzhen();
const t0 = performance.now();
jz.render(dom.window.document.body);
times.push(performance.now() - t0);
}
console.log("計時(" + iters + " 次中位數):");
console.log(" render:", median(times).toFixed(2), "ms (min",
Math.min(...times).toFixed(2) + ")");
}
// ---- render→revert 往返計時(第三層增量對照之基線)----
{
const times = [];
for (let k = 0; k < iters; k += 1)
{
const dom = freshDom();
const jz = createJuzhen();
jz.render(dom.window.document.body);
const t0 = performance.now();
jz.revert(dom.window.document.body);
jz.render(dom.window.document.body);
times.push(performance.now() - t0);
}
console.log(" revert+render 全樹往返:", median(times).toFixed(2), "ms");
}