小さい頃はエラ呼吸

いつのまにやら肺で呼吸をしています。


jQueryは他のライブラリとの競合をどうやって解決しているのか

はじめに

jQueryは他のライブラリと一緒に読み込まれたときに、$関数などが競合することを想定して設計されています。今日はそのあたりを少し調べてみました。

$関数の衝突

prototype.jsをはじめとするライブラリでは、ショートカット関数として$関数が定義されています。これらのライブラリを読み込んでからjQueryを読み込むと、$関数が上書きされるため、意図しない動作となる可能性があります。

このため、jQueryではすでに存在していた$関数を内部プロパティに退避するということをしています。具体的には、jquery-1.4.2.jsの28行目で_$という内部プロパティに$関数をコピーしています。また同様に、25行目では、_jQueryという内部プロパティにjQueryというグローバル変数をコピーしています。

// Define a local copy of jQuery
var jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context );
  },

  // Map over jQuery in case of overwrite
  _jQuery = window.jQuery,

  // Map over the $ in case of overwrite
  _$ = window.$,
$関数が上書きされる場合

$関数が衝突した場合のサンプルプログラムが以下になります。はじめにグローバル変数$を初期化しておき、その状態でjQueryを読み込みます。

<html>
<head>
<script type="text/javascript">
  // jQueryと$を初期化 --- (a)
  var jQuery = "jQuery_org";
  var $ = "$_org";
</script> 
<script type="text/javascript" src="./jquery-1.4.2.js" ></script> 
<script type="text/javascript">
  var ShowMessage = function() {
    alert($); // --- (b)
    alert(jQuery); // --- (c)
  }
</script>
</head>
<body>
<form>
  <input type="button" value="show" onclick="ShowMessage();">
</form>
</body>
</html>

上記のHTMLをブラウザで表示して、ボタンをクリックすると、以下の内容がalertされます。

function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
}

(b)と(c)のところでalertされるのは、$関数とjQuery関数そのものです。jQueryを後から読み込んでいるため、(a)で初期化した値は、上書きされます。

上書きされた$関数を使う

たとえば、prototype.jsとjQueryを併用している環境で、$関数はprototype.jsのほうを使いたいといった場合は、jQuery.noConflict()を使用します。

<html>
<head>
<script type="text/javascript">
  // jQueryと$を初期化 --- (a)
  var jQuery = "jQuery_org";
  var $ = "$_org";
</script> 
<script type="text/javascript" src="./jquery-1.4.2.js" ></script> 
<script type="text/javascript">
  var ShowMessage = function() {
    alert($);      // --- (b)
    jQuery.noConflict();
    alert($); // --- (c)
  }
</script>
</head>
<body>
<form>
  <input type="button" value="show" onclick="ShowMessage();">
</form>
</body>
</html>

今度は(b)と(c)で値が変わりました。(c)では最初に初期化したときの値が出力されます。jQuery.noConflict()では内部プロパティに退避していた_$関数を元のwindow.$関数に戻すことを行っています。
jquery-1.4.2.jsの361行目付近

jQuery.extend({
  noConflict: function( deep ) {
    window.$ = _$;

    if ( deep ) {
      window.jQuery = _jQuery;
    }

    return jQuery;
  },
jQuery.noConflict()の注意点

jQuery.noConflict()を呼び出した以降の処理では、$関数ではなく、jQuery関数を使用する必要があります。

$関数はprototype.jsなどをはじめ、多くのライブラリがそれぞれ拡張している関数である。
jQueryでも、核となるjQueryオブジェクトのショートカットして極めて頻繁に利用される。
このコマンドは、そのような$関数を定義する複数のライブラリを用いた際に衝突することを防ぐものである。
noConflictを使った場合、jQueryオブジェクトの呼び出しには明確に'jQuery'と書く必要がある。
例えば$(“div p”)と書いていたものも、jQuery(“div p”)と書かなければならない。
jQuery.noConflict() - jQuery 日本語リファレンス はてなブックマーク - jQuery.noConflict() - jQuery 日本語リファレンス

おわりに

上記のように、jQueryでは$関数の名前が衝突した場合でも、単純に上書きするだけでなく内部的に保持してくれているため、jQuery.noConflict()を使えば、元の$関数を呼び出すことが可能です。

はじめてjQueryのソースを読んでみましたが、結構難しいです。$関数の退避は分かったのですが、jQueryオブジェクトも退避している意味がよくわかりませんでした。異なるバージョンのjQueryを同時に読み込んだ場合を想定しているのかもしれません。