概述
本系列作为Effective JavaScript的读书笔记。
arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等。因此调用诸如:arguments.shift(),arguments.push()是错误的。
在Item 20和Item 21中,知道了函数对象上存在call和apply方法,那么是不是可以利用它们来让arguments也能够利用数组的方法呢:
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
发生错误的原因是:
arguments对象并不是函数参数的一份拷贝。函数声明的参数和arguments保存的对象存在着引用关系。比如在上面callMethod函数的例子中,声明了两个参数obj和method:
obj引用的就是arguments[0]
method引用的就是arguments[1]
而在调用了两次shift.call(arguments)之后,arguments由原来的:
[obj, "add", 17, 25]变成了[17, 25]
所以obj的引用从obj本身变成了17,method的引用从"add"变成了25。很显然17[25]得到的结果是undefined,因为根据JavaScript的运算规则,17首先会被转换为Number对象,而这个对象之上并没有25这个属性。
上述例子想表达的就是,函数中声明的参数和arguments之间的联系很脆弱,每个声明的参数实际上只是对arguments对象中对应位置的一个引用。
值得注意的是,在ES5的strict mode中,函数声明的参数并不会引用arguments:
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模式下,函数声明的参数和arguments的关系不一致,所以为了避免出现问题,不去修改arguments对象才是最安全的做法。
如果确实需要修改arguments对象,那么可以首先赋值一份arguments对象:
var args = [].slice.call(arguments);
当slice方法不接受任何参数的时候,就会执行复制操作,得到的args也是一个真正的数组对象。同时,args和函数声明的参数之间也没有任何联系了,对它进行操作是安全的。使用这种方式重新实现上面提到过的callMethod函数:
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
总结:
- 永远不要修改arguments对象
- 使用[].slice.call(arguments)得到arguments对象的一份拷贝,然后对拷贝进行修改
最后
以上就是慈祥牛排为你收集整理的Effective JavaScript Item 23 永远不要修改arguments对象的全部内容,希望文章能够帮你解决Effective JavaScript Item 23 永远不要修改arguments对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复