引用类型之Function类型:函数就是对象,函数名是指针。每一个函数都是Function类型的实例,而且和其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
函数声明与函数表达式
解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其执行代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。因此,除了什么时候可以通过变量访问函数这一点区别外,函数声明与函数表达式的语法其实是等价的。
//使用函数声明,可以正常执行。首先读取并将函数声明添加到执行环境中 alert(sum(10,20)); function sum(num1,num2){ return num1+num2; } //使用函数表达式不能正确执行,因为函数还没有加载 alert(sum(10,20)); var sum=function (num1,num2){ return num1+num2; }
函数没有重载
function sum(num1,num2){ return num1+num2; } function sum(num1){ return num1; } //比没有执行第一个函数,而是执行了第二个函数。所以函数没有重载。 //理解函数名是指针,第二个函数覆盖了第一个,指针执行了另一个地址。 alert(sum(20,30)); //20
作为返回值的函数
由于ECMAScript中的函数名本身就是变量,因此函数也可以作为值来使用。即可以作为参数或者返回值。
//接收两个参数,第一个参数是一个函数,第二个应该是要传递给该函数的一个值 function callSomeFunction(someFunction,someArgument){ return someFunction(someArgument); } function add10(num){ return num+10; } var result1=callSomeFunction(add10,10); alert(result1); function getGreeting(name){ return "Hello,"+name; } var result2=callSomeFunction(getGreeting,"Tom"); alert(result2);
我们可以从一个函数中返回另一个函数,即闭包。
//通过根据不同的属性,返回不同的比较函数 function createComparisonFunction(propertyName){ return function(object1,object2){ var value1=object1[propertyName]; var value2=object2[propertyName]; if(value1value2){ return 1; }else{ return 0; } } } var data=[{name:"Tom",age:25},{name:"Jerry",age:30}]; data.sort(createComparisonFunction("name")); alert(data[0].name);//Jerry data.sort(createComparisonFunction("age")); alert(data[0].name);//Tom
函数内部属性
在函数内部,有两个特殊的对象:arguments和this。arguments的主要用途是保存函数中所有参数,这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
//使用arguments.callee替代函数名,消除耦合 function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } } alert(factorial(5));
this引用的是函数据以执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)
var color="red"; var o={color:"blue"}; function sayColor(){ alert(this.color); } sayColor(); //red //函数据以执行的环境对象 o.sayColor=sayColor; o.sayColor(); //blue
ECMAScript 5也规范化了另一个函数对象的属性:caller。这个属性保存着调用当前函数的函数的引用,如果是在全局域中调用当前函数,它的值为null。
function outer(){ inner(); } function inner(){ alert(arguments.callee); alert(inner.caller); } /** * 调用输出的是 * alert(arguments.callee); *callee 指向当前的函数 * function inner(){ alert(arguments.callee); alert(inner.caller); } * alert(inner.caller); caller指向调用当前函数的函数 * function outer(){ inner(); } */ outer();
函数的属性和方法
每个函数都包含两个属性:length和prototype。其中,length表示函数希望接收的命名参数的个数;
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
apply()方法接收两个参数,一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。
function sum(num1,num2){ return num1+num2; } function callSum1(num1,num2){ //运行函数的作用域:在这里指callSum1 //传入arguments对象 return sum.apply(this,arguments); } function callSum2(num1,num2){ //传入数组 return sum.apply(this,[num1,num2]); } alert(callSum1(10,19)); alert(callSum1(10,19));
call()方法与apply()方法的作用相同,他们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。即,在使用call()方法时,传递给函数的参数必须逐个列举出来。传递参数并非apply()和call()真正用武之地,它们真正强大的地方是能够扩充函数赖以运行的作用域。对象和方法不需要任何的耦合关系
var color="red"; var o={color:"blue"}; function sayColor(){ alert(this.color); } //在o作用于上执行,对象和方法不需要任何的耦合关系 sayColor.call(o);