Javascript筆記:(実践編)jQueryプラグイン技術から-extedメソッドのソースコードを分析する(extedメソッドにbugがあることを発見する)(下編)
25106 ワード
1.1 分析$extedソース
ソースコードを分析する前に、sテストコードを追加します.コードは以下の通りです.
前に書いたテスト例には大きな対象が含まれていませんが、コピーの重要なシーンは対象が含まれている特殊なシーンですので、ここでこのようなシーンを補足します.印刷した結果から、deep属性が設定されていない場合、comment属性の値はtargtobjとsrcobjが合体した結果、commentの戻り値はsrbjのcommentの値であり、同時にextedメソッドの戻り値はtargetの値と同じであることが分かりました.deep属性の値を設定しましたが、deep値がfalseであれば、extedの戻り値とdeep属性を設定しない場合の値は同じです.しかし、targtobjの値は変わらないです.deep属性がfalseに設定されている場合、targtobjの値は変わりません.srbbjの余分な属性はターゲトbjに合併されますか?この問題のためにテストコードをもう一つ書きました.
へへへ、exted方法の使い方は今よりはっきりしましたよね.テスト結果を見て、発見します.
jQueryのextedメソッドにはbugがあります.パラメータdeepの値を設定しないと最終的にはdeepはtrueに代表される深度コピーではありません.しかし、deep属性をfalseに設定した場合とは結果が異なります.ターゲットのtargetは一つ変更されました.これはextedメソッドのバグと言えるでしょう.
次はextedメソッドのソースコードを読んで、どのような原因が発生したのかを確認してください.
ソースの最初の部分はextedメソッドの内部設定のためにいくつかのローカル変数を使用する必要があります.コードは以下の通りです.
このコードは問題があります.バグがあります.このコードも私達が最初のパラメータdeep値とdeep値を設定しないでfalseの最後の結果と一致しないところです.実はコード作者の原意はdeepが設定されていない時の結果とdeepがfalseに設定されているのと同じです.ただし、パラメータがfalseなら、typeof判定タイプはbollanではなく、Objectです.
みんなは次のテストコードを見ればその道理が分かります.
次のコードは深さコピーのコードです.このコードは前にもう書きました.ここでも多く説明しません.コードは以下の通りです.
最後に言いたいのですが、jQueryプラグインの技術についての深い理解はjQueryのソースコードの一つの重要な鍵を理解するのかもしれません.jQueryプラグインの技術紹介を書き終わったら、jQueryのシリーズを模写し続けます.jQueryのソースをよく分析してください.
ソースコードを分析する前に、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のソースをよく分析してください.