「MathJaxをデバッグしてJavaScriptの理解を深める ページ3」の版間の差分
(→ページ) |
|||
(同じ利用者による、間の24版が非表示) | |||
499行目: | 499行目: | ||
ん?startup.jsって何を受け持つファイルなんだろうな。あーなるほど。本来は全てはstartup.jsを呼び出す方式を使うのが普通なんだな。自分らが使っているtex-mml-chtmlとかは、出力の形式とかを設定しないで呼び出す楽々セットなんだな。だから基本的には、楽々セットの起動URLを使ってもstartup.jsは呼び出そうとするんだろう。楽々セットの癖に5万行になっているのはかなり独立してるんだろうね。MathJaxのそもそものオプション機能の多さについても理解しておいた方がよいのかもしれない。単純に数式をHTMLにするだけの機能ではないからね。SVGにもできれば、数式番号のリセットやら追加の描画呼び出しといったこともできるし、描画処理がどこまで進んだからで呼び出せる関数もあるみたい。でも、自分は、全貌を見ずに先頭から見ていく。 | ん?startup.jsって何を受け持つファイルなんだろうな。あーなるほど。本来は全てはstartup.jsを呼び出す方式を使うのが普通なんだな。自分らが使っているtex-mml-chtmlとかは、出力の形式とかを設定しないで呼び出す楽々セットなんだな。だから基本的には、楽々セットの起動URLを使ってもstartup.jsは呼び出そうとするんだろう。楽々セットの癖に5万行になっているのはかなり独立してるんだろうね。MathJaxのそもそものオプション機能の多さについても理解しておいた方がよいのかもしれない。単純に数式をHTMLにするだけの機能ではないからね。SVGにもできれば、数式番号のリセットやら追加の描画呼び出しといったこともできるし、描画処理がどこまで進んだからで呼び出せる関数もあるみたい。でも、自分は、全貌を見ずに先頭から見ていく。 | ||
===== t.loadAll ===== | ===== t.loadAll ===== | ||
以下のような関数です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.loadAll = function() { | |||
var t, e; | |||
try { | |||
for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) { | |||
var o = n.value; | |||
o.canLoad && o.load() | |||
} | |||
} catch(e) { | |||
t = { | |||
error: e | |||
} | |||
} finally { | |||
try { | |||
n && !n.done && (e = r.return) && e.call(r) | |||
} finally { | |||
if(t) throw t.error | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
この関数の繰り返し部で得られる個別の値はものすごく大きい配列です。 | |||
とはいいつつも、 | |||
name: "[tex]/action", | |||
name: "[tex]/ams", | |||
name: "[tex]/autoload", | |||
name: "[tex]/configmacros", | |||
name: "[tex]/newcommand", | |||
name: "[tex]/noundefined", | |||
name: "[tex]/require", | |||
name: "a11y/assistive-mml", | |||
name: "core", | |||
name: "input/mml", | |||
name: "input/tex", | |||
name: "input/tex-base", | |||
name: "loader", | |||
name: "output/chtml", | |||
name: "output/chtml/fonts/tex.js", | |||
name: "startup" | |||
name: "ui/menu", | |||
のような値のそれぞれに isLoaded: true, isLoading: false, hasFailed: false, dependents: dependencies: provided: [[Circular],…, dependencyCount: 0, provided: [resolve: function () { [native code] }, reject: function () { [native code] },promise: { … といったような構造を持った値です。 | |||
a11y は accessibility の間を数字に置き換えた。a11yという表現です。こういう表現と言うのはいくつかあります。i18nとかね。こういうのをヌメロニムというそうで。以下のようなものが代表的です。 | |||
*a11y - Accessibility(利用しやすさ) | |||
*c14n - Canonicalization(正準,正規化) | |||
*d11n - Documentation(文書化) | |||
*G11n - Globalisation / Globalization(世界化) | |||
*'''<span style="color: darkred;">i18n - Internationalisation / Internationalization(国際化)</span>''' | |||
*i14y - Interoperability(相互運用性)[7] | |||
*K8s - Kubernetes | |||
*L10n - Localisation / Localization(地域化)[5][6] | |||
*m17n - Multilingualisation / Multilingualization(多言語化)[5] | |||
*n11n - Normalisation / Normalization(正規化)[8] | |||
*P13n - Personalisation / Personalization(個人化) | |||
*P45 - Pneumonoultramicroscopicsilicovolcanoconiosis | |||
*tr8n - Translation(翻訳)[9] | |||
*v12n - Virtualisation / Virtualization(仮想化) | |||
*D34n - Demand response based resource provision | |||
それぞれの o.canLoad が true なら o.load() が実行されます。o.load() は 以下のような関数で、 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.load = function() { | |||
if(!this.isLoaded && !this.isLoading && !this.noLoad) { | |||
this.isLoading = !0; | |||
var e = t.resolvePath(this.name); | |||
l.CONFIG.require ? this.loadCustom(e) : this.loadScript(e) | |||
} | |||
</syntaxhighlight> | |||
isLoaded が false だったときに if の条件式が成立するようです。isLoad が false なら後ろの二つはもちろんくらいにflase になっているはずです。大体が this.isLoaded = true, this.isLoading = false, this.noLoad = false の状態で呼ばれますので、念のためのLoad チェック処理のように思えます。 | |||
どんな値がcanLoad = true なのかをみると core, input/tex-base, [tex]/newcommand, output/chtml, [tex]/action ★ では load の if文の中に入りますので、 isLoading が true になり、 resolvePath('[tex]/action')が実行されます。'file:///[DriveAlphabet:/MathJaxPath]/es5/input/tex/extensions/action.js'が得られます。l.CONFIG.require で必要とされているなら、loadCustom そうでないなら loadScript が実行されます。 | |||
loadScriptは以下のような関数で | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.loadScript = function(t) { | |||
var e = this, | |||
r = document.createElement("script"); | |||
r.src = t, r.charset = "UTF-8", r.onload = function(t) { | |||
return e.checkLoad() | |||
}, r.onerror = function(r) { | |||
return e.failed("Can't load \"" + t + '"') | |||
}, document.head.appendChild(r) | |||
} | |||
</syntaxhighlight> | |||
この関数でheadタグの中にscriptタグが付け足されます。'<script src="file:///[DriveAlphabet:/MathJaxPath]/es5/input/tex/extensions/action.js" charset="UTF-8"></script>'というものです。 | |||
===== t.prototype.makeDependencies ===== | ===== t.prototype.makeDependencies ===== | ||
以下のような関数です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.makeDependencies = function() { | |||
var e, r, n = [], | |||
o = t.packages, | |||
c = this.noLoad, | |||
u = this.name, | |||
p = []; | |||
l.CONFIG.dependencies.hasOwnProperty(u) ? | |||
p.push.apply(p, a([], s(l.CONFIG.dependencies[u]), !1)) | |||
: | |||
"core" !== u && p.push("core"); | |||
try { | |||
for(var h = i(p), f = h.next(); !f.done; f = h.next()) { | |||
var d = f.value, | |||
m = o.get(d) || new t(d, c); | |||
this.dependencies.indexOf(m) < 0 && | |||
(m.addDependent(this, c), | |||
this.dependencies.push(m), m.isLoaded || | |||
(this.dependencyCount++, n.push(m.promise))) | |||
} | |||
} | |||
catch(t) { e = { error: t }} | |||
finally { | |||
try { f && !f.done && (r = h.return) && r.call(h) } | |||
finally { if(e) throw e.error } | |||
} | |||
return n | |||
} | |||
</syntaxhighlight> | |||
t.packages は {0 : {key: 'loader', value: {name: t, isLoader = false, isLoading = false, hasFailed = false}} のような値です。this.noLoad = true, this.name = 'loader'で、l.CONFIG.dependencies.hasOwnProperty(u = 'loader')はflaseなのでloaderというメンバは定義されていません。"core" !== u && p.push("core"); が実行され、u=loaderのときは p に coreという配列値がプッシュされます。t.packages → o の core がメンバにあるか確認して undefined なので new t('core' , true) の処理結果が m に 'core' が格納される。'core' の処理順序制御についての構造が生成される。Dependents 配列に 'core' があるかを確認し、あれば 0 以上が返ってくるので、なければ -1 で論理演算の積の 0 が出てくるまで処理をすすめる仕組みを利用して次の処理をする。addDependent(this, true) 、 this.dependencies.push('core')、 m.isLoaded = false なので this.dependencyCount++ で増加させて、 n.push(m.promise) で n に Promise を 配列に追加。n は戻り値なので、この関数は Promise の配列追加処理が主な役割ということになりそうです。こういった処理を makeDependencies と言っているようです。 | |||
addDependent は 以下のようなものです。dependentsメンバ変数の配列に this オブジェクトを登録するような処理です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.addDependent = function(t, e) { | |||
this.dependents.push(t), e || this.checkNoLoad() | |||
} | |||
</syntaxhighlight> | |||
配列への追加が完了すると this.checkNoLoad() 関数を実行します。 | |||
checkNoLoad は以下のようなものです。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.checkNoLoad = function() { | |||
var t, e; | |||
if(this.noLoad) { | |||
this.noLoad = !1; | |||
try { | |||
for(var r = i(this.dependencies), n = r.next(); !n.done; n = r.next()) { | |||
n.value.checkNoLoad() | |||
} | |||
} | |||
catch(e) { t = { error: e } } | |||
finally { | |||
try { | |||
n && !n.done && (e = r.return) && e.call(r) | |||
} | |||
finally { if(t) throw t.error } | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
呼び出し元の処理順序制御構造オブジェクトの noLoad を 再帰的に false にしていく関数です。 | |||
===== t.prototype.makePromise ===== | ===== t.prototype.makePromise ===== | ||
以下のような関数です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.makePromise = function(t) { | |||
var e = this, | |||
r = new Promise((function(t, r) { | |||
e.resolve = t, | |||
e.reject = r | |||
})), | |||
n = l.CONFIG[this.name] || {}; | |||
return | |||
n.ready && | |||
( | |||
r = r.then((function(t) { | |||
return n.ready(e.name) | |||
})) | |||
), | |||
t.length && | |||
( | |||
t.push(r), | |||
r = Promise.all(t).then((function(t) { | |||
return t.join(", ") | |||
})) | |||
), | |||
n.failed && | |||
r.catch( | |||
(function(t) { | |||
return n.failed(new c(t, e.name)) | |||
}) | |||
), r | |||
} | |||
</syntaxhighlight> | |||
同期処理が成功したら e.resolve = t 失敗したら e.reject = r を呼び出します。そして n に l.CONFIG[this.name] つまり、l.CONFIG['core'] = undefined を格納します。戻り値はカンマ構文の最後の値 r です。処理が失敗したときの r です。n.ready が true なら r は失敗を通知されたら l.CONFIG['core'].ready('core') のような関数を実行します。t が空で無ければ、その r を t の配列に取り込みます。処理順序制御した関数の全てが終わったら、それらの関数名を【,】区切りで結合していきます。n. ready が false や n.failed が true なら エラー処理としてn.failed(new c(t, 'core'))のような処理を実行します。 | |||
===== t.prototype.load ===== | ===== t.prototype.load ===== | ||
[[MathJaxをデバッグしてJavaScriptの理解を深める_ページ3#t.loadAll|t.loadAll]]で説明されてる動きです。 | |||
===== t.prototype.loadCustom ===== | ===== t.prototype.loadCustom ===== | ||
今回の例では呼ばれない関数になっています。 | |||
===== t.prototype.loadScript ===== | ===== t.prototype.loadScript ===== | ||
[[MathJaxをデバッグしてJavaScriptの理解を深める_ページ3#t.loadAll|t.loadAll]]で説明されてる動きです。 | |||
===== t.prototype.loaded ===== | ===== t.prototype.loaded ===== | ||
以下のような関数です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.loaded = function() { | |||
var t, e, r, n; | |||
this.isLoaded = !0, this.isLoading = !1; | |||
try { | |||
for(var o = i(this.dependents), s = o.next(); !s.done; s = o.next()) { | |||
s.value.requirementSatisfied() | |||
} | |||
} | |||
catch(e) { t = {error: e } } | |||
finally { | |||
try { s && !s.done && (e = o.return) && e.call(o) } | |||
finally { if(t) throw t.error } } | |||
try { | |||
for(var a = i(this.provided), l = a.next(); !l.done; l = a.next()) { | |||
l.value.loaded() | |||
} | |||
} | |||
catch(t) { r = { error: t } } | |||
finally { | |||
try { l && !l.done && (n = a.return) && n.call(a) } | |||
finally { if(r) throw r.error } | |||
} | |||
this.resolve(this.name) | |||
} | |||
</syntaxhighlight> | |||
まず状態フラグの this.isLoaded = true, this.isLoading = false を設定します。最終的に this.resolve('loader') → this.resolve('startup') → this.resolve('core') のような関数を呼び出して処理を終えます。core のときだけthis.dependentsの値が 1: {… name:'loader' …} 2: {… name:'startup' …} という値を持っているためs.value.requirementSatisfied()が実行されます。 | |||
以下のような関数です。 | |||
<syntaxhighlight lang="javascript"> | |||
t.prototype.requirementSatisfied = function() { | |||
this.dependencyCount && (this.dependencyCount--, this.canLoad && this.load()) | |||
} | |||
</syntaxhighlight> | |||
ふむ、あれかな、Loadedだから読み込み終わったってことで、Promise で実行するオブジェクトも一つ減るので、this.dependencyCount--で一つ減らしたり、this.canLoad && this.load() のようにLoadできるならLoadしようとする処理が入っているような。ともかく最初は、core やら loader とか startup のようなものを処理順序制御をしながら実行している感じが続いてるような気がするね。実に細かい。同じようなことを何回も繰り返してる。処理手順を短くする速さやメモリの消費を抑えようという感じがあまりしないけど、めんどくさいことをしっかりやって着実に準備、という感じだな。 | |||
===== t.prototype.failed ===== | ===== t.prototype.failed ===== | ||
今回の例では呼ばれない関数になっています。 | |||
===== t.prototype.requirementSatisfied ===== | ===== t.prototype.requirementSatisfied ===== | ||
[[MathJaxをデバッグしてJavaScriptの理解を深める_ページ3#t.prototype.loaded|t.prototype.loaded]]で説明されてる動きです。 | |||
===== t.prototype.addDependent ===== | ===== t.prototype.addDependent ===== | ||
[[MathJaxをデバッグしてJavaScriptの理解を深める_ページ3#t.prototype.makePromise|t.prototype.makePromise]]で説明されてる動きです。 | |||
===== t.prototype.checkNoLoad ===== | ===== t.prototype.checkNoLoad ===== | ||
[[MathJaxをデバッグしてJavaScriptの理解を深める_ページ3#t.prototype.makePromise|t.prototype.makePromise]]で説明されてる動きです。 | |||
547行目: | 799行目: | ||
=== ページ === | === ページ === | ||
*[[MathJaxをデバッグしてJavaScriptの理解を深める]] | *[[MathJaxをデバッグしてJavaScriptの理解を深める]] | ||
*[[MathJaxをデバッグしてJavaScriptの理解を深める ページ2]] | *前:[[MathJaxをデバッグしてJavaScriptの理解を深める ページ2]] | ||
*[[MathJaxをデバッグしてJavaScriptの理解を深める ページ4]] | *次:[[MathJaxをデバッグしてJavaScriptの理解を深める ページ4]] | ||
2022年12月7日 (水) 18:28時点における最新版
概要
MathJaxをデバッグしてJavaScriptの理解を深める ページ2の続きです。
__webpack_modules__[265].call(r.exports, r, r.exports, __webpack_require__)
265:
function(t, e, r) {
var n, o = this && this.__extends || (n = function(t, e) {
return n = Object.setPrototypeOf || {
__proto__: []
}
instanceof Array && function(t, e) {
t.__proto__ = e
} || function(t, e) {
for(var r in e) Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r])
}, n(t, e)
},
function(t, e) {
if("function" != typeof e && null !== e)
throw new TypeError("Class extends value " + String(e) + " is not a constructor or null");
function r() {
this.constructor = t
}
n(t, e), t.prototype = null === e ? Object.create(e) : (r.prototype = e.prototype, new r)
}),
i = this && this.__values || function(t) {
var e = "function" == typeof Symbol && Symbol.iterator,
r = e && t[e],
n = 0;
if(r) return r.call(t);
if(t && "number" == typeof t.length) return {
next: function() {
return t && n >= t.length && (t = void 0), {
value: t && t[n++],
done: !t
}
}
};
throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
},
s = this && this.__read || function(t, e) {
var r = "function" == typeof Symbol && t[Symbol.iterator];
if(!r) return t;
var n, o, i = r.call(t),
s = [];
try {
for(;
(void 0 === e || e-- > 0) && !(n = i.next()).done;
)
s.push(n.value)
}
catch(t) { o = { error: t } }
finally {
try { n && !n.done && (r = i.return) && r.call(i) }
finally { if(o) throw o.error }
}
return s
},
a = this && this.__spreadArray || function(t, e, r) {
if(r || 2 === arguments.length)
for(var n, o = 0, i = e.length; o < i; o++)
!n && o in e || (n || (n = Array.prototype.slice.call(e, 0, o)), n[o] = e[o]);
return t.concat(n || Array.prototype.slice.call(e))
};
Object.defineProperty(e, "__esModule", {
value: !0
}),
e.Package = e.PackageError = void 0;
var l = r(235),
c = function(t) {
function e(e, r) {
var n = t.call(this, e) || this;
return n.package = r, n
}
return o(e, t), e
}(Error);
e.PackageError = c;
var u = function() {
function t(e, r) {
void 0 === r && (r = !1),
this.isLoaded = !1,
this.isLoading = !1,
this.hasFailed = !1,
this.dependents = [],
this.dependencies = [],
this.dependencyCount = 0,
this.provided = [],
this.name = e,
this.noLoad = r,
t.packages.set(e, this),
this.promise = this.makePromise(this.makeDependencies())
}
return Object.defineProperty(t.prototype, "canLoad", {
get: function() {
return 0 === this.dependencyCount && !this.noLoad && !this.isLoading && !this.hasFailed
},
enumerable: !1,
configurable: !0
}),
t.resolvePath = function(t, e) {
void 0 === e && (e = !0);
var r = {
name: t,
original: t,
addExtension: e
};
return l.Loader.pathFilters.execute(r), r.name
},
t.loadAll = function() {
var t, e;
try {
for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) {
var o = n.value;
o.canLoad && o.load()
}
}
catch(e) { t = { error: e } }
finally {
try { n && !n.done && (e = r.return) && e.call(r) }
finally { if(t) throw t.error }
}
},
t.prototype.makeDependencies = function() {
var e, r, n = [],
o = t.packages,
c = this.noLoad,
u = this.name,
p = [];
l.CONFIG.dependencies.hasOwnProperty(u) ?
p.push.apply(p, a([], s(l.CONFIG.dependencies[u]), !1))
:
"core" !== u && p.push("core");
try {
for(var h = i(p), f = h.next(); !f.done; f = h.next()) {
var d = f.value,
m = o.get(d) || new t(d, c);
this.dependencies.indexOf(m) < 0 &&
(
m.addDependent(this, c),
this.dependencies.push(m),
m.isLoaded || (this.dependencyCount++, n.push(m.promise))
)
}
}
catch(t) { e = { error: t } }
finally {
try { f && !f.done && (r = h.return) && r.call(h) }
finally { if(e) throw e.error }
}
return n
},
t.prototype.makePromise = function(t) {
var e = this,
r = new Promise((function(t, r) {
e.resolve = t, e.reject = r
})),
n = l.CONFIG[this.name] || {};
return n.ready && (r = r.then((function(t) {
return n.ready(e.name)
}))), t.length && (t.push(r), r = Promise.all(t).then((function(t) {
return t.join(", ")
}))), n.failed && r.catch((function(t) {
return n.failed(new c(t, e.name))
})), r
},
t.prototype.load = function() {
if(!this.isLoaded && !this.isLoading && !this.noLoad) {
this.isLoading = !0;
var e = t.resolvePath(this.name);
l.CONFIG.require ? this.loadCustom(e) : this.loadScript(e)
}
},
t.prototype.loadCustom = function(t) {
var e = this;
try {
var r = l.CONFIG.require(t);
r instanceof Promise ? r.then((function() {
return e.checkLoad()
})).catch((function(r) {
return e.failed("Can't load \"" + t + '"\n' + r.message.trim())
})) : this.checkLoad()
} catch(t) {
this.failed(t.message)
}
},
t.prototype.loadScript = function(t) {
var e = this,
r = document.createElement("script");
r.src = t, r.charset = "UTF-8", r.onload = function(t) {
return e.checkLoad()
},
r.onerror = function(r) {
return e.failed("Can't load \"" + t + '"')
},
document.head.appendChild(r)
},
t.prototype.loaded = function() {
var t, e, r, n;
this.isLoaded = !0, this.isLoading = !1;
try {
for(var o = i(this.dependents), s = o.next(); !s.done; s = o.next()) {
s.value.requirementSatisfied()
}
}
catch(e) { t = { error: e } }
finally {
try { s && !s.done && (e = o.return) && e.call(o) }
finally { if(t) throw t.error }
}
try {
for(var a = i(this.provided), l = a.next(); !l.done; l = a.next()) {
l.value.loaded()
}
}
catch(t) { r = { error: t } }
finally {
try { l && !l.done && (n = a.return) && n.call(a) }
finally { if(r) throw r.error }
}
this.resolve(this.name)
},
t.prototype.failed = function(t) {
this.hasFailed = !0, this.isLoading = !1, this.reject(new c(t, this.name))
},
t.prototype.checkLoad = function() {
var t = this;
((l.CONFIG[this.name] || {}).checkReady || function() {
return Promise.resolve()
})().
then((function() {
return t.loaded()
})).
catch((function(e) {
return t.failed(e)
}))
},
t.prototype.requirementSatisfied = function() {
this.dependencyCount && (this.dependencyCount--, this.canLoad && this.load())
},
t.prototype.provides = function(e) {
var r, n;
void 0 === e && (e = []);
try {
for(var o = i(e), s = o.next(); !s.done; s = o.next()) {
var a = s.value,
c = t.packages.get(a);
c ||
(l.CONFIG.dependencies[a] ||
(l.CONFIG.dependencies[a] = []),
l.CONFIG.dependencies[a].push(a),
(c = new t(a, !0)).isLoading = !0),
this.provided.push(c)
}
}
catch(t) { r = { error: t } }
finally {
try { s && !s.done && (n = o.return) && n.call(o) }
finally { if(r) throw r.error }
}
},
t.prototype.addDependent = function(t, e) {
this.dependents.push(t), e || this.checkNoLoad()
},
t.prototype.checkNoLoad = function() {
var t, e;
if(this.noLoad) {
this.noLoad = !1;
try {
for(var r = i(this.dependencies), n = r.next(); !n.done; n = r.next()) {
n.value.checkNoLoad()
}
}
catch(e) { t = { error: e } }
finally {
try { n && !n.done && (e = r.return) && e.call(r) }
finally { if(t) throw t.error }
}
}
},
t.packages = new Map, t
}();
e.Package = u
},
長い関数だけど、関数を呼び出した時に実行されるのは、ほとんどが関数の定義だけされる感じ。でもどんな処理をする関数かは眺めてみる。定義されるのは n → i → s → a → Object.defineProperty → e.Package → var l → var c → e.PackageError → var u (膨大な tのメンバ関数 を定義する関数) → e.Package という順。それぞれどんな関数か見ていきます。 前の項目で確認した部分も出てきます。265 自体は前の235番号関数定義の中で呼ばれるので、その関数で使われる部分もありました。__webpack_modules__[235].call の中で __webpack_modules__[265].callが呼ばれています。__webpack_require__(265)がその後よばれたときには、export{ 265:{}} というような既に実行したという形跡を残す処理をします。
n
for文のところが先に呼ばれる。t と e は以下のような値です。まず e から
e = { name: 'Error' stackTraceLimit: 10 }
t= { name: 'e' function(e, r) {var n = t.call(this, e) || this;return n.package = r, n} }
この n の関数は return として 3個のfunction(t,e) の関数をもっています。{__proto__: []} が 配列 なら 1つめの関数t.__proto__に eを格納して、2つめの関数で e の要素をひとつづつ取り出して、r に格納しながら hasOwnProperty.call(e, r) を実行します。そして呼び出された引数の t[r] に e[r] を格納します。そうやって再帰的に n の関数を実行します。3つめの関数で e の形式が function でもなく null でもないなら例外を発生させて、Error のときに実行される catch引数に if("function" != typeof e && null !== e) throw new TypeError("Class extends value " + String(e) + " is not a constructor or null"); を渡して実行します。3つ目の関数の最後に関数 r を t を返すようなthis.constructor を設定してます。また再帰的に n を処理しようとするカンマ構文があって、最後に e が null なら Object を新規に生成し、違えば rを返す関数のメンバ関数の r.prototype に e.prototype の内容を格納する。
i
i は Itratorを作成する関数です。
s
s に渡される t は ['input/tex-base'] → ['input/tex-base', '[tex]/ams'] → ['input/tex-base', '[tex]/newcommand'] → ['input/tex-base', '[tex]/noundefined'] → ['input/tex-base', '[tex]/require'] → ['input/tex-base', '[tex]/require', '[tex]/autoload'] → ['input/tex-base', '[tex]/newcommand', '[tex]/configmacros'] → ['input/tex-base', '[tex]/newcommand']のような配列です。e は undefined とかです。
s に t の値をプッシュするような関数です。265関数内のt.prototype.makeDependenciesという部分で処理されます。
a
a に渡される引数 t, e, r は t が []、 e は ['input/tex-base'] → ['input/tex-base', '[tex]/ams'] → ['input/tex-base', '[tex]/newcommand'] → ['input/tex-base', '[tex]/noundefined'] → ['input/tex-base', '[tex]/require'] → ['input/tex-base', '[tex]/require', '[tex]/autoload'] → ['input/tex-base', '[tex]/newcommand', '[tex]/configmacros'] → ['input/tex-base', '[tex]/newcommand']、r は flase のような値です。e の値を t にコピーして結合していくような関数です。
Object.defineProperty
実行すると e の __esModule が true になります。{PackageError: undefined, Package: undefined, __esModule: true}
e.Package
e.Package は undefined になる。
var l
l は、以下のような値になります。
{ Package: undefined, PackageError: undefined, PathFilters: undefined, Loader: undefined, MathJax: undefined, CONFIG: undefined, }
var c
は、今のサンプルだと呼ばれないね。
e.PackageError
var u
u の中で呼ばれる関数はいかのようなモノ。
function t(e, r) {
void 0 === r && (r = !1),
this.isLoaded = !1,
this.isLoading = !1,
this.hasFailed = !1,
this.dependents = [],
this.dependencies = [],
this.dependencyCount = 0,
this.provided = [],
this.name = e,
this.noLoad = r,
t.packages.set(e, this),
this.promise = this.makePromise(this.makeDependencies())
}
で、このときの e は 'loader' です。r は、true です。
this.makeDependencies は、この u の中で定義される関数で以下のようなモノです。
l.CONFIG.dependencies.hasOwnProperty(u) ?
p.push.apply(p, a([], s(l.CONFIG.dependencies[u]), !1))
:
"core" !== u && p.push("core");
try {
for(var h = i(p), f = h.next(); !f.done; f = h.next()) {
var d = f.value,
m = o.get(d) || new t(d, c);
this.dependencies.indexOf(m) < 0 &&
(
m.addDependent(this, c),
this.dependencies.push(m),
m.isLoaded ||
(this.dependencyCount++, n.push(m.promise))
)
}
} catch(t) {
e = {
error: t
}
} finally {
try {
f && !f.done && (r = h.return) && r.call(h)
} finally {
if(e) throw e.error
}
}
return n
}
この u という引数には 'core' → 'startup' とかという値が入ります。core のときは、判定式が false なので push 処理はされず、p が空ならば何も処理されません。startupのときは p が ['core'] になります。繰り返しの処理の個別の値で d が 'core' になって、
t.packages.set(e, this); → o = t.packages → o.get(d = 'core') で c = this.noLoad = true; m = o.get(d) || new t(d, c);
t.package.get('core') があれば、 m = new t('core', true) を実行します。 Promiseのcoreの登録がされる感じでしょうか。
t.prototype.makeDependencies を this とし、 Dependents値にPromiseの状態に関する値を構築しています。Promiseに登録した個数も管理しています。
return
Object.defineProperty
Object.defineProperty(t.prototype, "canLoad", {
get: function() {
return
0 === this.dependencyCount &&
!this.noLoad &&
!this.isLoading &&
!this.hasFailed
},
enumerable: !1,
configurable: !0
})
DefineProperty は 3つの引数をとります。 1.操作するオブジェクト名 t.prototype、2.プロパティ値 canLoad 3.データ記述子 enumerable configurable やアクセサー記述子 get です。get には関数が定義されています。this.dependencyCount が 0 なら !this.noLoad 値の true を、それも flase なら、次を返す。といった関数です。
t.resolvePath
235の関数のcheckVersion関数から呼び出されます。
t.resolvePath = function(t, e) {
void 0 === e && (e = !0);
var r = {
name: t,
original: t,
addExtension: e
};
return l.Loader.pathFilters.execute(r), r.name
},
実行されるときの t は 'core' のような値。e は undefined のような値になっています。eが undefined のとき e = !0 で trueとなり r = {neme: 'core', original: 'core', addExtension: true} で、これを引数として l.Loader.pathFilters.executeを呼び出します。返却値は r.name なので 'core' です。
execute の内容は以下のようなものです。
e.prototype.execute = function() {
for(var t, e, r = [], n = 0; n < arguments.length; n++) r[n] = arguments[n];
try {
for(var o = i(this), l = o.next(); !l.done; l = o.next()) {
var c = l.value,
u = c.item.apply(c, a([], s(r), !1));
if(!1 === u) return !1
}
} catch(e) {
t = {
error: e
}
} finally {
try {
l && !l.done && (e = o.return) && e.call(o)
} finally {
if(t) throw t.error
}
}
return !0
}
関数名の次の引数リストで変数名が付与されていないので、argument から取り出します。r[n] に保持します。繰り返し処理の個別の値は以下のような値です。i(this) → {item: ƒ function(t) {return e.CONFIG.source.hasOwnProperty(t.name) && (t.name = e.CONFIG.source[t.name]), !0 }, priority: 0} この関数を c.item として、a([], s(r), !1) = [ name:'core', original: 'core', addExtension: true ] で t.name も [ name:'core', original: 'core', addExtension: true ]です。この値を e.CONFIG.sourceのメンバ値として保持させます。次の値は{name: '[mathjax]/startup.js', original: 'startup', addExtension: true}のような値になります。
]
戻った関数で r が以下のような値になりますので、このような構造を得るのが目的なのだろうと思います。
{name: 'file:///C:/MathJax/es5/startup.js', original: 'startup', addExtension: true}
ん?startup.jsって何を受け持つファイルなんだろうな。あーなるほど。本来は全てはstartup.jsを呼び出す方式を使うのが普通なんだな。自分らが使っているtex-mml-chtmlとかは、出力の形式とかを設定しないで呼び出す楽々セットなんだな。だから基本的には、楽々セットの起動URLを使ってもstartup.jsは呼び出そうとするんだろう。楽々セットの癖に5万行になっているのはかなり独立してるんだろうね。MathJaxのそもそものオプション機能の多さについても理解しておいた方がよいのかもしれない。単純に数式をHTMLにするだけの機能ではないからね。SVGにもできれば、数式番号のリセットやら追加の描画呼び出しといったこともできるし、描画処理がどこまで進んだからで呼び出せる関数もあるみたい。でも、自分は、全貌を見ずに先頭から見ていく。
t.loadAll
以下のような関数です。
t.loadAll = function() {
var t, e;
try {
for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) {
var o = n.value;
o.canLoad && o.load()
}
} catch(e) {
t = {
error: e
}
} finally {
try {
n && !n.done && (e = r.return) && e.call(r)
} finally {
if(t) throw t.error
}
}
}
この関数の繰り返し部で得られる個別の値はものすごく大きい配列です。
とはいいつつも、
name: "[tex]/action", name: "[tex]/ams", name: "[tex]/autoload", name: "[tex]/configmacros", name: "[tex]/newcommand", name: "[tex]/noundefined", name: "[tex]/require", name: "a11y/assistive-mml", name: "core", name: "input/mml", name: "input/tex", name: "input/tex-base", name: "loader", name: "output/chtml", name: "output/chtml/fonts/tex.js", name: "startup" name: "ui/menu",
のような値のそれぞれに isLoaded: true, isLoading: false, hasFailed: false, dependents: dependencies: provided: [[Circular],…, dependencyCount: 0, provided: [resolve: function () { [native code] }, reject: function () { [native code] },promise: { … といったような構造を持った値です。
a11y は accessibility の間を数字に置き換えた。a11yという表現です。こういう表現と言うのはいくつかあります。i18nとかね。こういうのをヌメロニムというそうで。以下のようなものが代表的です。
- a11y - Accessibility(利用しやすさ)
- c14n - Canonicalization(正準,正規化)
- d11n - Documentation(文書化)
- G11n - Globalisation / Globalization(世界化)
- i18n - Internationalisation / Internationalization(国際化)
- i14y - Interoperability(相互運用性)[7]
- K8s - Kubernetes
- L10n - Localisation / Localization(地域化)[5][6]
- m17n - Multilingualisation / Multilingualization(多言語化)[5]
- n11n - Normalisation / Normalization(正規化)[8]
- P13n - Personalisation / Personalization(個人化)
- P45 - Pneumonoultramicroscopicsilicovolcanoconiosis
- tr8n - Translation(翻訳)[9]
- v12n - Virtualisation / Virtualization(仮想化)
- D34n - Demand response based resource provision
それぞれの o.canLoad が true なら o.load() が実行されます。o.load() は 以下のような関数で、
t.prototype.load = function() {
if(!this.isLoaded && !this.isLoading && !this.noLoad) {
this.isLoading = !0;
var e = t.resolvePath(this.name);
l.CONFIG.require ? this.loadCustom(e) : this.loadScript(e)
}
isLoaded が false だったときに if の条件式が成立するようです。isLoad が false なら後ろの二つはもちろんくらいにflase になっているはずです。大体が this.isLoaded = true, this.isLoading = false, this.noLoad = false の状態で呼ばれますので、念のためのLoad チェック処理のように思えます。
どんな値がcanLoad = true なのかをみると core, input/tex-base, [tex]/newcommand, output/chtml, [tex]/action ★ では load の if文の中に入りますので、 isLoading が true になり、 resolvePath('[tex]/action')が実行されます。'file:///[DriveAlphabet:/MathJaxPath]/es5/input/tex/extensions/action.js'が得られます。l.CONFIG.require で必要とされているなら、loadCustom そうでないなら loadScript が実行されます。
loadScriptは以下のような関数で
t.prototype.loadScript = function(t) {
var e = this,
r = document.createElement("script");
r.src = t, r.charset = "UTF-8", r.onload = function(t) {
return e.checkLoad()
}, r.onerror = function(r) {
return e.failed("Can't load \"" + t + '"')
}, document.head.appendChild(r)
}
この関数でheadタグの中にscriptタグが付け足されます。'<script src="file:///[DriveAlphabet:/MathJaxPath]/es5/input/tex/extensions/action.js" charset="UTF-8"></script>'というものです。
t.prototype.makeDependencies
以下のような関数です。
t.prototype.makeDependencies = function() {
var e, r, n = [],
o = t.packages,
c = this.noLoad,
u = this.name,
p = [];
l.CONFIG.dependencies.hasOwnProperty(u) ?
p.push.apply(p, a([], s(l.CONFIG.dependencies[u]), !1))
:
"core" !== u && p.push("core");
try {
for(var h = i(p), f = h.next(); !f.done; f = h.next()) {
var d = f.value,
m = o.get(d) || new t(d, c);
this.dependencies.indexOf(m) < 0 &&
(m.addDependent(this, c),
this.dependencies.push(m), m.isLoaded ||
(this.dependencyCount++, n.push(m.promise)))
}
}
catch(t) { e = { error: t }}
finally {
try { f && !f.done && (r = h.return) && r.call(h) }
finally { if(e) throw e.error }
}
return n
}
t.packages は {0 : {key: 'loader', value: {name: t, isLoader = false, isLoading = false, hasFailed = false}} のような値です。this.noLoad = true, this.name = 'loader'で、l.CONFIG.dependencies.hasOwnProperty(u = 'loader')はflaseなのでloaderというメンバは定義されていません。"core" !== u && p.push("core"); が実行され、u=loaderのときは p に coreという配列値がプッシュされます。t.packages → o の core がメンバにあるか確認して undefined なので new t('core' , true) の処理結果が m に 'core' が格納される。'core' の処理順序制御についての構造が生成される。Dependents 配列に 'core' があるかを確認し、あれば 0 以上が返ってくるので、なければ -1 で論理演算の積の 0 が出てくるまで処理をすすめる仕組みを利用して次の処理をする。addDependent(this, true) 、 this.dependencies.push('core')、 m.isLoaded = false なので this.dependencyCount++ で増加させて、 n.push(m.promise) で n に Promise を 配列に追加。n は戻り値なので、この関数は Promise の配列追加処理が主な役割ということになりそうです。こういった処理を makeDependencies と言っているようです。
addDependent は 以下のようなものです。dependentsメンバ変数の配列に this オブジェクトを登録するような処理です。
t.prototype.addDependent = function(t, e) {
this.dependents.push(t), e || this.checkNoLoad()
}
配列への追加が完了すると this.checkNoLoad() 関数を実行します。
checkNoLoad は以下のようなものです。
t.prototype.checkNoLoad = function() {
var t, e;
if(this.noLoad) {
this.noLoad = !1;
try {
for(var r = i(this.dependencies), n = r.next(); !n.done; n = r.next()) {
n.value.checkNoLoad()
}
}
catch(e) { t = { error: e } }
finally {
try {
n && !n.done && (e = r.return) && e.call(r)
}
finally { if(t) throw t.error }
}
}
}
呼び出し元の処理順序制御構造オブジェクトの noLoad を 再帰的に false にしていく関数です。
t.prototype.makePromise
以下のような関数です。
t.prototype.makePromise = function(t) {
var e = this,
r = new Promise((function(t, r) {
e.resolve = t,
e.reject = r
})),
n = l.CONFIG[this.name] || {};
return
n.ready &&
(
r = r.then((function(t) {
return n.ready(e.name)
}))
),
t.length &&
(
t.push(r),
r = Promise.all(t).then((function(t) {
return t.join(", ")
}))
),
n.failed &&
r.catch(
(function(t) {
return n.failed(new c(t, e.name))
})
), r
}
同期処理が成功したら e.resolve = t 失敗したら e.reject = r を呼び出します。そして n に l.CONFIG[this.name] つまり、l.CONFIG['core'] = undefined を格納します。戻り値はカンマ構文の最後の値 r です。処理が失敗したときの r です。n.ready が true なら r は失敗を通知されたら l.CONFIG['core'].ready('core') のような関数を実行します。t が空で無ければ、その r を t の配列に取り込みます。処理順序制御した関数の全てが終わったら、それらの関数名を【,】区切りで結合していきます。n. ready が false や n.failed が true なら エラー処理としてn.failed(new c(t, 'core'))のような処理を実行します。
t.prototype.load
t.loadAllで説明されてる動きです。
t.prototype.loadCustom
今回の例では呼ばれない関数になっています。
t.prototype.loadScript
t.loadAllで説明されてる動きです。
t.prototype.loaded
以下のような関数です。
t.prototype.loaded = function() {
var t, e, r, n;
this.isLoaded = !0, this.isLoading = !1;
try {
for(var o = i(this.dependents), s = o.next(); !s.done; s = o.next()) {
s.value.requirementSatisfied()
}
}
catch(e) { t = {error: e } }
finally {
try { s && !s.done && (e = o.return) && e.call(o) }
finally { if(t) throw t.error } }
try {
for(var a = i(this.provided), l = a.next(); !l.done; l = a.next()) {
l.value.loaded()
}
}
catch(t) { r = { error: t } }
finally {
try { l && !l.done && (n = a.return) && n.call(a) }
finally { if(r) throw r.error }
}
this.resolve(this.name)
}
まず状態フラグの this.isLoaded = true, this.isLoading = false を設定します。最終的に this.resolve('loader') → this.resolve('startup') → this.resolve('core') のような関数を呼び出して処理を終えます。core のときだけthis.dependentsの値が 1: {… name:'loader' …} 2: {… name:'startup' …} という値を持っているためs.value.requirementSatisfied()が実行されます。
以下のような関数です。
t.prototype.requirementSatisfied = function() {
this.dependencyCount && (this.dependencyCount--, this.canLoad && this.load())
}
ふむ、あれかな、Loadedだから読み込み終わったってことで、Promise で実行するオブジェクトも一つ減るので、this.dependencyCount--で一つ減らしたり、this.canLoad && this.load() のようにLoadできるならLoadしようとする処理が入っているような。ともかく最初は、core やら loader とか startup のようなものを処理順序制御をしながら実行している感じが続いてるような気がするね。実に細かい。同じようなことを何回も繰り返してる。処理手順を短くする速さやメモリの消費を抑えようという感じがあまりしないけど、めんどくさいことをしっかりやって着実に準備、という感じだな。
t.prototype.failed
今回の例では呼ばれない関数になっています。
t.prototype.requirementSatisfied
t.prototype.loadedで説明されてる動きです。
t.prototype.addDependent
t.prototype.makePromiseで説明されてる動きです。
t.prototype.checkNoLoad
t.prototype.makePromiseで説明されてる動きです。
ページ
- MathJaxをデバッグしてJavaScriptの理解を深める
- 前:MathJaxをデバッグしてJavaScriptの理解を深める ページ2
- 次:MathJaxをデバッグしてJavaScriptの理解を深める ページ4
関連情報