jQuery.extend()メソッドとjQuery.fn.extend()メソッドソース分析

18644 ワード

この2つの方法は同じコードを使用しており、1つはjQueryオブジェクトまたは通常のオブジェクトに属性をマージする方法であり、1つはjQueryオブジェクトのインスタンスであり、基本的な使い方についてはいくつかの例を挙げる.
htmlコードは次のとおりです.
<!doctype html>
<html>
   <head>
      <title></title>
        <script src='jquery-1.7.1.js'></script>
   </head>
   <body>
  
    <img src=''/>

   </body>
</html>

次はjsの使い方を書きます.
2つの一般オブジェクトを結合
      //           
      var obj1={name:'Tom',age:22};
      var obj2={name:'Jack',height:180};
      console.log($.extend(obj1,obj2));  //Object {name: "Jack", age: 22, height: 180}

jQueryオブジェクトに属性またはメソッドを追加
$.extend({hehe:function(){alert('hehe');}});

$.hehe();
//alert('hehe')

この用法は重要であり、jQuery内部にインスタンス属性と方法を追加することであり、プロトタイプ属性と方法の実現方法もjQueryプラグインを作成する方法であり、以下はjQuery 1である.7.1でextendメソッドを使用して独自のメソッドと属性を拡張
jQuery.extend({
    noConflict: function( deep ) {
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

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

        return jQuery;
    },

    // Is the DOM ready to be used? Set to true once it occurs.
    isReady: false,

    // A counter to track how many items to wait for before
    // the ready event fires. See #6781
    readyWait: 1,
.....

この例ではオブジェクトパラメータが1つしか入力されていません.デフォルトでは、これをマージするオブジェクトとして使用します.
jQueryオブジェクトインスタンスに属性またはメソッドを追加
 //  jQuery      
      console.log($('img').extend({'title':'img'}));//[img, img#img.img, prevObject: jQuery.fn.jQuery.init[1], context: document, selector: "img", title: "img", constructor: function…]

マージのみマージ対象オブジェクトを変更しない
      var obj1={name:'Tom',age:22};
      var obj2={name:'Jack',height:180};
      console.log($.extend(obj1,obj2));   //Object {name: "Jack", age: 22, height: 180}
      console.log(obj1);                  //Object {name: "Jack", age: 22, height: 180}

デフォルトでは、マージ対象オブジェクトは、戻り結果と同様に変更されます.マージ後のオブジェクトを1つだけ取得し、元のオブジェクトを破壊したくない場合は、このメソッドを使用します.
  var obj1={name:'Tom',age:22};
  var obj2={name:'Jack',height:180};
  var empty={};
  console.log($.extend(empty,obj1,obj2));   //Object {name: "Jack", age: 22, height: 180}
  console.log(obj1);                  //Object {name: "Tom", age: 22}

使用すると再帰的にマージまたは深度コピーと呼ばれます
 var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}};
 var obj2={name:'Jack',love:{drink:'water',sport:'football'}};
  
 console.log(($.extend(false,obj1,obj2)).love);   //Object {drink: "water", sport: "football"}
 console.log(($.extend(true,obj1,obj2)).love);    //Object {drink: "water", eat: "bread", sport: "football"}

詳しい使い方は、参考マニュアルhttp://www.w3cschool.cc/manual/jquery/を参照してください
次に、1.7.1ソースコードでどのように実現されているかを分析します.
 
jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;
       ...
}

まず、パラメータの個数が不確定であるためargumentsオブジェクトアクセス伝達のパラメータを直接呼び出す変数のセットを定義します.
変数options:ソースオブジェクトを指します.‰ ‰ 変数name:ソースオブジェクトの属性名を表します.‰ ‰ 変数src:ターゲットオブジェクトの属性の元の値を表します.‰ ‰ 変数copy:ソースオブジェクトの属性の値を表します.‰ ‰ 変数copyIsArray:変数copyが配列であるかどうかを示します.‰ ‰ 変数clone:深度コピー時の元の値の修正値を表します.‰ ‰ 変数target:ターゲットオブジェクトを指します.‰ ‰ 変数i:ソースオブジェクトの先頭下付きを表します.‰ ‰ 変数length:変数targetを修正するためのパラメータの個数を表します.‰ ‰ 変数deep:深度コピーを実行するかどうかを示し、デフォルトはfalseです.
コード実装をよりよく理解するために、ここで上述した例を例示としてソースコードの実行状況を観察する.
      var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}};
      var obj2={name:'Jack',love:{drink:'water',sport:'football'}};
      $.extend(true,obj1,obj2)

 
ソース分析
// Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;
        target = arguments[1] || {};
        // skip the boolean and the target
        i = 2;
    }

深度コピーかどうかを判断し、1番目のパラメータがブール値であれば1番目のパラメータの値をdeepに与え、2番目のパラメータをターゲットオブジェクトとし、2番目のパラメータが存在しない場合は空のオブジェクトに値を付け、ソースオブジェクトの下付きを2に変更します.この例では、1番目のパラメータがtureでdeepがtrueになったため、ここを歩きます.targetは2番目のパラメータであるobj 1に修正され、ソースオブジェクトの先頭に2を付けると、3番目からソースオブジェクトである本例のobj 2となる
// Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

