Javascript筆記:(実践編)jQueryプラグイン技術から-extedメソッドのソースコードを分析する(extedメソッドにbugがあることを発見する)(下編)

25106 ワード

1.1     分析$extedソース
ソースコードを分析する前に、sテストコードを追加します.コードは以下の通りです.
<script type="text/javascript">

$(document).ready(function(){

    console.log('==================  06 start');

    var targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},

        srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};

    var resobj = $.xjcopy(targetobj,srcobj);

    console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}

    console.log(targetobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}

    targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},

    srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};

    resobj = $.xjcopy(false,targetobj,srcobj);

    console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}

    console.log(targetobj);//Object { id="NO1111", name="xiajun", age=23, sex="man",comment=Object { test01="t01", test02="t02", test03="t03"}}

    targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},

    srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};

    resobj = $.xjcopy(true,targetobj,srcobj);

    console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test03="t03",test04="t04"}}

    console.log(targetobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test03="t03",test04="t04"}}

    console.log('==================  06 end');

});

</script>
 
前に書いたテスト例には大きな対象が含まれていませんが、コピーの重要なシーンは対象が含まれている特殊なシーンですので、ここでこのようなシーンを補足します.印刷した結果から、deep属性が設定されていない場合、comment属性の値はtargtobjとsrcobjが合体した結果、commentの戻り値はsrbjのcommentの値であり、同時にextedメソッドの戻り値はtargetの値と同じであることが分かりました.deep属性の値を設定しましたが、deep値がfalseであれば、extedの戻り値とdeep属性を設定しない場合の値は同じです.しかし、targtobjの値は変わらないです.deep属性がfalseに設定されている場合、targtobjの値は変わりません.srbbjの余分な属性はターゲトbjに合併されますか?この問題のためにテストコードをもう一つ書きました.
    targetobj = {'id':'NO1111','name':'xiajun','age':23,'comment':{'test01':'t01','test02':'t02','test03':'t03'}},

    srcobj = {'id':'NO1122','name':'sharp','sex':'man','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};

    resobj = $.xjcopy(false,targetobj,srcobj);

    console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}

    console.log(targetobj);//Object { id="NO1111", name="xiajun", age=23, comment=Object { test01="t01", test02="t02", test03="t03"}}
私達はtargtobjの値が確かに変わっていないことを発見しました.私たちはdeepの値をtrueに設定すると、結果からtargtobjとexted方法の戻り値が合併された結果であることが分かります.
へへへ、exted方法の使い方は今よりはっきりしましたよね.テスト結果を見て、発見します.
jQueryのextedメソッドにはbugがあります.パラメータdeepの値を設定しないと最終的にはdeepはtrueに代表される深度コピーではありません.しかし、deep属性をfalseに設定した場合とは結果が異なります.ターゲットのtargetは一つ変更されました.これはextedメソッドのバグと言えるでしょう.
  
次はextedメソッドのソースコードを読んで、どのような原因が発生したのかを確認してください.
ソースの最初の部分はextedメソッドの内部設定のためにいくつかのローカル変数を使用する必要があります.コードは以下の通りです.
        // options                  ,name         

        // src             ,copy                   

        //    src = targetobj['id'],copy = targetobj['id']

        // copyIsArray     ,              

        // clone           (                )

        // target      extend   target

        // i extend  objectN arguments     

        // length      ,deep       ,         deep false

        var options, name, src, copy, copyIsArray, clone,

            target = arguments[0] || {},

            i = 1,

            length = arguments.length,

            deep = false;
説明はコメントに書いてありますが、ここは疲れていません.次のコードを読みます.
        //

        if ( typeof target === "boolean" ) {

            deep = target;//  deep  ,     

            target = arguments[1] || {};//    deep  ,  target     

            //                 ,  i  2,  arguments objectN       2    

            i = 2;

        }
注釈がはっきりしています.ここもうるさくないです.
このコードは問題があります.バグがあります.このコードも私達が最初のパラメータdeep値とdeep値を設定しないでfalseの最後の結果と一致しないところです.実はコード作者の原意はdeepが設定されていない時の結果とdeepがfalseに設定されているのと同じです.ただし、パラメータがfalseなら、typeof判定タイプはbollanではなく、Objectです.
みんなは次のテストコードを見ればその道理が分かります.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>     </title>

</head>



<body>

</body>

</html>

<script type="text/javascript">

window.onload = function(){

    //alert(typeof false);//boolean

    //alert(typeof true);//boolean

    typeftn(false,{},{});

    typeftn(true,{},{});

}



function typeftn(deep,target,src){

    var bobj = arguments[0] || {};

    //console.log(typeof bobj);//   false,   object,   true    boolean

    alert(typeof bobj);

}

