基礎となるインターフェイスに互換性を持たせるにはさまざまな問題がありますが、これはクライアントがサポートするインターフェイスを決定するために if を使用することに他なりません。最も有名な例はイベントです:
var addEvent = function( e, what, how) {
if (e.addEventListener) e.addEventListener(what, how, false)
else if (e.attachEvent) e.attachEvent('on' what, how )
}
ここでは、イベントを要素にバインドするときに発生する可能性のある 2 つの状況、つまり標準の W3C DOM インターフェイスと DHTML によって提供されるインターフェイスについて検討します。もちろん、この例はまだ未熟ですが、問題を説明するには十分です。
本来の方法は、互換性層で現場判定を呼び出し、対応するif分岐に入る方法です。明らかに、この「その場で判断する」方法は効率的ではありません。その後、人々はこの方法を採用しました:
if ( MSIE) {
addEvent = function(e, what, how) {
e.attachEvent('on' what, how)
}
} else {
addEvent = function(e , what , how) {
e.addEventListener(what, how);
}
}
1 回の判断後に異なるコードを addEvent にバインドすることで、実行する必要がなくなります。時間分岐判定。
残念ながら、この問題は簡単ではありません。まず第一に、「attachEvent を使用する」と「クライアントは MSIE である」をバインドするのは非常に時代遅れの考えです。いつか Microsoft の良心がそれを知ったらどうなるでしょうか?これは現在起こっています。IE9 は明らかに DOM インターフェイスをサポートしており、DOM3 でさえそれをサポートしています。その結果、この「良心の発見」の動きによって多くのフロントエンド ライブラリが壊れ、コードの変更を余儀なくされることになります (IE8 が登場したときと同じように)。さらに、このアプローチでは「不明なクライアント」が考慮されていません。私の知る限り、Google が Chrome をリリースした後、多くのクラス ライブラリのコードが書き直されました。
特徴検出を行うにはどうすればよいですか?機能検出により、「新しいクライアント」によって引き起こされるトラブルを最小限に抑えることができます。クラス ライブラリの初期化時に定義されたコードのセットを通じてクライアントの機能を検出し、この検出値のセットを使用してクラス ライブラリ コードをバインドします。
var supportAddEventListener = !!(checkerElement.addEventListener);
if (supportsAddEventListener) {
addEvent = function(e, what, how) {
e.addEventListener(what, how);
}
} else if (supportsAttachEvent) {
addEvent = function(e, what, how) {
e.attachEvent('on' what, how);
}
}
特徴検出は実際には分離です "特定のクライアントを使用する」および「特定の機能をサポートする」 - if ブランチに「機能が存在するかどうか」(インターフェイスが一貫しているかどうか) を直接判断させることで、クライアントの「良心の発見」によって引き起こされる「善意」を排除します。取引先のメーカーが悪いことをする。」実際、これは歴史的な傾向とも一致しています。標準インターフェイスが徐々に普及し、クライアントの「表現が一貫している」ようになったら、一貫した互換性レイヤー インターフェイスを作成しないのはなぜでしょうか。
ドロップ もう一度コードを見てみましょう。通常、互換性のために機能検出を使用するコードは次のようになります:
if (new_interface_detected) {
comp = function() {uses_new_interface};
} else if (old_interface_detected) {
comp = function() {uses_old_interface}; } else {
throw new Error('Unadaptable!')
}
つまり、プロセスは次のとおりです。
クライアントが新しいインターフェイスをサポートしている場合は、互換性レイヤーを新しいインターフェイスにバインドします
それ以外の場合、クライアントが古いインターフェイス/一貫性のないインターフェイスをサポートしている場合は、バインドします互換性レイヤーを新しいインターフェイスにバインドします。 古いインターフェイスにバインドします。
それ以外の場合は、可能であれば、エラー フィードバックを返します。
つまり、クライアントが「高度な」機能 (新しいもの) をサポートしている場合、互換性レイヤー プログラムは高高度から「落ちます」。インターフェイス、標準インターフェイス)、それを「キャッチ」するだけです - 互換性レイヤーにホームがあります。そうでない場合は、落ち続けます - 古いインターフェイスがそれをキャッチした場合、誰もそれをキャッチしない場合は、古いインターフェイスを使用します。彼は地面に倒れ込み、息を引き取りながらこう叫びました。「あなたが使っているクライアントはニッチすぎる、私には何もできません!」
これは何に似ていますか?実際、JavaScript オブジェクト システムのメカニズムを理解していれば、次のように類推できます。「これは単なるプロトタイプではないでしょうか?」プロトタイプ システムはこのドロップを利用します。特定のメンバーを探し、それがこのオブジェクトに定義されている場合はそれを返し、そうでない場合はプロトタイプ チェーンに沿って上方向に検索し (はい、今回は上方向です)、以下同様です。プロトタイプ チェーンが実際に最後に到達すると、unknown が返されます。
とにかくやってみよう!ここでも例として addEvent を使用します。まず、何も含まれていない空のドライバーを定義します。
var nullDriver = {} 次に、オブジェクトを作成し、プロトタイプ チェーンがそれを指すようにします。 ECMA V5 時代では、Object.create を使用できますが、古いクライアントがまだたくさんあるため (それ以外は互換性があります)、独自の関数を作成します:
varderive = Object.create ? Object.create: function() {
var T = function() {} ;
return function(obj) {
T.prototype = obj;
return new T
}
}()
この使い方は奇妙ですが、問題なく動作し、遅くはありません - Object.create の約半分の速さです。この派生を使用して開始します:
var dhtmlDriver = detect(nullDriver);
var dhtmlDriverBugfix = 派生(dhtmlDriver); ここでのバグ修正は、いくつかの「バグ」および特殊な状況のために定義された特別なドライバーです。ここでは無視して構いません。さて、DHTML の addEvent とは何ですか?
if (supportsAttachEvent) {
dhtmlDriver.addEvent = function(e, what, how) {
e.attachEvent('on' what, how)
}
}
それでは?プロトタイプ チェーンの先頭にあるドライバーは W3C 標準ドライバーであるはずです。それを書き留めてください。
コードをコピーします コードは次のとおりです。 🎜>var w3cDriverBugfix =derive(w3cDriver);
if (supportsAddEventListener) {
w3cDriver.addEvent = function(e, what, how) {
e.addEventListener(what, how)
}
}
最後に、最終的な呼び出しインターフェイスを作成するために何かを配置します。 (w3cDriverBugfix が醜すぎるため...)
コードをコピー
その後呼び出されます。これにより、フォールバックの本質を失うことなく、これらの怖そうな分岐判断がシンプルかつ効果的になります。addEventListener をサポートするクライアントで addEvent を呼び出すことは、w3cDriver.addEvent を呼び出すことと同等であり、これをサポートするクライアントでは最後に落ちます。次に、たとえば dhtmlDriver.addEvent を呼び出します。さらに、バグ修正を簡単に実行できます。元のレイヤーにはまったく影響を与えずに、専用の「バグ修正」レイヤーをフックできます。
待って、これほど多くのレイヤーを継承すると遅くなりますか?確かに、これほど奥深いプロトタイプチェーンは間違いなく遅くなりますが、私には方法があります。オブジェクトのプロパティに書き込むと何が起こるかを覚えていますか?
コードをコピー
コードは次のとおりです。 var ego = function(x) {return x} for (ドライバー内のそれぞれの変数) {
if (! (nullDriver 内のそれぞれ)) {
ドライバー[それぞれ] = ego(ドライバー[それぞれ])
}
}
はい、元々プロトタイプチェーンで上位にあったメソッドは、突然最下位に落ちてしまいます。今回は、プロトタイプ チェーンを検索する必要はなく、プロパティを最後から直接取得するだけです。ここで ego 関数を使用する理由は、一部のブラウザーがここでコードを「最適化」できないようにするためです。
まとめ ここでは互換性について話しますが、その本質は言語機能にあります。プロトタイプの継承を使用すると、この面倒な操作を非常にエレガントに完了できます。はい、フレームの美しさは外側だけでなく、内側も、たとえ最も煩わしいものであっても、同様にエレガントでなければなりません。
ここでのテクノロジーはdessにあります。