函数

函数对于任何语言来说都是一个核心的概念。ECMAScript中的函数使用function关键字来声明,后跟一组参数以及函数体。

创建函数的两种方式:1.函数声明;2.函数表达式

1.函数声明的语法:

1 function functionName(arg0, arg1, arg2) {
2     //函数体
3 }
//Firefox、Safari、Chrome和OPera都给函数定义了一个非标准的name属性,通过这个属性可以访问到函数指定的名字
alert(functionName.name); //"functionName"

2.函数表达式的语法:

1 var functionName = function(arg0, arg1, arg2) {
2     //函数体
3 };

注:使用函数声明的方式创建函数,会存在函数声明提升(function declaration hoisting)的现象。意思是在执行代码之前会先读取函数声明。即可以把函数声明放在调用它的语句后面:

1 sayHi();
2 function sayHi() {
3     alert("Hi !");
4 }

但是函数表达式不存在函数提升。

理解函数的参数

ECMAScript函数的参数与大多数其他语言中函数的参数有所不同。ECMAScript函数不介意传递进来多少个参数,也不在乎传进来参数的数据类型。意思是说,当你定义一个只接收两个参数的函数,在调用这个函数的时候,可以传递一个,两个、三个甚至不传递参数。

原因是ECMAScript中函数的参数在内部是用一个数组来表示的。函数接收的始终是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。

arguments对象是一个类数组对象(并不是Array的实例),因为可以使用方括号语法访问它的每一个元素,使用length属性来确定传递进来多少个参数。

 1 function sayHi() {
 2     alert("Hello " + arguments[0] + ", " + arguments[1]);
 3 }
 4 
 5 function howManyArgs() {
 6     alert(arguments.length);
 7 }
 8 
 9 howManyArgs("string", 45);  //2
10 howManyArgs();  //0

arguments对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数个数决定的。没有传递值的命名参数将被自动赋予undefined值,这跟定义了变量但又没有初始化一样。

 1 function doAdd1(num1, num2) {
 2     if(arguments.length == 1) {
 3         alert(num1 + 10);
 4     } else if(arguments.length == 2) {
 5         alert(arguments[0] + num2);
 6     }
 7 }
 8 
 9 //如果只给doAdd1()函数传递了一个参数,则num2中就会保存undefined值
10 
11 //arguments对象可以与命名参数一起使用
12 
13 function doAdd2(num1, num2) {
14     arguments[1] = 20;
15     alert(arguments[0] + num2);
16 }
17 
18 //如果doAdd()只传入了一个参数,那么为arguments[1]设置的值不会反映到命名参数中。

注:ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数

没有重载

ECMAScript函数不能像传统意义上那样实现重载。而在其他语言中(如Java),可以为一个函数编写两个定义,只要这两个定义的签名(接收的参数类型和数量)不同即可。ECMAScript函数没有签名,因为其参数是由包含零或多个值的arguments对象来表示的。而没有函数签名,真正的重载是不可能做到的。

 如果在ECMAScript中定义了两个名字相同的函数,则该名字只属于后定义的函数。

1 function addSomeNumber(num) {
2     return num + 100;
3 }
4 
5 function addSomeNumber(num1, num2) {
6     return num1 + 200;
7 }
8 
9 var result = addSomeNumber(200);//300

Function 类型

函数也是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。换句话说一个函数可能会有多个名字。

 1 function sum(num1, num2) {
 2     return numm1 + num2;
 3 }
 4 alert(sum(10, 10));  //20
 5 
 6 var anotherSum = sum;
 7 alert(anotherSum(10, 10));  //20
 8 
 9 sum = null;
10 alert(anotherSum(10, 10));  //20

函数内部属性

在函数内部,有两个特殊的对象:argumentsthis。arguments是一个类数组对象,包含着传入函数中的所有参数。arguments对象有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数

 1 function factorial1(num) {
 2     if(num <= 1) {
 3         return 1;
 4     } else {
 5         return num * factorial(num-1);
 6     }
 7 }
 8 
 9 //使用arguments.callee重写factorial()函数
10 function factorial2(num) {
11     if(num <= 1) {
12         return 1;
13     } else {
14     return num * arguments.callee(num-1);
15     }
16 }
17 
18 //这种写法消除了这个函数的执行与函数名factorial紧紧耦合在一起的现象。
19 
20 var trueFactorial = factorial2;
21 
22 factorial2 = function() {
23     return 0;
24 };
25 
26 alert(trueFactorial(5)); //120
27 alert(factorial2(5));  //0

函数内部的另一个特殊对象是this。this引用的是函数据以执行的环境对象。

ECMAScript5中规范化了另一个函数的属性caller。这个属性中保存着调用当前函数的函数的引用。如果是在全局作用域中调用当前函数,它的值为null。

 1 function outer() {
 2     inner();
 3 }
 4 
 5 function inner() {
 6     alert(inner.caller);
 7 }
 8 
 9 outer();  //警告框中显示outer()函数的源代码。
10 
11 function inner() {
12     alert(arguments.callee.caller);
13 }

函数属性和方法

每个函数都包含两个属性:lengthprototype。其中,length属性表示函数希望接收的命名参数的个数

 1 function sayName(name) {
 2     alert(name);
 3 }
 4 
 5 function sayHi() {
 6     alert("hi");
 7 }
 8 
 9 function sum(num1, num2) {
10     return num1 + num2;
11 }
12 
13 alert(sayName.length);  //1;
14 alert(sayHi.length);  //0;
15 alert(sum.length);  //2

对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。换句话说,诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。

每个函数包含两个非继承而来的方法:apply()cal()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array实例,也可以说是arguments对象。

call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。

ECMAScript 5还定义了一个方法:bind()。这个方法创建一个函数的实例,其this值会被绑定到传给bind()方法的值。

1 window.color = "red";
2 var o = {color: "blue"};
3 
4 function sayColor() {
5     alert(this.color);
6 }
7 
8 var objectSayColor = sayColor.bind(o);
9 objectSayColor();  //blue

 

posted @ 2020-09-26 13:53  焱雨  阅读(138)  评论(0编辑  收藏  举报