完全理解JS--arguments

什么是arguments?

它是JS的一个内置对象,常被人们所忽略,但实际上确很重要,JS不像JAVA是显示传递参数,JS传的是形参,可以传也可以不传,若方法里没有写参数却传入了参数,该如何拿到参数呢,答案就是arguments了,在一些插件里通常这样使用。

每一个函数都有一个arguments对象,它包括了函数所要调的参数,通常我们把它当作数组使用,用它的length得到参数数量,但它却不是数组,使用instanceof查看下,若使用push添加数据将报错,代码如下:

(function(){
    console.log([] instanceof Array)
    console.log(arguments instanceof Array)
    if(arguments.push) arguments.push('test')
})()

创建一个灵活的格式化函数

上面说了arguments可以使用函数使用数量不定的参数,下面看看它的一个实际应用:

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];
  });   
};
format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");

这里我借用了别人的一个例子,一个模板字符串,可以使用%1到%9等9个占位符,然后提供9个参数给这些占位符,最后替换生成真正的字符串。
上面的代码返回:“And the papers want to know whose shirt you wear”

把arguments转换成一个真正的数组

有时我们希望将它转换成真正的数组使用,可以使用下面的代码:

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

现在args就是一个标准的js数组了,可以使用数组的标准方法了。

通过arguments对象封装format函数

arguments允许我们去执行所有类型的js方法,下面通过一个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)));
  };
}

第一个arguments给makeFunc提供了你调用的函数的引用,它将第一个参数从arguments数组里移除,然后makeFunc返回了一个匿名函数去运行规定的方法。
apply的第一个参数是函数调用的范围,主要是函数内部关联部分所指向的,这里设为null,它的arguments是一个数组,即匿名函数调用时传入的参数,匿名函数将传入的参数串联到原参数对象args里组成完整的匿名函数所需要参数。

你需要输出一个模板,总是相同位置的字符发生改变,这样就可以使用makeFunc去生成一个模板函数,传入不同的参数多次调用生成不同的内容了。

var func = makeFunc(format, "I like %1 not %2.");
func("js", "java");   
func("java", "python");

每一次调用func,它会同时调用format函数和第一个arguments,然后填充已有的模板。
执行结果如下:

"I like js not java."
"I like java not python."

这样封装format是不是很酷,不过arguments还有更多惊喜。

ES6重构makeFunc

es6中不推荐使用arguments,那就要使用es6中提供的语法...rest
Rest就是为解决传入的参数数量不一定, rest parameter(Rest 参数) 本身就是数组,数组的相关的方法都可以用。
下面是重构后的代码:

function format(string, ...args) {
    var pattern = new RegExp("%([1-" + args.length + "])", "g");
    return String(string).replace(pattern, function(match, index) {
      return args[index-1];
    });   
};
format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");

function makeFunc(...args) {
    var func = args.shift();
    return function(...rest) {
      return func.apply(null, args.concat(rest));
    };
}

var func = makeFunc(format, "I like %1 not %2.");
func("js", "java");
func("java", "python");

创建引用自身的函数

arguments.callee包括了一个函数的引用去创建一个arguments对象,它能让一个匿名函数很方便的指向本身。
下面的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获取一个引用,指向运行原始指令的函数。这样,匿名函数就可以再次调用自身,看看下面的调用:

var somethingWrong = repeat(function(s){console.log(s)}, 3, 2000);
somethingWrong("Can you hear me, major tom?");

ES6重构repeat

现在已经不推荐使用arguments.callee();
原因:访问 arguments 是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建。影响现代浏览器的性能,还会影响闭包。
其实很简单,给内部函数一个名字即可:

function repeat(fn, times, delay) {
    return function _fn(...args) {
      if(times-- > 0) {
        fn.apply(null, args);
        // 现在arguments.callee被弃用了, 给内部函数一个名字即可:
        setTimeout(function(){_fn.apply(null, args)}, delay);
      }
    };
  }

var somethingWrong = repeat((s)=>{console.log(s)}, 3, 2000);
somethingWrong("Can you hear me, major tom?");

可以看到somethingWrong函数的结果被打印了3次,每隔2秒。
arguments还有很多惊喜,非常值得我们去了解,欢迎加入前端交流群。
学习交流,请加群:867541922

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容