深入理解JavaScript中的arguments对象:特性、陷阱与最佳实践
本文深入解析JavaScript中重要的arguments对象。文章介绍了其作为类数组对象的特性,展示了如何利用它创建可变参数列表函数。重点讨论了ES5严格模式下arguments与形参绑定的差异,以及修改arguments时的安全风险。此外,还讲解了通过闭包保存arguments引用的技巧,并辨析了callee与caller属性的区别与应用场景。
arguments对象的基本概念
1、什么是arguments
arguments是JavaScript内置的一个特殊对象,它常被开发者忽视,却在核心功能中扮演着关键角色。几乎所有主流JavaScript库都依赖于此对象,因此熟悉它是JS程序员的必修课。在函数内部,标识符arguments指向一个特殊的属性,用于引用这个Arguments对象。虽然它表现得像数组,但需要明确的是,它并非真正的数组实例。
在JavaScript函数体内,arguments对象表现出类似数组的特征(尽管它本质上是Arguments对象而非数组实例)。它拥有length属性,该属性记录了实际传递给函数的参数数量。
JavaScript中的Arguments对象存储的是调用函数时传入的实际参数。其长度由实参的个数决定,而非函数定义中的形参个数。形参是在函数内部独立开辟内存空间存储的变量,它们与arguments对象所占用的内存空间并不重叠。
JavaScript引擎不会自动验证传递给函数的参数数量是否正确。如果传入的参数多于定义的数量,多余的部分将被忽略;如果少于定义的数量,未提供的参数值将为undefined。因此,我们可以利用arguments的length属性来检查调用时是否传递了正确数量的参数,因为JS本身不会执行此类检查。
function f(x,y,z)
{
//首先检查传递的参数数量是否正确
if(arguments.length != 3)
{
throw new Error("function f called with " + arguments.length + "arguments");
}
//下面运行真正的函数
}
利用arguments构建可变参数函数
2、arguments创建可变参数列表函数
arguments提供了一种机制,允许函数接收任意数量的实际参数:
例如,我们可以编写一个display()函数来计算多家公司的员工工资总和。无论传入多少个参数都可以,但前提是你必须传入数字,因为函数内部没有进行类型判断。
function display(){
var sum=0; //总额
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
document.write(sum+'<br>');
}
//A公司
display(10000,2000,5000);
//B公司
display(1000,2000,5000,8000,10000);
这种方法是否非常巧妙?
需要说明的是,arguments与显式声明的形式参数在值存在时是一致的:
当arguments和显式参数都存在时,修改其中一个会同步改变另一个的值,即两者的值会保持一致。
function f(a, b, c){
alert(arguments.length); // result: "2"
a = 100;
alert(arguments[0]); // result: "100"
arguments[0] = "qqyumidi";
alert(a); // result: "qqyumidi"
alert(c); // result: "undefined"
c = 2012;
alert(arguments[2]); // result: "undefined"
}
f(1, 2);
修改arguments对象的风险与ES5严格模式
3、永远不要修改arguments对象
函数中声明的参数与arguments之间的联系非常脆弱。每个声明的参数实际上只是对arguments对象中对应位置的一个引用。
值得注意的是,在ES5的strict mode(严格模式)中,函数声明的参数不再引用arguments:
function strict(x) {
"use strict";
arguments[0] = "modified";
return x === arguments[0];
}
function nonstrict(x) {
arguments[0] = "modified";
return x === arguments[0];
}
strict("unmodified"); // false
nonstrict("unmodified"); // true
正因为strict模式与非strict模式下,函数声明参数与arguments的关系不一致,为了避免潜在问题,最安全的做法是不要直接修改arguments对象。
如果确实需要修改arguments对象,可以先将其赋值给一个副本:
var args = [].slice.call(arguments); 当slice方法不接受任何参数时,它会执行复制操作,生成的args是一个真正的数组对象。同时,args与函数声明的参数之间不再有任何联系,对args进行操作是安全的。
保存arguments引用的最佳实践
4、一个变量来保存arguments的引用
假设我们需要一个API来遍历若干元素,用法如下:
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // 1
it.next(); // 4
it.next(); // 1
相应的实现代码可以是:
function values() {
var i = 0, n = arguments.length;
return {
hasNext: function() {
return i < n;
},
next: function() {
if (i >= n) {
throw new Error("end of iteration");
}
return arguments[i++]; // wrong arguments
}
};
}
然而实际执行结果却是:
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // undefined
it.next(); // undefined
it.next(); // undefined
原因在于:对arguments对象的赋值是隐式完成的。在next方法内部使用了arguments,但此arguments与values方法开始处的arguments并非同一个对象。这里的arguments对象指的是函数next()的作用域内的arguments。
解决方法很简单,即将需要访问的arguments使用另一个变量进行引用。然后利用闭包的特性在嵌套函数中访问它,如下所示:
function values() {
var i = 0, n = arguments.length, a = arguments;
return {
hasNext: function() {
return i < n;
},
next: function() {
if (i >= n) {
throw new Error("end of iteration");
}
return a[i++];
}
};
}
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // 1
it.next(); // 4
it.next(); // 1
arguments.callee属性与递归应用
5、arguments对象的callee属性:
arguments的callee属性用于引用当前正在执行的函数,这对于未命名函数调用自身非常有用。
首先使用命名函数表达式实现递归函数:
//函数直接量 指定函数名 递归函数
var result = function fact(x){
if(x<=1)
return 1;
else
return x*fact(x-1);
};
之前提到,可以为函数直接量指定函数名。这样实现递归可以方便地调用自身。
现在使用arguments的callee属性同样可以简单地实现:
//用函数直接量,采用arguments.callee属性实现递归函数
var result = function(x){
if(x<=1) return 1;
return x*arguments.callee(x-1);
};
最后提醒大家一点,既然arguments如此强大,就不要将变量命名为arguments,事实上arguments是JavaScript的保留字之一。
最后补充一点:
arguments.caller属性详解
区别caller
返回一个对函数的引用,该函数调用了当前函数。
- - functionName.caller
- - functionName 对象是所执行函数的名称。
对于函数来说,caller属性仅在函数执行时才有定义。如果函数是由顶层调用的,那么caller包含的就是null。如果在字符串上下文中使用caller属性,结果与functionName.toString相同,即显示函数的反编译文本。
代码:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->// caller demo {
function callerDemo() {
if (callerDemo.caller) {
var a= callerDemo.caller.toString();
alert(a);
} else {
alert("this is a top function");
}
}
function handleCaller() {
callerDemo();
}
handleCaller();//弹出handleCaller的定义
以上就是针对javascript的arguments对象的相关介绍,希望对大家的学习有所帮助。
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
