jQuery.extend マニアックス
jQuery の extend メソッドは便利なんだが複雑で忘れてしまいがちなのでメモしておく。
jQuery.extend
の呼び出しパターンは次の4通り。
$.extend([deep,] target, obj1, [obj2, [obj3, ...]])
$.extend([deep,] obj)
$.fn.extend([deep,] obj)
$(...).extend([deep,] obj)
全てのパターンで第一引数に [deep,]
がある。これはオプションの引数で true
を指定するとディープコピーしてくれる。
以下では分かりやすくするために deep
オプションは省略した一覧を掲載する。
$.extend(target, obj1, [obj2, ...]])
$.extend(obj)
$.fn.extend(obj)
$(...).extend(obj)
だいぶシンプルになった。各パターンを見ていく。
$.extend(target, obj1, [obj2, ...]])
まずは1つ目のパターン。
$.extend(target, obj1, [obj2, ...]])
target
に obj1
を上書きコピーし、書き換えられた target
を返す。
以下は簡単な使用例。
>>> a = {a: 3, b: 4} Object { a=3, b=4} >>> $.extend(a, {c: 99}) Object { a=3, b=4, c=99} >>> a Object { a=3, b=4, c=99}
obj1
は複数個指定できる。複数個が必要あるのかと思うかもしれないが、継承もどきを実現するときに楽に書ける。
// 継承元クラス function B() { } B.prototype = { a: function() { }. b: function() { } } // 派生クラス function D() { } D.prototype = $.extend({}, B.prototype, { c: function() { }. d: function() { } });
もし、ここで
D.prototype = $.extend(B.prototype, { //.... );
とやってしまうと、B.prototype
が書き換えられてしまう。
そこで、一旦、空オブジェクトに B.prototype
をコピーしておいて、D
に実装したいメソッドを上書きコピーしてあげている。
ちなみに
D.prototype = $.extend({}, B.prototype, { ... });
は、次のように書いてもよい書くと同じように動くように見えるが
D.prototype = $.extend({ ... }, B.prototype);
こうすると、B と D に同じメソッド名を作成しようとしたときに、B のものが優先されてしまうので注意が必要だ。
target を省略した extend
次は、target を省略した extend の呼び出しパターン3つを見る。
$.extend(obj)
$.fn.extend(obj)
$(...).extend(obj)
target を省略して obj が1つしかない場合には、extend
を呼び出したときの this が target として解釈される。
$.extend(obj)
$.extend(obj)
は jQuery 自身を obj
で拡張する。
例えば、
$.extend({ min: function(a, b) { return a < b ? a : b; } });
とすると、$.min(3, 5);
で小さいほうの値を取得できる(引用元:jQuery.extend(object) - jQuery 日本語リファレンス)。
jQuery クラスに static メソッドを追加するようなイメージだ。
$.fn.extend(obj)
$.fn
は jQuery.prototype
のこと。つまり、$.fn.extend(obj)
を利用すると jQuery オブジェクトにメソッドを追加できる。
たとえば、
$.fn.extend({ log: function() { console.log(this); return this; } });
とすることで、log
メソッドを追加できる。
例えばこんな風にして使う。
$("div") .log() .each(function(){ // ... })
メソッドチェーンの途中に突っ込んでログ出力できて便利!
$.fn.extend
は各種 jQuery プラグインの実装で頻繁に利用されている。
$(...).extend(obj)
実際に利用されてるのは見たことないし、公式ドキュメントにも書いていないんだけど、これが意外に便利なんじゃないかというのが今回の本題。本題までが長かった…。
$(...).extend(obj)
を使うと、extend
されるのは jQuery オブジェクト自身。jQuery オブジェクトにメソッドを追加できる。
例えば「フォームの要素を enable/disable する」という処理がソースのあちらこちらに存在しているする。
$("<form>").submit(function(){ // form 配下のコントロールを disable にする $(this).find(":enabled").attr("disabled", "disabled"); var self = this; $.post('url', { ... }, function(){ // POST が完了すると、コントロールを enable にする $(self).find(":disabled").attr("disabled", ""); // ... }, "json"); return false; });
この例ではまだ一箇所だが、find(":enabled").attr("disabled", "disabled")
のようなコードが色んな場所にあると、ソースが煩雑になる。
そこで、次のようにしてみよう。
var enable_mixin = { enableAll: function(){ this.find(":enabled").attr("disabled", ""); return this; }, disableAll: function(){ this.find(":enabled").attr("disabled", "disabled"); return this; } }; $("<form>").submit(function(){ // form 配下のコントロールを disable にする $(this).extend(enable_mixin).disableAll(); var self = this; $.post('url', { ... }, function(){ // POST が完了すると、コントロールを enable にする $(self).extend(enable_mixin).enableAll(); // ... }, "json"); return false; });
ちょっとしたメソッド集 enable_mixin
を作成しておく。そして、メソッドチェーンの中で、extend
を使って動的にメソッドを追加し、そのメソッドをすぐさま呼び出している。
もちろん、$.fn.extend
を使えば全ての jQuery オブジェクトにメソッドを追加できるのだが、全体で利用するほどの汎用性はないようなちょっとした処理を、その場で気楽にミックスインして使えるのがうれしい。
おわり
jQuery.extend
は複雑である。