Effective JavaScript Item 23は永遠にargmentsオブジェクトを修正しないでください.
2354 ワード
このシリーズはEffective JavaScriptの読書ノートとして使われています.
argmentsオブジェクトは同じ配列のオブジェクトにすぎませんが、shift、pushなどの配列オブジェクトから提供される方法はありません.したがって、例えば、argments.shift()、argments.push()はエラーです.
Item 20とItem 21では、関数オブジェクトにcallとapplyの方法があることが分かりましたが、それらを利用してargmentsも配列を利用することができますか?
argmentsオブジェクトは、関数パラメータのコピーではありません.関数宣言のパラメータとargments保存のオブジェクトには参照関係があります.例えば、上記のcalMethod関数の例では、2つのパラメータobjとmethodが宣言されている:
objが引用しているのは、argments[0]です.
methodが引用しているのはアーグメンント[1]です.
2回のshift.callを呼び出した後、argmentsは元のものになります.
[obj,「add」,17,25]は[17,25]になりました.
だから、objの引用はobj自体から17になり、methodの引用は「add」から25になった.明らかに17[25]で得られた結果はundefinedであり、JavaScriptの演算規則により、17はまずNumberオブジェクトに変換されますが、このオブジェクトの上には25の属性がありません.
上記の例で表現したいのは、関数で宣言されたパラメータとargmentsとの間の連絡が脆弱であり、各宣言のパラメータは実際にはargmentsオブジェクト中の対応する位置に対する参照だけである.
注目すべきは、ES 5のstrict modeにおいて、関数宣言のパラメータはargmentsを参照しないことである.
もしargmentsオブジェクトを修正する必要があるなら、まずargmentsオブジェクトを割り当てられます.
argmentsオブジェクトを永遠に変更しないでください.
使用[].slice.cal(argments)を使用してargmentsオブジェクトのコピーを取得し、コピーを修正する.
argmentsオブジェクトは同じ配列のオブジェクトにすぎませんが、shift、pushなどの配列オブジェクトから提供される方法はありません.したがって、例えば、argments.shift()、argments.push()はエラーです.
Item 20とItem 21では、関数オブジェクトにcallとapplyの方法があることが分かりましたが、それらを利用してargmentsも配列を利用することができますか?
function callMethod(obj, method) {
var shift = [].shift;
shift.call(arguments);
shift.call(arguments);
return obj[method].apply(obj, arguments);
}
しかし、上記の方法は以下の応用シーンに問題があります.var obj = {
add: function(x, y) { return x + y; }
};
callMethod(obj, "add", 17, 25);
// error: cannot read property "apply" of undefined
エラーが発生した原因は:argmentsオブジェクトは、関数パラメータのコピーではありません.関数宣言のパラメータとargments保存のオブジェクトには参照関係があります.例えば、上記のcalMethod関数の例では、2つのパラメータobjとmethodが宣言されている:
objが引用しているのは、argments[0]です.
methodが引用しているのはアーグメンント[1]です.
2回のshift.callを呼び出した後、argmentsは元のものになります.
[obj,「add」,17,25]は[17,25]になりました.
だから、objの引用はobj自体から17になり、methodの引用は「add」から25になった.明らかに17[25]で得られた結果はundefinedであり、JavaScriptの演算規則により、17はまずNumberオブジェクトに変換されますが、このオブジェクトの上には25の属性がありません.
上記の例で表現したいのは、関数で宣言されたパラメータとargmentsとの間の連絡が脆弱であり、各宣言のパラメータは実際にはargmentsオブジェクト中の対応する位置に対する参照だけである.
注目すべきは、ES 5のstrict modeにおいて、関数宣言のパラメータはargmentsを参照しないことである.
function strict(x) {
"use strict";
arguments[0] = "modified";
return x === arguments[0];
}
function nonstrict(x) {
arguments[0] = "modified";
return x === arguments[0];
}
strict("unmodified"); // false
nonstrict("unmodified"); // true
strictと非strictモードでは関数宣言のパラメータとargmentsの関係が一致しないからこそ、問題が起こらないように、argmentsオブジェクトを修正しないのが一番安全です.もしargmentsオブジェクトを修正する必要があるなら、まずargmentsオブジェクトを割り当てられます.
var args = [].slice.call(arguments);
sliceメソッドがパラメータを受け入れない場合、コピー操作を行います.得られたargsも本物の配列オブジェクトです.同時に,argsと関数宣言のパラメータの間には何の関係もなく,動作は安全である.このような方法を使って上記のcall Method関数を再実現します.function callMethod(obj, method) {
var args = [].slice.call(arguments, 2);
return obj[method].apply(obj, args);
}
var obj = {
add: function(x, y) { return x + y; }
};
callMethod(obj, "add", 17, 25); // 42
まとめ:argmentsオブジェクトを永遠に変更しないでください.
使用[].slice.cal(argments)を使用してargmentsオブジェクトのコピーを取得し、コピーを修正する.