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,如需转载请自行联系原作者



相关文章
|
3天前
|
存储 人工智能 自然语言处理
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
ChatMCP 是一款基于模型上下文协议(MCP)的 AI 聊天客户端,支持多语言和自动化安装。它能够与多种大型语言模型(LLM)如 OpenAI、Claude 和 OLLama 等进行交互,具备自动化安装 MCP 服务器、SSE 传输支持、自动选择服务器、聊天记录管理等功能。
43 14
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
|
15天前
|
机器学习/深度学习 JavaScript Cloud Native
Node.js作为一种快速、可扩展的服务器端运行时环境
Node.js作为一种快速、可扩展的服务器端运行时环境
29 8
|
18天前
|
JavaScript
使用Node.js创建一个简单的Web服务器
使用Node.js创建一个简单的Web服务器
|
25天前
|
安全 开发工具 Swift
Swift 是苹果公司开发的现代编程语言,具备高效、安全、简洁的特点,支持类型推断、闭包、泛型等特性,广泛应用于苹果各平台及服务器端开发
Swift 是苹果公司开发的现代编程语言,具备高效、安全、简洁的特点,支持类型推断、闭包、泛型等特性,广泛应用于苹果各平台及服务器端开发。基础语法涵盖变量、常量、数据类型、运算符、控制流等,高级特性包括函数、闭包、类、结构体、协议和泛型。
25 2
|
25天前
|
JavaScript
使用node.js搭建一个express后端服务器
Express 是 Node.js 的一个库,用于搭建后端服务器。本文将指导你从零开始构建一个简易的 Express 服务器,包括项目初始化、代码编写、服务启动与项目结构优化。通过创建 handler 和 router 文件夹分离路由和处理逻辑,使项目更清晰易维护。最后,通过 Postman 测试确保服务正常运行。
42 1
|
29天前
|
弹性计算 安全 网络安全
阿里云服务器四种常见购买方式适用对象与购买流程参考
阿里云服务器有多种购买方式,不同的方式适合对象与购买流程不一样,常见的购买方式有自定义购买、一键购买、云市场购买和通过活动购买四种方式。本文为大家介绍这四种方式的区别、适合对象以及详细的流程,以供参考和选择。
|
1月前
|
数据采集 JavaScript 搜索推荐
服务器端渲染(SSR)(Nuxt+Next.js)
服务器端渲染(SSR)技术在服务器上生成页面HTML,提升首屏加载速度和SEO效果。Nuxt.js和Next.js分别是基于Vue.js和React.js的流行SSR框架。Nuxt.js提供自动化路由管理、页面级数据获取和布局系统,支持SSR和静态站点生成。Next.js支持SSR、静态生成和文件系统路由,通过`getServerSideProps`和`getStaticProps`实现数据获取。SSR的优点包括首屏加载快、SEO友好和适合复杂页面,但也会增加服务器压力、开发限制和调试难度。选择框架时,可根据项目需求和技术栈决定使用Nuxt.js或Next.js。
|
1月前
|
缓存 负载均衡 监控
性能优化:Node.js高效服务器开发技巧与最佳实践
【10月更文挑战第29天】在Node.js服务器开发中,性能优化至关重要。本文介绍了几种高效开发的最佳实践,包括使用缓存策略、采用异步编程、实施负载均衡和性能监控。通过示例代码展示了如何实现这些技术,帮助开发者构建更快、更稳定的Node.js应用。
68 2
|
1月前
|
关系型数据库 API 数据库
后端开发的艺术:从零到一构建高效服务器
在数字化时代,后端开发是支撑现代互联网应用的基石。本文旨在探讨后端开发的核心概念、关键技术以及如何构建一个高效的服务器。我们将从基础的编程语言选择开始,逐步深入到数据库设计、API开发和性能优化等关键领域。通过实际案例分析,我们将揭示后端开发的复杂性和挑战性,同时提供实用的解决方案和最佳实践。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和启发。
|
2月前
|
网络安全 Docker 容器
VScode远程服务器之远程 远程容器 进行开发(五)
VScode远程服务器之远程 远程容器 进行开发(五)
43 1