arguments:JavaScript的一个怪老头

arguments是在每一个函数内都能够访问的、类似于数组的局部变量。它虽然很怪异,常常被忽略,但却是很多编码技巧(programming wizardry)的基础,所有主要的JavaScript库都对arguments的进行了深入的挖掘,每一个JavaScript程序员都应该熟练掌握它。

你可以在任何函数内访问到arguments,它以类似数组的形式保存了函数被调用时的参数,但它却不是一个真正的JavaScript数组,使用typeof arguments语句测试会返回:"object"。你可以通过数组下标的方式访问每一个参数值,它也像数组一样有一个length属性,但是它并不包含标准的数组方法,比如push、pop等。

创建灵活的函数

虽然它看上去用途有限,但arguments的确是一个很实用的对象。例如,你可以让函数接受不定量的参数。Dean Edwards的base2库中的format函数展示了这种灵活性。

function format(string) {
  var args = arguments;
  var pattern = new RegExp("%([1-" + arguments.length + "])", "g");
  return String(string).replace(pattern, function(match, index) {
    return args[index];
  });
};

你提供一个使用%1到%9充当占位符的模板字符串,然后再提供最多9个待替换的字符串参数,例如:

format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");

上面这行代码会返回字符串:“And the papers want to know whose shirt you wear”。

你可能注意到了一件事,在format函数的定义arguments:JavaScript的一个怪老头中我们只指定了一个参数:string。JavaScript允许我们传递任意数量的参数给一个函数,无论该函数如何定义,arguments对象都能够访问到它们。

转换成一个真正的数组

尽管arguments不是一个真正的JavaScript数组,但是我们可以使用Array的slice方法轻松地将它转换成一个真正的数组对象,就像这样:

var args = Array.prototype.slice.call(arguments);

 args变量现在是一个包含arguments全部数据的JavaScript数组对象。

创建包含预置参数的函数

我们可以使用arguments玩各种各样的技巧。下面是makeFunc函数的定义,makeFunc函数允许你提供一个函数的引用以及为这个引用函数准备的任意数量的参数,它会返回一个匿名函数,匿名函数会使用你预置的参数和匿名函数被调用时提供的参数来调用你指定的函数。

function makeFunc() {
  var args = Array.prototype.slice.call(arguments);
  var func = args.shift();
  return function() {
    return func.apply(null, args.concat(Array.prototype.slice.call(arguments)));
  };
}

提供给makeFunc的第一个参数会被认为是你希望调用函数的引用(当然,在这个简单的示例中我们没有添加校验),然后该函数引用会从arguments中被移除并保存到func中。然后makeFunc会返回一个匿名函数,匿名函数会使用Function对象的apply方法调用你指定的函数。

apply的第一个参数会指定被调用函数的作用域,也就是当函数被调用时,函数内部的this关键词所指向的对象。现在讲这个有点太高级了,所以我们给它赋null值就可以了。第二个参数是一组会被传递给func函数的arguments对象的值,makeFunc将预置的数组和匿名函数被调用时传进来的参数数组拼接在一起作为参数传递给被调用的函数func。

假设你需要输出一个模板固定的消息字符串。为了将你从每次调用format函数时都要引用模板字符串的重复工作中解救出来,你可以使用makeFunc这个函数工具来返回一个能够帮你自动填入模板参数并调用format函数的函数:

var majorTom = makeFunc(format, "This is Major Tom to ground control. I'm %1.");

你可以重复使用majorTom函数:

majorTom("stepping through the door");
majorTom("floating in a most peculiar way");

每次你调用majorTom的时候,它都会去使用你提供的第一个参数和已填入的模板来调用format函数。上面的代码返回:

"This is Major Tom to ground control. I'm stepping through the door."
"This is Major Tom to ground control. I'm floating in a most peculiar way."

创建自引用的函数

你可能认为这很酷,别急,arguments还有一个惊喜等着你,它还有另外一个有用的属性:callee。arguments.callee包含了创建arguments对象的函数的引用。我们怎么使用这么个玩意儿呢?arguments.callee是匿名函数引用自身的一个简便方法。

repeat是一个函数,它包含一个函数引用和两个数字。第一个数字代表函数执行的次数,第二个数字代表每次执行前的延迟时间,单位是毫秒,repeat函数的定义如下:

function repeat(fn, times, delay) {
  return function() {
    if(times-- > 0) {
      fn.apply(null, arguments);
      var args = Array.prototype.slice.call(arguments);
      var self = arguments.callee;
      setTimeout(function(){self.apply(null,args)}, delay);
    }
  };
}

repeat使用arguments.callee来获取匿名函数的引用并将它保存在self变量中,这样匿名函数就能够通过setTimeout在一段时间的延迟后调用自己。

我的项目里有这么一个简单的函数,它接受一个字符串作为参数,然后弹出一个包含该字符串的警告框:

function comms(s) {
  alert(s);
}

但是,我要创建一个它的特别版函数,让它能够重复执行三次,间隔时间是两秒,我可以用我的repeat函数这样做:

var somethingWrong = repeat(comms, 3, 2000);
somethingWrong("Can you hear me, major tom?");

调用somethingWrong函数的结果是重复弹出三次警告框,间隔时间是两秒。

arguments用的不多,有点怪怪滴,但却充满了惊喜,值得我们去了解和学习。

原文地址:arguments: A JavaScript Oddity

相关推荐