ここでtargetについてさらに処理すると、非オブジェクトおよび関数のデータ型に対してカスタム属性を追加するのは無効です.例えば、文字列の自己呼び出し可能なメソッドや属性などです.
// extend jQuery itself if only one argument is passed
    if ( length === i ) {
        target = this;
        --i;
    }

length属性がiの値に等しい場合は、ターゲットオブジェクトが存在しないことを示し、通常はlengthがiより大きい値であるべきである場合、このときthisをターゲットオブジェクトとしてi値を減算してlength値がiより大きい(iより1大きい)ことを実現する
これがjQueryが自分に属性を拡張する方法の実現原理であり,ターゲットオブジェクトに伝わらなければよい.
2つの可能な状況:$.extend(obj)または$.extend(false/true,obj);
    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

この部分がこの方法の核心であり,arguementsオブジェクトのi番目の下付き値からループ操作を開始し,まずソースオブジェクトがnullまたはundefinedであることをフィルタリングする場合,実際には
ソース・オブジェクトが本当に対像であるとは限らないし、文字列などの他のタイプの値で書くこともできます.
console.log($.extend({'name':'tom'},'aa'));   //Object {0: "a", 1: "a", name: "tom"}

変な感じがしますか?どうやって実現したのか.続いて見る
フィルタリング後にforループsrcを開始して保存するのは、ターゲットオブジェクトのキーの値、copyプロパティが保存するソースオブジェクトのキーの値です.この2つのキーは同じです
// Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

ソース・オブジェクトの属性値がターゲット・オブジェクトである場合、デッドループによってプログラムがクラッシュする可能性があります.ここでは、このループをスキップするように制限します.たとえば、次のようにします.
var o = {};
o.n1 = o;
$.extend( true, o, { n2: o } );
//
// Uncaught RangeError: Maximum call stack size exceeded

しかし、このようにすると、正常な状況に濡れ衣を着せられます.例えば、
 var obj1={a:'a'}
 var obj2={a:obj1};
 console.log($.extend(obj1,obj2)); //Object {a: "a"}

この場合も、ソースオブジェクト値がターゲットオブジェクトに等しいことを満たすが、obj 1のaの属性値が変更されていないことが判明した.continueが実行されたためであり、以下、ソースコードのこの文を実行に注釈する
Object {a: Object}

この时、正常に修正された个人の感じはこの地方が改善する必要があります;
次にif判断は,深さレプリケーションを行うか否かを区別する深さレプリケーションを見ない先見一般的な
target[ name ] = copy;

単純にcopyに値がある限りターゲットオブジェクトに直接コピーし、ターゲットオブジェクトがある場合は修正がない場合は増加し、マージが実現します.
forループの後、新しいターゲットオブジェクトが返されるので、ターゲットオブジェクトは最後に修正され、結果は返される結果と同じです.
// Return the modified object
    return target;
};

次に、深度コピーをどのように処理するかについて説明します.
まずdeepがtrueであることを保証し、copyに値があり、オブジェクトまたは配列であることを保証します(オブジェクトと配列の深さのコピーでなければ説明できません).次に、グループとオブジェクトを分割して処理し、配列の状況を見てみましょう.
if ( copyIsArray ) {
         copyIsArray = false;
         clone = src && jQuery.isArray(src) ? src : [];

} else {
        clone = src && jQuery.isPlainObject(src) ? src : {};
}

配列copyIsArrayの値を真にして中に入った値をfalseに変更すると、現在のループのソースオブジェクト属性に対してターゲットオブジェクトがあるかもしれないしないかもしれないし、配列かどうかを判断すると元の配列が変わらないのではないかと判断すると配列になります.ソースオブジェクトの現在のプロパティが配列である以上、最後のターゲット要素も配列である必要があります.配列ではなく、オブジェクトがターゲットオブジェクトの現在のプロパティをオブジェクトに変更します. 
    // Never move original objects, clone them
     target[ name ] = jQuery.extend( deep, clone, copy );

次に、ソースオブジェクトの現在の属性値(配列またはオブジェクト)と改造されたターゲットオブジェクトの現在の属性を再帰的に結合し、最後に返された新しい配列またはオブジェクトをターゲットオブジェクトに付与し、最終的に深さコピーを実現します.
しかし、この中にはもう一つの奇妙な現象があります.例えば、このような操作があります.
      console.log($.extend({a:1},'aa')); //Object {0: "a", 1: "a", a: 1}

元のソースオブジェクトが本当にオブジェクトeであるとは限らないし、文字列を分解してターゲットオブジェクトとマージできるなんて、for...inループは操作文字列です
      var str='aa';
      for(var name in str){  
         console.log(name);
         console.log(str[name])
      }

これも可能です.文字列を分解して数字の下付きで読みますが、ソースコードでは
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) )

配列やオブジェクトの制限がありますが、深さのコピーでは効果がありませんか?
ソースコードのcopyの値が匿名関数になったので、深さのコピーをテストしてもいいです.
 alert(jQuery.isPlainObject(copy)); //true

なぜ関数なのか筆者はまだはっきりしていないので、後で解決しましょう.