永远不要修改函数的arguments对象
函数的arguments对象看起来像个数组,但其实他并不是数组的一个实例,因此他没有数组的方法,如arguments.shift(),但是我们可以借助函数对象的call方法来实现,假如有以下例子
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) { returnx + y; }
};
callMethod(obj,"add",17,25);// error: cannot read property "apply" of undefined
所有命名的参数其实是根据他们的顺序绑定在arguments的索引上的,即arguments[0]是obj,arguments[1]是method,即使我们修改了arguments对象,这种引用关系也不会改变,所以上面的代码中我们以为我们调用的是obj["add"],其实我们调用的是17[25],因此17被当作了一个number对象,而他并没有key为25的value值,所以为undefined,这也告诉我们不要去修改arguments对象
而对于ES5来说,严格模式没有绑定arguments的这种引用关系
function strict(x) {
"use strict";
arguments[0] = "modified";
returnx === arguments[0];
}
function nonstrict(x) {
arguments[0] = "modified";
return x === arguments[0];
}
strict("unmodified"); // false
nonstrict("unmodified");// true
如果真的需要对arguments对象的某些值进行操作,应先把它copy到一个真正的数组中
var args = [].slice.call(arguments);
如果修复最上面的例子
function callMethod(obj, method) {
var args = [].slice.call(arguments,2);
return obj[method].apply(obj, args);
}
var obj = {
add: function(x, y) { returnx + y; }
};
callMethod(obj,"add",17,25);// 42
如果必要,记得使用一个变量来保存arguments的引用
假设我们要实现一个遍历器,如下
var it = values(1,4,1,4,2,1,3,5,6);
it.next();// 1
it.next();// 4
it.next();// 1
我们可能会这么写
function values() {
var i = 0, n = arguments.length;
return{
hasNext: function() {
returni < n;
},
next: function() {
if (i >= n) {
throw newError("end of iteration");
}
return arguments[i++]; // wrong arguments
}
};
}
错误的原因是每个函数都会绑定一个arguments对象,因此next函数中的arguments并不是values函数中的arguments,解决的办法如下
functionvalues() {
var i = 0, n = arguments.length, a = arguments;
return{
hasNext: function() {
returni < n;
},
next: function() {
if(i >= n) {
throw newError("end of iteration");
}
returna[i++];
}
};
}
varit = values(1,4,1,4,2,1,3,5,6);
it.next();// 1
it.next();// 4
it.next();// 1