函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
一、定义函数的方法:
1.函数声明
function sum(num1,num2){}
2.函数表达式
var sum=function(num1,num2){}
使用函数表达式定义时,没有必要使用函数名,但也可以是var sum=function ab(num1,num2){}
使用不带圆括号的函数名是访问函数指针,而非调用函数。
3.区别
解析器会率先读取函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
二、函数没有重载
JS中函数没有重载,在创建第二个函数时,实际上覆盖了第一个函数。
三、作为值的函数
函数本身就是变量,所以函数也可以作为值来使用。可以像传递参数一样把一个函数传递给另一个函数,而且也可以将一个函数作为另一个函数的结果返回。
四、函数内部属性
1.arguments
在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。arguments对象和数组类似,因此可以通过方括号访问,length确定传递进来的参数个数。arguments[0]
另外arguments的值永远与对应命名参数的值保持同步。
function doAdd(num1,num2){
arguments[1]=10;
alert(arguments[0]+num2);//num2每次都被重写为10
}
这个对象还有一个属性callee,该属性是一个指针,指向拥有这个arguments对象的函数。
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
2.另一个函数对象的属性caller
这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。
function outer(){
inner();
}
function inner(){
alert(inner.caller);//弹出调用inner的函数的引用,也就是outer,因此会输出function outer(){inner();}
}
为了实现更松的耦合,可以这样调用
function inner(){
alert(arguments.callee.caller);
}
五、函数的属性和方法
每个函数都包含两个属性length和prototype; length属性表示函数希望接受的命名参数的个数。
function sayName(num1,num2){}
alert(sayName.length)//2
prototype是保存函数所有实例方法的真正所在。不可枚举,无法使用for-in发现。
每个函数都包含两个非继承而来的方法:apply()和call()
用途:在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
扩充函数赖以运行的作用域。
1.apply()
接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。第二个参数可以是Array的实例,也可以是arguments对象。
2.call()
作用同apply,区别仅在于接收参数的方式不同。第一个参数相同,第二个参数是直接传递给函数,也就是说传递给函数的参数必须逐个列举出来。
3.bind()
其this值会被指定给传给bind()函数的值。
window.color='red'; var o={color:'blue'}; var b={color:'yellow'}; function sayColor(){ alert(this.color); } var osaycolor=sayColor.bind(o); var bsaycolor=sayColor.bind(b); osaycolor()//'blue' bsaycolor()//'yellow'
4.toLocalString() toString() valueOf()
始终返回函数的代码。
六、理解apply() call()
function cat(){ } cat.prototype={ food:"fish", say: function(){ alert("I love "+this.food); } } var newCat = new cat(); newCat.say();
但是如果我们有一个对象newDog = {food:"bone"},我们不想对它重新定义say方法,那么我们可以通过call或apply用newCat的say方法:
所以,可以看出call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。
用的比较多的,通过document.getElementsByTagName选择的dom 节点是一种类似array的array。它不能应用Array下的push,pop等方法。我们可以通过:
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
这样domNodes就可以应用Array下的所有方法了。
call和apply可以用来重新定义函数的执行环境,也就是this的指向。通过一个操作DOM的例子来理解。
function changeStyle(attr, value){ this.style[attr] = value; } var box = document.getElementById('box'); window.changeStyle.call(box, "height", "200px");
call中的第一个参数用于指定将要调用此函数的对象,在这里,changeStyle函数将被box对象调用,this指向了box对象,如果不用call的话,程序报错,因为window对象中没有style属性。
apply的用法:
window.changeStyle.apply(box, ['height', '200px']);
function add(a,b){ alert(a+b); } function sub(a,b){ alert(a-b); } add.call(sub,3,1); // 4
2.
function Animal(){ this.name = "Animal"; this.showName = function(){ alert(this.name); } } function Cat(){ this.name = "Cat"; } var animal = new Animal(); var cat = new Cat();
//通过call或apply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。
animal.showName.call(cat,","); //Cat
//animal.showName.apply(cat,[]); //Cat
3.实现继承
function Animal(name){ this.name = name; this.showName = function(){ alert(this.name); } this.hi=function(){// 注意这里必须是this引用才能继承,否则cat.hi()时,会报错,hi不是一个函数 alert("hello "+this.name); } } function Cat(name){ Animal.call(this, name); } var cat = new Cat("Black Cat"); cat.showName();//Black Cat cat.hi();//hello Black Cat
4.多重继承
function Class10(){ this.showSub = function(a,b){ alert(a-b); } } function Class11(){ this.showAdd = function(a,b){ alert(a+b); } } function Class2(){ Class10.call(this); Class11.call(this); } var c2=new Class2(); c2.showSub(10,3)//7 c2.showAdd(10,3)//13