http://book.csdn.net/bookfiles/959/10095930029.shtml
Function(函数)是JavaScript程序中最为常见的语法,也是JavaScript程序最基本的元素之一。事实上,Function可视为JavaScript语言的核心体系。灵活运用Function往往会使程序达到出奇制胜的效果!
通过对闭包、函数式编程及元编程的认识,可以看出Function在JavaScript中的核心地位。对Function的深入理解将引领我们步入JavaScript高级应用的殿堂。
Ø JavaScript Function对象
Ø 闭包
Ø 函数式编程
Ø 元编程
本章旨在阐述JavaScript函数(Function)对象的高级应用技巧。首先介绍函数对象的属性和方法,以及定义函数的方式。接着介绍定义函数时经常采用的一种高级技巧——闭包,并阐述使用闭包时容易发生的问题及相应的解决之道。最后介绍函数式编程思想及元编程。
Function是JavaScript的一种特殊的数据类型,在JavaScript运行期拥有两种含义:(1)Function类型对象;(2)声明一个新的方法或者函数。如:
//声明一个变量funO,并定义该变量为Function对象
var funO = new Function("x","alert(x)");
//定义一个function,并命名为funF
function funF(x)
{
alert(x);
}
调用funO的结果与调用funF相同,而并不认为funO为一对象。而方法funF亦隐式地继承了Function对象的原型方法,如“apply”、“call”等。JavaScript Function对象与function函数的关系,可以简单地定义为“自动拆/装包”,即对象与此类对象所代表的值或实例可以自动隐式地转换。
Function对象拥有如下属性和方法。
(1)arguments,当前Function执行函数的参数列表对象。arguments拥有一个length属性,表示Function参数的个数。如:
function test()
{
alert(arguments.length);
}
function test1()
{
alert(test1.arguments.length)
}
在JavaScript中,方法或函数不会验证传递给自己的实参个数是否等于自己所定义的形参个数,即方法或函数可以接受任意个数的参数,而不会引发任何错误。任何遗漏的参数都会以undefined传递给函数,多余的参数将忽略。如:
function funcParameter(a,b,c)
{
alert(a+" "+b+" "+c);
}
funcParameter(1); //1 undefined
funcParameter(1,2);//1 2 undefined
funcParameter(1,2,3,4);// 1 2 3忽略了4
使用实参的方式有两种:一种为使用形参传递;另一种为使用arguments对象传递。如:
function funcParameter(a,b,c)
{
var p = [];
for(var i=0;i<arguments.length;i++)
{
p[i]=arguments[i];
}
alert(p.join(" "));
}
funcParameter(1); //1
funcParameter(1,2);//1 2
funcParameter(1,2,3,4);// 1 2 3 4
arguments传递参数区别于形参传递方式之处在于,arguments对象能分析实参的个数,且忽略形参所设定的函数或方法的参数个数,从而使JavaScript具有一定程度上的对象重载的特点。如:
function overload()
{
var arg = arguments;
if(arguments.length==0)
{
(function()
{
alert("IllegalArgumentException");
})();
}
if(arguments.length==1)
{
(function()
{
alert(arguments[0]);
}).apply(null,arg);
}
if(arguments.length==2)
{
(function()
{
alert(arguments[0]+" "+arguments[1]);
}).apply(null,arg);
}
}
overload(); // IllegalArgumentException
overload(1); //1
overload(1,2); //1 2
arguments可以理解为特殊的数组,对于arguments对象所包含的元素,其访问方法与访问数组中的元素的方法相同,都使用顺序位作为下标。
arguments对象拥有两个主要属性分别为:
Ø length,表示实参的个数。如上代码片段中的“arguments.length”。
Ø callee,返回正在执行的Function对象。如:
function calleeTest()
{
alert(arguments.callee); // calleeTest
}
callee作为arguments对象的一个属性,仅当相关函数正在执行时才可用,初始值为正在执行的Function对象。callee在匿名函数的递归调用中特别有用。如:
(function()
{
var _this = arguments.callee;
if( !_this.$value )
{
_this.$value = 0;
}
_this.$value++;
if( _this.$value > 10 )
{
return _this.$value; //11
}
_this.apply(null,arguments);
})();
Ø caller,表示当前函数的调用者的一个引用,如:
function callerTest()
{
alert(callerTest.caller );
}
function test()
{
callerTest();
}
test(); //function test
与arguments不同,caller属性中不能省略当前执行函数的函数名,即不能把“callerTest.caller”省略为“caller”。
(2)prototype,在JavaScript中所有对象的原型引用。是JavaScript实现对象继承的主要手段。如:
function testPrototype(){}
testPrototype.prototype.a=1;
var test = new testPrototype();
alert(test.a);
(3)constructor,表示当前对象的构造方法,也是实现对象继承的主要手段,通过该属性可以有效地区别当前对象的构造方法。
(4)toString,Function对象内部方法,如果是自定义Function,则返回Function对象的完整定义,反之返回以下格式的字符串:
Function functionname( ) { [native code] }
提示:我们在3.2.4节中曾经利用这一特点来判断某个内部方法是否被劫持。
(5)valueOf,同toString类似,如果是自定义Function,则返回Function对象的完整定义,反之返回相应格式的字符串。
(6)apply,代理Function的调用。该方法有两个参数:被调用对象的原型链上下文,即关键字“this”所表示的执行环境;一个数组类型的参数列表,该参数是可选的。通过apply的代理,可以实现简单的对象、方法的继承。如:
function testApply(a,b)
{
this.a=a;
this.b=b;
}
var to = {};
to.c="11";
testApply.apply(to,[5,2]);
alert(to.a);
上面的代码片段运行后输出“5”。
(7)call,与apply类似,代理Function的调用,不同的是除了第1个参数表示执行上下文外,其他的可选参数个数不定,这些参数的顺序位与被代理Function的顺序位一一对应,如:
function testCall(a,b,c)
{
alert(this.d+" "+a+" "+b+" "+c);
}
var d = {};
d.d="d";
testCall.call(d,"a","b",1);
提示:上面的代码片段运行后输出“d a b 1”。提示:
有关Function对象apply、call方法在JavaScript面向对象编程中的作用,详见第6章“JavaScript面向对象编程”。
JavaScript中定义function有以下三种方式:
(1)声明一个表达式变量,并定义该变量的表达式。如:
var func = function()
{
/* body code*/
}
(2)定义一个function表达式,并指定该表达式的标识。如:
function func()
{
//body code
}
(3)使用JavaScript内置Function对象构造。如:
var funcO = new Function(“/*parameters*/”,”/*body code*/”);
声明变量定义与使用function表达式标识定义是有区别的。我们知道,function在发生传递时采用的是引用传递类型,使用变量定义时保存了表达式的地址引用,而使用标识定义保存了表达式的地址。因此当我们改变或重新定义变量时,并不会导致原来的表达式改变;而当改变标识时,其对应的表达式也随之改变。如:
//声明一个变量,并定义该变量的表达式引用
var test = function()
{
alert("reference test");
}
//定义一个表达式,保存其地址信息于 test中
function test1()
{
alert("reference1 test1");
}
//将 test所引用的表达式传递给reference
var reference = test;
//将 test1表达式的地址传递给referencl
var reference1 = test1;
//改变变量 test的引用
test = function()
{
alert("new test");
}
//重新定义 test1地址内的数据
function test1()
{
alert("new test1");
}
alert(reference); //其所引用的表达式不改变
alert(reference1); //由于referencel是testl地址的引用,当testl地址表示的内容改变时,referencel的内容也随之改变