首先,说一个很重要的概念性的东西:“函数是对象,函数名是指针”,这个概念能让我们很清晰的了解到函数是一个什么东西。
每个函数都是Function类型的实例,具有和其他引用类型一样的属性和方法;
定义函数的方法有两种:
一、函数声明语法定义:
function sum(num1,num2){
return num1 + num2 ;
};
二、函数表达式:
var sum = new Function("num1", "num2", "return num1 + num2"); //不推荐这种写法,因为这样会导致代码被解析两次;
这两种定义函数的方式有什么不一样呢:
解析器会先读取函数声明,并且让他可以在执行任何代码之前可以访问,而函数表达式则是必须要等到解析器执行到它所在的代码行,很会被解析执行。
关于‘函数名只是一个指针’:
就跟对象一样,当你用另外一个变量名去存储一个函数时,只是存储这个函数的引用,因此一个函数可以有多个名字。
例如:
function sum(num1,num2){
return num1 + num2;
};
alert sum(10.10); //20
var sum2 = sum;
alert sum2(10,10); //20
sum = null;
alert sum2(10,10) //20
当sum = null;时,sum就已经和函数没有任何关系了,但是还是可以继续执行sum2();因为即使修改sum,你只是修改他的引用而已,跟函数本身没有任何关系。
这样就可以很好地理解函数为什么没有重载了,因为声明两个同名函数时,后面的函数会覆盖前面的函数,就是覆盖了引用第一个函数的变量;
例如:
var sum3 =function (num) {
return num + 100;
};
sum3 = function (num) {
return num +200;
};
var total = sum3(100); //300
在创建第二个函数时,实际上覆盖了引用第一个函数的变量sum3;
还有很重要的一点是,函数也是可以作为值来使用的,甚至可以作为参数传入另外一个函数;
函数的内部特殊对象有两个:arguments和this;
arguments是函数的参数,this是函数执行的环境对象;
callee属性,这个属性是一个指针,指向的是拥有这个arguments对象的函数。
caller属性,这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,他的值为null;
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);
}
outer(); //outer
在这个函数中arguments.callee会返回inner()
所以inner.caller就会返回outer;
函数属性有两个:length,prototype
length属性表示函数接收的命名参数的个数;
prototype属性是保存引用类型所有实例方法的所在(关于继承以后在写);
函数的方法有两个:call(),apply()
需要传入两个参数:运行函数的作用域,参数数组;
用途就是在特定的作用域中调用函数,所以他们真正强大的地方就是能够扩充函数赖以运行的作用域;
区别:就是传递参数的方式,如果你打算直接传入arguments对象,或者一个数组,那么使用apply()是更方便的,其他的请使用call();
改变作用域:
window.color= "red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(o); //blue
sayColor.call(window); //red