</script>
まずこのバグを棚上げして、私達はextedのソースコードを読みます.
    //               ,      ,   target = {}     

        // extend      target,    target            ,           

        //     extend   ,      object  ,      objectN     ,extend        

        //   objectN object                  

        if ( typeof target !== "object" && !$.isFunction(target) ) {

            target = {};

        }
下のコードはなぜextedでプラグインを作成できるのですか?コードは以下の通りです.
        //          ,target    this,        ,            

        if ( length === i ) {

            target = this;

            --i;

        }
私たちは一つのパラメータだけを伝えて、このパラメータはobjectタイプです.ターゲットはthisに設定します.だから、jQuery.exted方式でextedメソッドを呼び出すと、thisはjQueryオブジェクトを指します.コードの後ろにーiを書きます.このパラメータはタージパラメータではなく、ojjjQueryパラメータになります.コピー対象になります.最終パラメータの内容はjQueryオブジェクト内部にコピーされ、最終的にはjQueryのグローバルオブジェクトの一つの属性となります.
次のコードは深さコピーのコードです.このコードは前にもう書きました.ここでも多く説明しません.コードは以下の通りです.
    //        javascript         ,                    

        for ( ; i < length; i++ ) {

            //        null/undefined   

            if ( (options = arguments[i]) != null ) {

                for ( name in options ) {

                    src = target[ name ];

                    copy = options[ name ];

    

                    //

                    if ( target === copy ) {

                        continue;

                    }

    

                    //                         

                    if ( deep && copy && ( $.isPlainObject(copy) || (copyIsArray = $.isArray(copy)) ) ) {

                        if ( copyIsArray ) {

                            copyIsArray = false;

                            clone = src && $.isArray(src) ? src : [];

    

                        } else {

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

                        }

    

                        //

                        target[ name ] = $.xjcopy( deep, clone, copy );

    

                    } else if ( copy !== undefined ) {

                        target[ name ] = copy;

                    }

                }

            }

        }

        //     

        return target;
次は私が書いたexted方法と同じxj copy方法を書き換えて、exted方法のbugを修正します.コードは以下の通りです.
;(function($){

    $.xjcopy = $.fn.xjcopy = function(){

        // options                  ,name         

        // src             ,copy                   

        //    src = targetobj['id'],copy = targetobj['id']

        // copyIsArray     ,              

        // clone           (                )

        // target      extend   target

        // i extend  objectN arguments     

        // length      ,deep       ,         deep false

        var options, name, src, copy, copyIsArray, clone,

            target = arguments[0] || {},

            i = 1,

            length = arguments.length,

            deep = false;

    

        //

        if (length >2){

            if (deep === false || deep === true){

                deep = target;//  deep  ,     

                target = arguments[1] || {};//    deep  ,  target     

                //                 ,  i  2,  arguments objectN       2    

                i = 2;                

            }

        }

        

        /*if ( typeof target === "boolean") {

            deep = target;//  deep  ,     

            target = arguments[1] || {};//    deep  ,  target     

            //                 ,  i  2,  arguments objectN       2    

            i = 2;

        }*/

    

        //               ,      ,   target = {}     

        // extend      target,    target            ,           

        //     extend   ,      object  ,      objectN     ,extend        

        //   objectN object                  

        if ( typeof target !== "object" && !$.isFunction(target) ) {

            target = {};

        }

    

        //

        //          ,target    this,        ,            

        if ( length === i ) {

            target = this;

            --i;

        }    

        

        //        javascript         ,                    

        for ( ; i < length; i++ ) {

            //        null/undefined   

            if ( (options = arguments[i]) != null ) {

                for ( name in options ) {

                    src = target[ name ];

                    copy = options[ name ];

    

                    //

                    if ( target === copy ) {

                        continue;

                    }

    

                    //                         

                    if ( deep && copy && ( $.isPlainObject(copy) || (copyIsArray = $.isArray(copy)) ) ) {

                        if ( copyIsArray ) {

                            copyIsArray = false;

                            clone = src && $.isArray(src) ? src : [];

    

                        } else {

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

                        }

    

                        //

                        target[ name ] = $.xjcopy( deep, clone, copy );

    

                    } else if ( copy !== undefined ) {

                        target[ name ] = copy;

                    }

                }

            }

        }

        //     

        return target;    

    };

})(jQuery)
extedの内容についてはここで説明します.ここで説明したいのですが、jQuery内のexted方法はjQueryプラグインの技術を代表していません.extedはjQueryプラグインの技術を実現する手段の一つとしか言えません.jQueryのプラグインの技術にはまだ多くの内容があります.その中にはextedを使わずにプラグインを実現する方法も含まれています.プラグインの詳細については今後のブログで書きます.
最後に言いたいのですが、jQueryプラグインの技術についての深い理解はjQueryのソースコードの一つの重要な鍵を理解するのかもしれません.jQueryプラグインの技術紹介を書き終わったら、jQueryのシリーズを模写し続けます.jQueryのソースをよく分析してください.