关于ECMAScript函数参数的多方面理解

写在前面

无论在哪种编程语言中,函数都是特别有意思的部分,但同时也是一个难点。在ECMAScript中,作为对象的函数也不例外,让人又爱又恨。这一章我们主要从多个方面聊一聊函数参数这一部分,至于函数像海一样深的其他部分我们有机会再聊。

函数的有些知识点是比较简单的,所以在进入正题之前,我们先简单介绍两点:函数的return和函数的重载,因为函数的重载需要arguments的知识,所以我们最后来理解函数的重载。

章节结构如下:

  • 函数的return
  • 函数参数
  • 函数的重载

注:如没有另外说明,文中的函数均是指ECMAScript中的函数。

函数的return

return顾名思义就是返回,函数在执行完return后就会立即停止并返回,就是说,return后面的语句是永远也不会执行的。如下:

function functionReturn(){
	return ‘over;
	console.log(‘something after return‘);
}

something after return是永远也不会执行的。

注:这里可能会有歧义产生。这里说的return之后是紧接着return之后,像这样是不包括的:

function fn() {
	(function(){
		for(var i in a){
			return(i);
		}
	})();

console.log(‘hello world‘);
}

var a = [1, 2, 3];
fn();

这里有return,后面的打印依然可以正常输出,这个应该不难理解。评论里网友韩子迟有提出这个歧义,特此更正,同时也感谢韩子迟的提出。

函数参数

关于函数参数我总结了8点,我们分开来说。

1. 函数对传递进来的参数个数没有限制,对传递进来的参数的数据类型也没有限制。即,如果形参有2个,但是传递进来的实参可以是1个,也可以是3个,而且参数类型也无所谓。

2. 每个函数在被调用的时候,其活动对象会获得两个特殊的变量,其中之一就是arguments对象,在arguments对象中保存着传递给函数的参数,在函数体内可以通过arguments对象来访问这个参数数组,从而可以获得每一个实参,但是要注意的是,arguments对象只是与数组类似的一个对象,它并不是Array的实例。

看个例子:

function funcAdd(num1,num2){
	console.log(num1+num2);
	console.log(arguments);
}
funcAdd(100,200);

调用funcAdd函数的时候,实参被保存在arguments对象中,所以打印结果为300[100,200]。我们可以像访问数组一样访问arguments对象,如下:

function funcAdd(num1,num2){
	console.log(num1+num2);
	console.log(arguments[0],arguments[1],arguments[2]);
}
funcAdd(100,200);

因为只传递了两个参数,所以打印结果为,100200undefined

3. 由上面的例子,还可以得出:没有传递值的命名参数将赋值为undefined

4. 命名参数(叫形参应该好理解一些)不是必须的。

如下:

function funcAdd(){
	console.log(‘传递给函数的两个参数是:‘+arguments[0]+‘和‘+arguments[1]);
}
funcAdd(1,‘para2‘);

在这个函数中,并没有显式的给出命名参数,而是使用的arguments对象中的前两个值,打印结果是:传递给函数的两个参数是:1和para2,所以命名参数并不是必须的。显式写出命名参数一是习惯使然,二是提供一种视觉上和编程上的便利。

5. 关于命名参数,其他编程语言比如C需要函数签名,即函数名、参数个数、参数类型、返回值等相关声明信息,之后调用函数时必须和函数签名一致,但是 ECMAScript中的函数没有函数签名。这一点在下面的函数的重载中会有介绍。同时,也正是因为没有ECMAScript没有函数签名,所以函数不能 实现重载。

6. 函数的arguments对象有一个length属性,可以看成是数组的长度(arguments对象只是与数组类似的一个对象,它并不是Array的实例),从而获取传递给函数的参数的个数

如下:

function funcAdd(){
	console.log(arguments.length);
}
funcAdd();
funcAdd(1);
funcAdd(1,2);

这段代码只是打印出调用函数时传递给函数的参数个数,打印结果为:0``1``2(之前写的是123,特此更正)。

7. 命名参数的值永远和arguments中的值(arguments中有值)保持同步,这是单向的,即arguments中的值会使命名参数的值同步。

如下:

function funcAdd(num1,num2){
	arguments[0]=20;
	console.log(num1+num2);
}
funcAdd(10,10);

打印结果是:30。因为对于命名参数和arguments,函数调用的是arguments。只是一般情况下有一个命名参数的值会保存到arguments中的过程而已。

注:虽然命名参数的值永远和arguments中的值(arguments中有值)保持同步,但是它们的内存空间是独立的,并不共用同一个内存空间,只是结果两个值会同步而已。

8. ECMAScript中的所有参数传递都是按值传递,访问变量有按值访问和按引用访问两种方式。

首先说一下基本类型的复制。基本类型的复制是完全独立的,比如value1=value2,在这里将value2的值复制给了value1,这两个值是完全独立的,随意修改其中一个,另一个值是不会发生改变的。

但是引用类型的复制就不一样了,和基本类型的复制不同的是,引用类型复制的是指针,这个指针指向的都是堆内存中的同一个对象,所以,如果修改一个复制的变量,另一个变量也会跟着发生变化。

var o1=new Object(),o2=o1;
o1.color=‘red‘;
o2.color=‘blue‘;
console.log(o1.color);
console.log(o2.color);

因为o1o2引用的是同一个对象,所以修改其中一个引用,另一个跟着变化。所以打印结果分别为:blue``blue

接下来说值的传递。

