JavaScript服务器端开发(函数实参对象arguments使用的几个注意事项)

简介:

为什么引入实参对象arguments

 

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

 

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

 

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

 

 

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

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

 

1
2
3
4
5
6
7
8
9
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信息时容易出现问题。请参考下面的开码片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
       } ,
       next: function (){
           if (start>=n){
               throw  new  Error( "End of iteration!" );
           }
           console.log( arguments[start++]);  //wrong usage
       }
     };
}

undefined

undefined

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
       } ,
       next: function (){
           if (start>=n){
               throw  new  Error( "End of iteration!" );
           }
           console.log( outerA[start++]);  //correct usage
       }
     };
}

JavaScript

Java

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

避免修改arguments的技巧

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

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

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

1
2
3
4
5
6
7
8
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对象取别名。请参考下面的代码:

1
2
3
4
5
6
7
8
9
10
11
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中数据到一个真正的数组中。为此,可以使用如下编码技巧:

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

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

关于arguments对象的calleecaller属性

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

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

1
2
3
4
5
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时,如下:

1
2
3
4
5
function  f(){
     'use strict' ;
     return  f.callee;
}
console.log(f());

undefined

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

 















本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/1657417,如需转载请自行联系原作者



相关文章
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
741 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
机器学习/深度学习 JavaScript 前端开发
JS进阶教程:递归函数原理与篇例解析
通过对这些代码示例的学习,我们已经了解了递归的原理以及递归在JS中的应用方法。递归虽然有着理论升华,但弄清它的核心思想并不难。举个随手可见的例子,火影鸣人做的影分身,你看到的都是同一个鸣人,但他们的行为却能在全局产生影响,这不就是递归吗?雾里看花,透过其间你或许已经深入了递归的魅力之中。
431 19
|
编解码 JavaScript 前端开发
【Java进阶】详解JavaScript的BOM(浏览器对象模型)
总的来说,BOM提供了一种方式来与浏览器进行交互。通过BOM,你可以操作窗口、获取URL、操作历史、访问HTML文档、获取浏览器信息和屏幕信息等。虽然BOM并没有正式的标准,但大多数现代浏览器都实现了相似的功能,因此,你可以放心地在你的JavaScript代码中使用BOM。
391 23
|
JavaScript
JS实现多条件搜索函数
JS封装的多条件搜索
|
JavaScript 前端开发
JavaWeb JavaScript ③ JS的流程控制和函数
通过本文的详细介绍,您可以深入理解JavaScript的流程控制和函数的使用,进而编写出高效、可维护的代码。
319 32
|
JavaScript 前端开发 Java
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
柯里化是一种强大的函数式编程技术,它通过将函数分解为单参数形式,实现了灵活性与可复用性的统一。无论是参数复用、延迟执行,还是函数组合,柯里化都为现代编程提供了极大的便利。 从 Redux 的选择器优化到复杂的数据流处理,再到深度嵌套的函数优化,柯里化在实际开发中展现出了非凡的价值。如果你希望编写更简洁、更优雅的代码,柯里化无疑是一个值得深入学习和实践的工具。从简单的实现到复杂的应用,希望这篇博客能为你揭开柯里化的奥秘,助力你的开发之旅! 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
8月前
|
弹性计算 运维 安全
阿里云轻量应用服务器与云服务器ECS啥区别?新手帮助教程
阿里云轻量应用服务器适合个人开发者搭建博客、测试环境等低流量场景,操作简单、成本低;ECS适用于企业级高负载业务,功能强大、灵活可扩展。二者在性能、网络、镜像及运维管理上差异显著,用户应根据实际需求选择。
687 10