ECMAScript中的函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。
由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
function sum(num1,num2){
return num1+num2;
}
var sum = function(num1,num2){
return num1+num2;
};
sum(10,10); //完全可以正常运行
function sum(num1,num2){
return num1+num2;
}
上段代码之所以可以运行,是因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。所以即使声明函数的代码在调用它的代码后面,js引擎也能把函数声明提升到顶部。
sum(10,10); //产生错误
var sum = function (num1,num2){
return num1+num2;
}
上段代码之所以会产生错误,原因在于函数位于一个初始化语句中,而不是一个函数声明,在执行到函数所在语句之前,变量sum中不会保存有对函数的引用。
构造函数可以接收任意数量的参数,但最后一个参数始终被看成是函数体,而前面的参数则枚举出了新函数的参数。
var sum = new Function("num1","num2","return num1+num2");
不推荐这种方法定义函数,因为这种方法回到直接洗两次代码(第一次解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没什么不同。
function sum(num1,num2){
return num1+num2;
}
sum(10,10); //20
//anotherSum和sum指向同一函数
var anotherSum = sum;
anotherSum(10,10); //20
sum = null;
anotherSum(10,10); //20
使用不带圆括号的函数名是访问指针,而非调用函数。
当一个函数被调用时,它从第一个语句开始执行,并在遇到关闭函数体的}
时结束,然后函数把控制权交还给调用该函数的程序。
return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。
一个函数总会返回一个值,如果没有指定返回值,则返回undefined。
如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回该新对象(this)。
之前曾经有个例子:
function addSomeNumber(num){
return num+=100;
}
function addSomeNumber(num){
return num+=200;
}
var result = addSomeNumber(100); //300
结果是后面的函数覆盖了前面的函数。以上代码其实和以下代码没什么区别:
var addSomeNumber = function(num){
return num+=100;
}
addSomeNumber= function(num){
return num+=200;
}
var result = addSomeNumber(100); //300
观察重写之后的代码,很容易看清楚在创建第二个函数时,实际上覆盖了引用第一个函数的变量。
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。
function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}
fucntion add10(num){
return num+10;
}
var result = callSomeFunction(add10,10); //20
要访问函数的指针而不执行函数的话,必须去掉函数名后面的圆括号。因此上面例子中传递给callSomeFunction的是add10而不是执行它们之后的结果。
是一个类数组对象,包含着传入函数中的所有参数。
//经典的阶乘函数
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
这个函数的执行与函数名紧紧耦合在了一起,为了消除这种耦合现象,可以使用arguments.callee:
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
是函数据以执行的环境对象。
window.color = "red";
var o = {color:"blue"};
fucntion sayColor(){
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); //blue
函数的名字仅仅是一个包含指针的变量而已,因此,即使在不同的环境中执行,全局的sayColor和o.sayColor 指向的仍然是同一个函数。
保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer();
alert警告框中最后会显示outer函数的源代码。因为outer调用了inner,所以inner.caller就指向outer()。
为了实现更松散的耦合,也可以通过arguments.callee.caller来访问相同的信息。
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);
}
outer();
当在严格模式下运行时,访问arguments.callee会导致错误。
并且严格模式下,不能为函数的caller属性赋值。
表示函数希望接受的命名参数的个数。
fucntion sayName(name){
alert(name);
}
function sum(num1,num2){
return num1+num2;
}
function sayHi(){
alert("hi");
}
sayName.length; //1
sum.length; //2
sayHi.length; //0
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
function sum(num1,num2){
return num1+num2;
}
function callSum1(num1,num2){
return sum.apply(this,arguments); //传入arguments对象
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2]); //传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
上面的代码中,callSum1在执行sum函数时传入了this(因为是在全局作用域中调用的,所以传入的就是window对象)。
function sum(num1,num2){
return num1+num2;
}
function callSum(num1,num2){
return sum.call(this,num1,num2);
}
call方法和apply方法的结果并没有什么不同,决定使用哪个完全取决于采取哪种给函数传递参数的方式最方便。如果打算直接传入arguments对象或者包含函数中先接收到的也是一个数组,那么使用apply方法肯定更方便,否则可能就会选择call方法。
call和apply最大的用武之地是能够扩充函数赖以运行的作用域:
window.color = "red";
var o = {color:"blue"};
fucntion sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
在调用sayColor.call(o)时函数的执行环境改变了,函数内部的this对象指向了o。
使用apply和call来扩充作用域的最大好处是,对象不需要与方法有任何耦合关系。
这个方法会创建一个函数的实例,其this值会被绑定到传给bind函数的值:
window.color = "red";
var o = {color:"blue"};
fucntion sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
这里,sayColor调用bind并传入o,创建了objectSayColor 函数,objectSayColor 函数的this值等于o,因此即使在全局作用域中调用这个函数,也会看到blue
兼容到ie9,polyfill:
Function.prototype.bind = function(context){
var args = [].slice.apply(arguments,1);
var func = this;
return function(){
return func.apply(context,args.concat([].slice.apply(arguments)));
}
}
toLocaleString、toString和valueOf都返回函数的代码。