奇异的 arguments(翻译)November 19, 2008

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 标记,然后其余的参数就会依次替换这些地方。例如:

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 函数定义中,我们仅定义了个名为 string 的参数。而 Javascript 不管函数自身定义的参数数量,它都允许我们向一个函数传递任意数量的参数,并将这些参数值保存到被调用函数的 arguments 对象中。

转换成实际数组

虽然 arguments 对象并不是真正意义上的 Javascript 数组,但是我们可以使用数组的 slice 方法将其转换成数组,类似下面的代码

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

这样,数组变量 args 包含了所有 arguments 对象包含的值。

创建预置参数的函数

使用 arguments 对象能够简短我们编写的 Javascript 代码量。下面有个名为 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 的第一个参数指定需要调用的函数名称(是的,在这个简单的例子中没有错误检查),获取以后从 args 中删除。makeFunc 返回一个匿名函数,它使用函数对象的(Function Object)apply 方法调用指定的函数。

apply 方法的第一个参数指定了作用域,基本上的作用域是被调用的函数。不过这样在这个例子中看起来会有点复杂,所以我们将其设定成 null ;其第二个参数是个数组,它指定了其调用函数的参数。makeFunc 转换其自身的 arguments 并连接匿名函数的 arguments,然后传递到被调用的函数。

有种情况就是总是要有个输出的模板是相同的,为了节省每次是使用上面提到的 format 函数并指定重复的参数,我们可以使用 makeFunc 这个工具。它将返回一个匿名函数,并自动生成已经指定模板后的内容:

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 函数时,它都会使用第一个指定的参数填写已经指定的模板。例如上述的代码返回:

"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.callee 是个非常有用的调用自身的匿名函数。

下面有个名为 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?");

结果就犹如预期的那样,弹出了三次警告框每次延时两秒。

最后,arguments 即便不会经常被用到,甚至显得有些诡异,但是它上述的那些惊艳的功能(不仅仅是这些!)值得你去了解它。

§ 14 条评论

  1. 顶明城兄弟

  2. As中的arguments更为强大!RIA阵营向明城同学敞开双手~~~

  3. 看不懂....

  4. airwin airwin

    感谢博主的翻译
    这里就是不太懂times的作用域,在匿名函数中没这个参数也可以使

  5. @airwin 可以尝试了解下“闭包”等概念

  6. vampire vampire

    哈 巧 上周末在GR订阅的sitepoint里看到这篇文章了 感觉写的相当不错 于是翻译了 发在几百年不上的blueidea 结果无人回应 你看http://bbs.blueidea.com/thread-2896886-1-6.html 呵呵

  7. vampire vampire

    文章的第一个例子我有些疑问 match和index不是正则表达式(即pattern)的属性吗?这里为何作为参数传递?

  8. @vampire 其实 RegExp 的第一个参数是字符串,参见 https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp

  9. vampire vampire

    可能是我没把问题表述清楚,我是想说,第一个例子中的match和index好像凭空冒出来的一样的,match和index是replace方法(或其第二个参数,此例中为一个函数)可直接调用的?以前从没见过

  10. 手气不错 手气不错

    摘记小马的回复,感谢:

    很有收获。arguments 一直不太重视,这次有了一个比较全局的了解。

    从翻译的角度,有一些建议:

    1. ...它的特性需要每个 Javascript 程序员去熟悉它...

    英语喜欢用名词做主语,而中文习惯“主谓宾”的结构,所以我们可以调整为: “每个JavaScript程序员都应该熟悉它的特性”。

    2. ...它都允许我们向一个函数传递任意数量的参数...

    这样的句式都可以翻译成 “我们可以向它(或JavaScript)的(a不用翻译)函数传递任何数量的参数”。这会更符合中文习惯。

    3. ...编写下面的提供个字符串并弹出个警告框的简单函数...

    编写..简单函数,中间隔得太长,读者看着就会有些吃力。不妨可以改成: “编写下面这样一个简单的函数,它接受一个字符串,用警告对话框弹出显示”

  11. 纠错:即使是数组,直接typeof也是object。呵呵

  12. 上篇日记里也提到了,这周主要是到工厂和实验室参观,然后再学校就是做了学校里面的一个多点的小项目(小的快称不上项目了),于是称空余时间读了些技术文章,感觉看的文章多了也不...

  13. 你好,想请教你一个问题,不过这个问题跟此文没有任何关系。

    如下:
    <div id="abc" onclick="myfunction();"></div>
    这个onclick在除了IE之外的所有浏览器上都能正常执行,可是在IE上时点击这个div时,好像它根本没发现我在点击。

    怎么解决呢?
    希望大哥指点指点。。。

  14. [...]翻译http://www.gracecode.com/archives/2551/[...]

添加评论




* Required (but your email address will never be published)

Yahoo 统计