基本类型值的传递和基本类型变量的复制一个道理,引用类型值的传递和引用类型值的复制一样,只是复制引用,对于函数来说,把函数外面的值传递给函数内部,就是把函数外部的值复制给函数内部,和一个变量复制一个变量的道理一样。

有了函数参数的一些理论,下面聊一聊函数重载。

没有重载

函数的重载是指函数名相同,但是形参列表(参数的个数、参数的数据类型)不相同。在其他编程语言比如C++(之前写的是C,C没有重载,特此更正)中,重载是可以实现的,但是在ECMAScript中,函数是没有重载的。先看一段代码:

function funcAdd(num1,num2){
	console.log(num1+num2);
}

function funcAdd(num){
	console.log(num);
}

funcAdd(100,200);
funcAdd(100);

在ECMAScript中,位于后面的同名函数会覆盖前面的函数,所以最终调用的函数是funcAdd(num),在执行console.log(funcAdd(100,100));是,因为调用的函数形参只有一个,传入arguments的是两个参数100200,即arguments[0]的等于100,arguments[1]的等于200,但是函数调用时只传递arguments[0],为了验证我们单独看下面一段代码:

function funcAdd(num){
	console.log(num);
	console.log(arguments);
}

funcAdd(100,200);

打印结果为100[100,200],验证了上面的结论。

虽然没有重载,但是我们可以来模拟重载机制,即可以来检测数据长度和数据类型,从而执行不同的代码来模拟重载的实现。我们简单写一段代码:

function funOverride(){
	if(arguments.length==1){
		console.log(‘Hi, ‘+arguments[0]+‘.‘);
	}else{
		console.log(‘Please input one para.‘);
	}
}
funOverride();
funOverride(‘Jim‘);

在这里,我们分别判断实参的个数从而决定不同的输出,这在某种程度上模拟了重载。当然,这只是一段简单的代码,要完全实现重载还差的很多。

时间: 2024-08-01 08:57:43

关于ECMAScript函数参数的多方面理解的相关文章

JS 函数参数

1.简单的无参函数调用 function Test1(Func) { Func(); } function Test2() { alert("我要被作为函数参数啦!"); } //使用 Test1(Test2); 2.简单的有参数的函数调用 function Test1(Func) { Func(data2); } function Test2(data1) { alert(data1); } //使用 Test1(Test2("李杰")); 3. 匿名函数作为函数

理解JavaScript函数参数

前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数. arguments javascript中的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查.实际上,javascript函数调用甚至不检查传入形参的个数 ? 1 2 3 4 5 6 7 function add(x){ return x+1; } console.log(add(1));//2 console

c语言中二维数组作函数参数以及二维数组的理解

当二维数组作函数参数接受主调函数中的二维数组时,退化为一个指向数组的指针. 在此引用<c与指针>中的观点:作为函数 参数的多位数组名的穿递方式和一维数组名相同--实际传递的是个指向数组第一个元素的指针.但是,两者之间的区别在于, 多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值.

&lt;16&gt;【理解】数组元素作为函数参数+【掌握】数组名作为函数参数+【掌握】数组名作为函数参数的注意点

#include <stdio.h> int sum(int x,int y){ return x+y; } void printNum(int x){ //判断x的值 if (x>0) { printf("%d\t",x); }else{ printf("0\t"); } } int main(int argc, const char * argv[]) { int a[5]={1,-2,-3,-4,5}; //需求:要求计算数组的第一个元素和最

python函数参数理解

1.位置参数 函数调用时,参数赋值按照位置顺序依次赋值. e.g. 1 def function(x): 2 3 return x * x 4 5 print function(2) 输出结果: 4 1 def function(x, n): 2 3 return x / n 4 5 print function(4, 2) 输出结果: 2 2.默认参数 在函数定义时,直接指定参数的值. e.g. 1 def function(x, n = 2): 2 3 s = 1 4 5 while n >

js参数arguments的理解

原文地址:js参数arguments的理解 对于函数的参数而言,如下例子 function say(name, msg){ alert(name + 'say' + msg); } say('xiao', 'hello'); 当调用say()函数时,函数会创建arguments参数数组,这个数组跟形参没有多大关系,即使没有形参, function say(){ alert(arguments[0] + 'say' + arguments[1]); } say('xiao', 'hello');

二维数组如何作为函数参数使用?(转)

如果我们需要编写一个处理二维数组的函数,那么这个函数原型应该如何声明呢? 首先,我们应该牢记:数组名被视为其地址,因此,相应的形参是一个指针.例如,假设有如下的代码: [cpp] view plaincopy int data[3][4] = { {1, 2, 3, 4}, {5, 5, 7, 8}, {9, 10, 11, 12} } int total = sum(data, 3); 那么sun函数的原型应该如何声明呢?为什么将行数3作为参数,而不将列数4作为参数呢? 我们可以这样理解:da

Python中的函数参数

在讲函数参数之前还是简单的讲一下Python中的可变对象与不可变对象. 一.可变对象与不可变对象 在Python中,一切皆对象,python中不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传址.所谓可变对象是指,对象的内容可变,而不可变对象是指对象内容不可变(即在其创建后,值不能改变,但可创建新的对象并以同一变量名对其赋值,而旧的对象会被清理掉,这在python里叫对象的垃圾收集).不可变(immutable):int.字符串(string).float.(数值型number).元

Java函数参数传递方式详解

在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简单的例子让大家自己找答案: 1. 先定义一个类型Value Java代码   public static class Value { private String value = "value"; public String getValue() { return value; } pub