如何编写高效的js代码(4)

永远不要修改函数的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  
作者:shaynegui
喜欢打德州,玩dota,听电音,web前端脑残粉
我的专栏 GitHub