深入理解JavaScript函数参数机制

作者:拾光

时间:2026-10-30

浏览:224

本文深入解析JavaScript函数参数的独特行为,包括不限制参数个数与类型、arguments对象的属性与同步机制、callee与caller的使用及严格模式限制,以及按值传递与引用传递的区别,并通过代码示例说明如何模拟函数重载。

文章目录

JavaScript函数参数概述

前言

JavaScript函数的参数机制与多数其他编程语言存在显著差异。在JavaScript中,调用函数时传入的参数数量不受限制,参数的数据类型也不受约束,甚至允许不传递任何参数。

arguments对象详解

arguments对象

JavaScript函数定义时并未指定形参的具体类型,调用时也不会对实参进行类型检查。此外,JavaScript函数调用甚至不校验传入实参的数量是否与形参数量匹配。


function add(x){
return x+1;
}
console.log(add(1));//2
console.log(add('1'));//'11'
console.log(add());//NaN
console.log(add(1,2));//2

同名形参处理

在非严格模式下,函数定义中允许出现同名形参,但访问该名称时只能获取最后声明的那个形参值。


function add(x,x,x){
return x;
}
console.log(add(1,2,3));//3

若启用严格模式('use strict'),定义同名形参会直接导致语法错误(SyntaxError)。


function add(x,x,x){
'use strict';
return x;
}
console.log(add(1,2,3));//SyntaxError: Duplicate parameter name not allowed in this context

参数数量处理

当传入的实参数量少于函数声明的形参数量时,未对应的剩余形参将自动被赋予undefined值。


function add(x,y){
console.log(x,y);//1 undefined
}
add(1);

通常利用逻辑或运算符(||)为缺失的参数提供合理的默认值。


function add(x,y){
y = y || 2;
console.log(x,y);//1 2
}
add(1);

[注意]使用y || 2这种方式并不严谨,因为当y为undefined、null、false、0、-0、空字符串或NaN等假值时,结果与省略参数相同。因此,应根据具体业务场景选择合适的默认值设置方式。

当传入的实参数量多于形参数量时,多余的实参无法通过形参名直接访问,必须借助下文将介绍的arguments对象来获取。

JavaScript内部将函数接收到的参数存储在一个类数组对象中,无论实际传入哪些参数。在函数内部,可以通过arguments对象访问这个参数数组,从而获取每一个传递进来的参数。需要注意的是,arguments并非Array的实例,但它支持使用方括号语法访问元素。


function add(x){
console.log(arguments[0],arguments[1],arguments[2])//1 2 3
return x+1;
}
add(1,2,3);

arguments对象具有length属性,用于显示实际传入的实参个数;而函数本身的length属性则显示定义时声明的形参个数。


function add(x,y){
console.log(arguments.length)//3
return x+1;
}
add(1,2,3);
console.log(add.length);//2

形参的定义主要是为了提供便利,并非访问参数的必要条件。


function add(){
return arguments[0] + arguments[1];
}
console.log(add(1,2));//3

对象参数模式

对象参数模式

当函数包含超过3个形参时,记忆调用时实参的正确顺序变得困难且容易出错。


function arraycopy(/*array*/from,/*index*/form_start,/*array*/to,/*index*/to_start,/*integer*/length){
//todo
}

可以通过名/值对的形式传入参数,从而摆脱顺序限制。定义函数时,将所有实参封装在一个单独的对象中;调用时传入该对象,对象中的名/值对即代表所需的实参数据。


function easycopy(args){
arraycopy(args.from,args.form_start || 0,args.to,args.to_start || 0, args.length);
}
var a = [1,2,3,4],b =[];
easycopy({form:a,to:b,length:4});

参数同步机制

参数同步机制

当实参数量与形参数量一致时,arguments对象中对应索引的值与同名形参的值保持同步。


function test(num1,num2){
console.log(num1,arguments[0]);//1 1
arguments[0] = 2;
console.log(num1,arguments[0]);//2 2
num1 = 10;
console.log(num1,arguments[0]);//10 10
}
test(1);

[注意]尽管命名形参与arguments对象中对应索引的值相同,但它们并不共享同一命名空间。两者的命名空间是独立的,但数值是同步关联的。

