第21条讲述使用可变参数的函数average。该函数可处理任意数量的参数并返回这些参数的平均值。

如何创建可变参数的函数

1、实现固定元数的函数

书上的版本

function averageOfArray(a){
for(var i=0,sum=0,n=a.length;i<n;i++){
sum+=a[i];
}
return sum/n
}

使用ES5 Array.reduce方法的版本

function averageOfArrayES5(a){
if({}.toString.call(a) !== '[Object Array]')a=[].slice.call(a);
var n=a.length;
var sum=a.reduce(function(prev,cur){return prev+cur;});
return sum/n;
}

调用方法

averageOfArray([2,7,1,8,3,4,5]);

averageOfArray函数定义了一个形参,即参数列表中的变量a。当调用函数averageOfArray时,只需提供单个参数,即数字数组。

2、利用arguments实现可变元数函数

利用js的一个事实,即给每个函数都隐式地提供了一个名为arguments的局部变量。arguments对象是一个类数组。它为每个实参提供了一个索引属性,还包含一个length属性用来指示参数的个数。从而可以通过遍历arguments对象的每个元素来实现可变元数的函数

function average(){
for(var i=0,sum=0,n=arguments.length;i<n;i++){
sum+=arguments[i];
}
return sum/n;
}

同样使用ES5 Array.reduce方法的版本,这里需要对arguments类数组对象转化为真正的对象。我们使用Array.prototype.slice()方法结合之前讲的函数的call方法来处理。

代码如下:

var obj={
"0":100,
"1":"sdfsafd",
length:2,
each:function(){ }
}
Array.prototype.slice.call(obj);//[100, "sdfsafd"]

然后把参数对象转化一下

function average(){
var a=[].slice.call(arguments);
var sum=a.reduce(function(prev,cur){return prev+cur;});
return sum/n;
}

注:其中[].slice.call()方法和Array.prototype.slice.call()方法,效果相同。类似的Object.prototype.toString.call()和({}).toString.call()方法。想了解更详细,可以自行查找原型链的相关知识。

可变参数函数提供了灵活的接口。不同的调用者可使用不同数量的参数来调用它们。但它们自身也失去了一点便利。
想使用计算数组参数来调用可变参数的函数,只能使用apply()方法。如果提供了一个便利的可变参数的函数,也最好提供一个需要显示指定数组的固定元数的版本。

3、提供一个轻量级的封装

function average(){
return averageOfArray(arguments);
}

这里编写了一个轻量级的封装,并委托给固定元数的版本来实现可变参数的函数。

实现函数的重载

可以根据传入参数的情况,实现适当的功能。

function doAdd(){
if(arguments.length==1){
return arguments[0]+10;
}else if(arguments.length==2){
return arguments[0]+arguments[1];
}
}
doAdd(10);//20
doAdd(30,20);//50

arguments对象和命名参数一起使用。

function doAdd(num1,num2){
if(arguments.length==1){
return num1+10;
}else if(arguments.length==2){
return num1+num2;
}
}
doAdd(10);//20
doAdd(30,20);//50

下面的代码对于使用jquery的同学应该不会很陌生。

$('body').css('font-size');
$('body').css('font-size',30);

用得就是类似的特性。

对于这方面再进行扩展一下。比如根据传入参数的个数及参数的类型,来完成对应不同的功能。jquery里的很多API,都是用相关的方法来实现的,有兴趣的可以看一下jquery的源码。

提示

- 使用隐式的arguments对象实现可变参数的函数
- 考虑对可变参数的函数提供一个额外的固定元数的版本,从而使用者无需借助apply方法。

附录:arguments对象

arguments对象是一个类数组的对象,包含着传入函数中的所有参数。
下面我们示例看一下,这个arguments对象到底包含哪些东西。

function a(){
console.log(arguments);
}
a(1,2,3,6,7);//

得到如下的图示

arguments对象属性:

索引值 "0","1"代表对应参数。

function a(){
console.log(arguments[3]);
}
a(1,2,3,6,7);//6

length代表参数长度。

function a(){
console.log(arguments.length);
}
a(1,2,3,6,7);//5

callee是一个指针指向拥有这个arguments对象的函数。

function a(){
console.log(arguments.callee);
}
a(1,2,3,6,7);//function a(){...}

caller是一个在ES3并没有定义的属性。

这个属性中保存着调用当前函数的函数的引用,如果是全局作用域中调用当前函数,它的值为null。

function a(){
console.log(arguments.caller);
}
a(1,2,3,6,7);//null

