JavaScript函数
定义函数
最常见的函数定义方法如下所示,该示例定义了一个函数——sayHi。
sayHi();
function sayHi(name, message) {
alert("hello " + name + "," + message);
}
还可以通过函数表达式定义一个函数,如下例所示,同样定义了函数sayHi,只不过sayHi在这里是作为一个变量出现的,使用typeof sayHi
检测得到"function"
。但由于JavaScript变量初始化顺序的问题,这种定义方式要求函数必须在声明后才能使用,而不能像前面的示例一样在声明前使用。而且,从这种定义方式,也就不难理解另一个现象:函数可以被覆盖,就像变量被重新赋值一样。
var sayHi = function(name, message) {
alert("hello " + name + "," + message);
}
sayHi();
函数的参数
没有重载
JavaScript的函数没有重载。以前面定义的
sayHi
函数为例,虽然函数声明了name
和message
两个参数,但下面几种调用方式均不会出错,即实际传入的参数数量并不需要和函数声明中的形参一致,未传入的参数值为undefined
,多余的参数无法直接通过形参访问。从这个角度,JavaScript的函数没有重载的概念。sayHi(); // =hello undefined,undefined sayHi("John"); // =hello John,undefined sayHi("John", "welcome to javascript"); // =hello John,welcome to javascript sayHi("John", "welcome to javascript", "2015/08/26") // =hello John,welcome to javascript
arguments对象
如上所述,实际传入函数的参数数量可以和函数声明中的形参不一致。其实,在调用函数时,会生成一个参数数组对象——
arguments
,在函数内部可以访问该对象来获取每一个参数。它的特点有:- 与数组类似(然而并不是Array实例),可以通过下标访问元素,如
arguments[0]
访问第一个参数,arguments[1]
访问第二个参数,依次类推 - 参数数组长度不可改变,由传入的参数个数决定的
- 若有定义形参,则它的值与对应形参的值保持同步,即在函数内对形参的修改会同步到对应的
arguments
元素上,反之亦然。需要强调的是,形参与arguments
对应不同的内存空间,只不过它们的值会同步
- 与数组类似(然而并不是Array实例),可以通过下标访问元素,如
函数作为变量
因为函数名本身就是变量,所以函数也可以作为值来使用。即函数既可以被赋值给一个变量,也可以作为参数传递给另一个函数,或者被另一个函数返回。如下面的示例,函数runWithOne
接受另一个函数作为参数,并运行传入的函数。
function runWithOne(f) {
f(1);
}
funWithOne(function(a) {
alert(a);
});
this属性
this引用
this是函数的一个内部属性,引用的是执行函数的环境对象,当在网页的全局作用域中调用函数时,this对象引用的就是window。需要强调的是,在调用函数之前,this的值是不确定的。如下面的示例所示:
window.color = "red";
var o = {color: "blue"};
function sayColor() {
alert(this.color);
}
sayColor(); // =red; this = window
o.sayColor = sayColor;
o.sayColor(); // =blue; this = o
改变this引用
可以通过apply
、call
或bind
方法改变函数的this引用。具体方法为:
apply
函数的内部方法,第一个参数为作用域,第二个参数为参数数组,以前面的递归函数
factorial
为例。其中,下面的两种调用方式均可。function callFactorial(num) { return factorial.apply(this, arguments); // return factorial.apply(this, [num]); }
call
函数的内部方法,第一个参数为作用域,其余的参数直接以形参的形式传递给要调用的函数,而不是参数数组(这是与
apply
方法唯一的区别)。以前面的递归函数factorial
为例。function callFactorial(num) { return factorial.call(this, num); }
bind
该方法创建一个函数实例,唯一的参数为作用域,如下例所示。
function callFactorial(num) { var newFactorial = factorial.bind(this); return newFactorial(num); }
构造函数
当使用new
创建一个函数对象时,会将函数的作用域赋给新建的对象,因此,那时候,this指向的不再是执行函数的环境对象。该问题在后续会继续介绍。
其他函数内部属性
arguments
前面介绍过,
arguments
存放函数的参数,此外,它还有一个callee
属性,指向函数本身。比如下面是一个阶乘算法的示例。function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } }
但该实现的一个很大缺点就是函数的实现与函数名
factorial
紧密耦合,如果需要修改函数名,则还需要修改对应的函数实现。下面展示了如何通过callee
来解决这个问题:function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } }
在严格模式下,访问
arguments.callee
会出错。caller
caller保存了调用当前函数的函数的引用。如果在全局作用域中调用该函数,则其值为null。调用方式如下例所示,其中,使用
arguments.callee.caller
可以实现更松散的耦合。function outer() { inner(); } function inner() { alert(inner.caller); alert(arguments.callee.caller); } outer();