博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript服务器端开发(函数实参对象arguments使用的几个注意事项)
阅读量:6745 次
发布时间:2019-06-25

本文共 3667 字,大约阅读时间需要 12 分钟。

   

为什么引入实参对象arguments

 

    在JS开发中,每一个函数都对应一个实参对象,称为arguments。这个对象引用的目的是为了解决如下问题:

 

  当调用函数的时候传入的实参个数超过函数定义时的形参个数时,没有办法直接获得未命名值的引用。

 

  因为JS函数定义与调用极其灵活,参数个数是不确定的,而且系统也不会作自动检测。这为开发带来灵活性的同时也带来相当的麻烦。下文将结合实际开发中使用到arguments时经常遇到的几个“麻烦”进行讨论,并给出对应的解决方案。

 

 

在函数体内,标识符arguments是指向实参对象的引用,实参对象是一个类数组对象,这样可以通过数字下标就能访问传入函数的实参值,而不用非要通过名字来得到实参。

根据上面的分析,不难得出这样的结论:可以让一个函数轻松地操作任意数目的实参。例如下面的例子,返回任意个数的一组数据中的最大值。

 

function maxValue(/*...*/){    var max=Number.NEGATIVE_INFINITY;    for(var i= 0,n=arguments.length;i< n;i++){        if(arguments[i]>max) max=arguments[i];    }    return max;}var largestData=maxValue(3,342,3,45454,999,-2929,999);console.log("larget is: "+largestData);

 

 

专家建议:arguments[]对象最适合的应用场景是在这样一类函数中,这类函数包含固定个数的命名和必需参数,以及随后个数不定的可选实参。

 

嵌套函数中使用arguments的问题

作为一个类数组对象,arguments包含了所有函数调用时的实参信息。但是,如果在嵌套函数中也需要外层函数的arguments信息时容易出现问题。请参考下面的开码片段。

var it=List("JavaScript","Java","C#","C++","Objective-C","C","Swift","Python","Visual Basic");it.next();it.next(); function List(){    var start= 0,n=arguments.length;     return{      hasNext:function(){          return start
=n){              throw new Error("End of iteration!");          }          console.log( arguments[start++]); //wrong usage      }    };}

undefined

undefined

细究起来,问题就出在迭代器next方法中最后一句,原意图是在此调用外层函数的实参对象arguments,但正是在此出现错误这时的arguments是本层函数对应的实参对象(元素个数为0),根本就不同于外层函数的实参对象arguments,所以才有上面的输出结果。理解了这一点,上面的代码便可以轻松地修改为如下形式:

var it=List("JavaScript","Java","C#","C++","Objective-C","C","Swift","Python","Visual Basic");it.next();it.next(); function List(){    var        start= 0,        n=arguments.length,        outerA=arguments;     return{      hasNext:function(){          return start
=n){              throw new Error("End of iteration!");          }          console.log( outerA[start++]); //correct usage      }    };}

JavaScript

Java

总结:上面的技巧在实际开发中也经常使用到,应当熟练掌握。

避免修改arguments的技巧

arguments是一个实参对象。尽管每个实参对象都包含以数字为索引的一组元素以及length属性,但它们的确不是真正的数组。

实参对象包含一个非同寻常的特性。在非严格模式下,当一个函数包含若干形参,实参对象的数组元素是函数形参所对应实参的别名形参名称可以认为是相同变量的不同命名。通过实参名字来修改实参值的话,通过arguments[]数组也可以获取到更改后的值。

下面这个例子清楚地说明了这一点:

function f(x){console.log(x);//输出实参的初始值arguments[0]=null;console.log(x);//输出"null"}var v=100;f(v); console.log(v);

输出结果如下:

100 //输出实參的初始值

null

100//因为JS函数参数是传值方式调用的

在这个例子中,arguments[0]x指代同一个值,修改其中一个的值会影响到另一个。

既然arguments对象是函数参数的别名,而不是函数参数的副本,那么有意或者无意地修改arguments对象会冒着使函数命名参数失去意义的危险。因此,在ECMAScript5中移除了实参对象的上述特殊特性,即函数参数不支持对其arguments对象取别名。请参考下面的代码:

function strictTest(val){    'use strict';    arguments[0]="modified"; //不再支持这种修改,但是没有显式给出错误提示    console.log( val===arguments[0]);}function nonstrictTest(val){    arguments[0]="modified";    console.log(val===arguments[0]);}strictTest("original");nonstrictTest("original");

false

true

在严格模式下还有一点(和非严格模式下相比的)不同,在非严格模式中,函数里的arguments仅仅是一个标识符,在严格模式中,它变成了一个保留字。严格模式中的函数无法使用arguments作为形参名或局部变量名,也不能给arguments赋值。

但是,实际开发中往往需要从函数内部修改传入的参数,特别是在不定参数的情况下。此时想到的一个办法是:尽早地复制实参对象arguments中数据到一个真正的数组中。为此,可以使用如下编码技巧:

var args=Array.prototype.slice.call(arguments);
var args=[].slice.call(arguments);
var args=[].slice.call(arguments,N);

上述技巧在实际开发及JS库中广泛使用。

关于arguments对象的calleecaller属性

calleecaller属性除了数组元素,实参对象还定义了calleecaller属性。在ECMAScript5严格模式中,对这两个属性的读写操作都会产生一个类型错误。而在非严格模式下,ECMAScript标准规范规定callee属性指代当前正在执行的函数。caller是非标准的,但大多数测览器都实现了这个属性,它指代调用当前正在执行的函数的函数。

严格模式下,参考下面代码:

function f(){    'use strict';    return f.caller;}f();

上面代码运行时出现运行时错误,信息如下:

TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

把上面函数内部的f.caller修改为f.callee时,如下:

function f(){    'use strict';    return f.callee;}console.log(f());

undefined

专家建议:避免使用arguments对象的calleecaller属性,除非有特殊的针对性需要并明确由此带来的后果。

 

转载地址:http://gdcho.baihongyu.com/

你可能感兴趣的文章
【鲁班学院】面试总结:Java高级篇(上):集合的类型以及重新认识HashMap
查看>>
GCD倒计时
查看>>
js基础
查看>>
我的友情链接
查看>>
mysql主从复制
查看>>
Symfony2CookBook:如何使用虚拟的表单域选项
查看>>
纹理与表面细节添加方法---帧映射
查看>>
Android上的磁盘加密(/data)
查看>>
我的友情链接
查看>>
装饰器
查看>>
【技术维新 践行精彩】IBM WebSphere Lombardi Edition 建模业务流
查看>>
LVS负载均衡配置与实现
查看>>
linux更改文件乱码
查看>>
linux tomcat7 开机自动启动
查看>>
Windows 2012 SYSVOL复制失败的处理
查看>>
【Git】02、Git基础
查看>>
merge_sort_recursive
查看>>
cisco交换机端口安全
查看>>
调用后台进程到前台显示
查看>>
分布式利器Zookeeper(三)
查看>>