Effective JavaScript Item 23は永遠にargmentsオブジェクトを修正しないでください.


このシリーズはEffective JavaScriptの読書ノートとして使われています.
 
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オブジェクトのコピーを取得し、コピーを修正する.