在严格模式下,arguments.callee和arguments.caller都会报错。
ES5中arguments.caller的值始终是undefined。
arguments的值永远与对应命名参数的值保持同步。代码示例:

function b(name,age){
arguments[1]=20;
return name+'是'+age+'岁!';
}
b('li lie',30);//"li lie是20岁!"
b('han mei mei');//"han mei mei是undefined岁!"

arguments对象的值会自动反应到对应的命名参数上。所以每次都对argument[1]的修改,也会修改age的值,结果它们都是20。但这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但它们的值会同步。
当只传入一个参数时,arguments[1]的值不会反应到命名参数中,因为arguments对象的长度是由传入的参数决定的,不是由定义函数时的命名参数的个数决定的。此时arguments[1]虽然有值,但命名参数是不会同步值的。
没有传递值的命名参数将自动被赋undefined值。和定义变量但没初始化一样。
注意:在严格模式下对arguments对象的赋值会变得无效。就算像上面那样arguments[1]=20,age的值也不会改变。另外重写arguments对象的值会导致错误(代码不会执行)。
ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。

var a=[1,2,3,4,5];
function b(c){c=[]};
b(a);
a;//[1, 2, 3, 4, 5]

[Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数的更多相关文章

  1. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  3. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  4. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  5. [Effective JavaScript 笔记]第24条:使用变量保存arguments对象

    迭代器(iterator)是一个可以顺序存取数据集合的对象.其一个典型的API是next方法.该方法获得序列中的下一个值. 迭代器示例 题目:希望编写一个便利的函数,它可以接收任意数量的参数,并为这些 ...

  6. [Effective JavaScript 笔记]第21条:使用apply方法通过不同数量的参数调用函数

    apply()方法定义 函数的apply()方法和call方法作用相同,区别在于接收的参数的方式不同.apply()方法接收两个参数,一个是对象,一个是参数数组. apply()作用 1.用于延长函数 ...

  7. JavaScript Arguments 实现可变参数的函数,以及函数的递归调用

    //可变参数的函数 注:也可以使用对象作为参数来实现 function Max() { var temp = arguments[0] || 0; for (var i = 1; i < arg ...

  8. [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法

    前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...

  9. [Effective JavaScript 笔记]第54条:将undefined看做“没有值”

    undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...

随机推荐

  1. 高版本jquery尤其是1.10.2的版本设置input radio设置值的最正确的姿势。

    $("input:radio[name="analyshowtype"]").attr("checked",false); $(" ...

  2. fis-receiver:一行命令将项目部署到远程服务器

    前言 本项目基于FIS2,没了.其实fis项目本身就提供了php版本的范例,这里翻译成node版本. 项目地址:https://github.com/chyingp/fis-receiver 服务端接 ...

  3. 记录我开始学习 Git的路程

    工作半年多了,总觉得没学到什么东西,于是乎找了个Git学习一下,感觉还蛮厉害的样子.为此记录下我的路程 2015,11,26 更新 前面的路都挺艰难的,在官网下载msysgit网速几乎为0(心情千万只 ...

  4. jQuery ajax - get(),getJSON(),post()方法

    1)       jQuery ajax - get() 方法: $(selector).get(url,data,success(response,status,xhr),dataType) 参数 ...

  5. 最新微信小程序(应用号)视频教程,实战教程

    1.1课程介绍,定个小目标            http://v.youku.com/v_show/id_XMTc2NzIwNDk1Ng==.html 1.2开发文档简读,了解全貌       ht ...

  6. Git.Framework 框架随手记--存储过程简化

    在很多的ORM中对存储过程操作都是一个棘手的地方,因为存储过程是一段预编译的代码,其中可以包含很多处理过程.在Git.Framework中也同样存在这样的问题,目前没有能力解决这个问题.但是对于存储过 ...

  7. css清楚浮动的几种常用方法

    请先看博客:http://www.jb51.net/css/173023.html

  8. Java设计模式-访问者模式(Visitor)

    访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化.访问者模式适用于数据结构相对稳定算法又易变化的系统.因为访问者模式使得算法操作增加变得容易.若系统数据结构对象易于变化,经 ...

  9. ThinkPHP多表联合查询的常用方法

    1.原生查询示例: $Model = new Model(); $sql = 'select a.id,a.title,b.content from think_test1 as a, think_t ...

  10. MyEclipse------在特定目录创建文件和书写内容

    readwrite.jsp <%@ page language="java" import="java.util.*" pageEncoding=&quo ...