在严格模式下,arguments对象中的值与形参的值变为相互独立,不再同步。


function test(num1,num2){
'use strict';
console.log(num1,arguments[0]);//1 1
arguments[0] = 2;
console.log(num1,arguments[0]);//1 2
num1 = 10;
console.log(num1,arguments[0]);//10 2
}
test(1);

如果某个形参没有对应的实参传入,arguments对象中对应索引的值与形参的值将不对应,通常表现为undefined。


function test(num1,num2){
console.log(num1,arguments[0]);//undefined,undefined
num1 = 10;
arguments[0] = 5;
console.log(num1,arguments[0]);//10,5
}
test(); 

arguments内部属性

内部属性

【callee属性】

arguments对象包含一个名为callee的属性,该属性是一个指针,指向拥有该arguments对象的函数本身。

以下是经典的阶乘函数实现示例。


function factorial(num){
if(num <=1){
return 1;
}else{
return num* factorial(num-1);
}
} 
console.log(factorial(5));//120

上述函数的执行逻辑与函数名紧密耦合,使用arguments.callee可以解除这种耦合,使函数更通用。


function factorial(num){
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
} 
console.log(factorial(5));//120

然而,在严格模式下访问arguments.callee会抛出TypeError错误。


function factorial(num){
'use strict';
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
} 
//TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
console.log(factorial(5));

此时,推荐使用具名函数表达式来替代。


var factorial = function fn(num){
if(num <=1){
return 1;
}else{
return num*fn(num-1);
}
}; 
console.log(factorial(5));//120

【caller属性】

实际上存在两个不同的caller属性概念。

【1】函数的caller属性

函数的caller属性保存着调用当前函数的外部函数的引用。如果当前函数是在全局作用域中被调用,该属性的值为null。


function outer(){
inner();
}
function inner(){
console.log(inner.caller);//outer(){inner();}
}
outer(); 
function inner(){
console.log(inner.caller);//null
}
inner();

在严格模式下,访问函数的caller属性会抛出TypeError错误。


function inner(){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(inner.caller);
}
inner();

【2】arguments对象的caller属性

该属性始终为undefined,定义此属性旨在区分arguments.caller与函数的caller属性。


function inner(x){
console.log(arguments.caller);//undefined
}
inner(1);

同样地,在严格模式下访问arguments.caller会抛出TypeError错误。


function inner(x){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(arguments.caller);
}
inner(1);

函数重载模拟

函数重载模拟

JavaScript函数无法像传统强类型语言那样实现真正的重载。在其他语言中,只要函数签名(参数类型和数量)不同,就可以为同一函数编写多个定义。

JavaScript函数的参数由包含0个或多个值的数组表示,没有固定的函数签名,因此真正的重载无法实现。


//后面的声明覆盖了前面的声明
function addSomeNumber(num){
return num + 100;
}
function addSomeNumber(num){
return num + 200;
}
var result = addSomeNumber(100);//300

可以通过检查传入参数的类型和数量,并据此做出不同反应,来模拟方法的重载行为。


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

参数传递机制

参数传递方式

JavaScript中所有函数的参数均按值传递。这意味着将函数外部的值复制到函数内部的参数中,其过程与将一个变量的值复制到另一个变量完全相同。

【1】基本类型值传递

当传递基本类型值时,该值会被复制给一个局部变量(即命名参数或arguments对象的一个元素)。


function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count);//20,没有变化
console.log(result);//30

【2】引用类型值传递

当传递引用类型值时,该值在内存中的地址会被复制给一个局部变量。因此,对该局部变量的修改会反映在函数外部。


function setName(obj){
obj.name = 'test';
}
var person = new Object();
setName(person);
console.log(person.name);//'test'

如果在函数内部重写引用类型的形参,该变量将指向一个新的局部对象。这个新创建的局部对象会在函数执行完毕后立即被销毁,不影响外部引用。


function setName(obj){
obj.name = 'test';
console.log(person.name);//'test'
obj = new Object();
obj.name = 'white';
console.log(person.name);//'test'
}
var person = new Object();
setName(person); 

以上是对JavaScript函数参数的深入解析,希望对大家有所帮助。如有任何疑问,欢迎留言,小编会及时回复。感谢大家对本站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章