一,了解函数
1,函数实际是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法,由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
2,函数没有重载,当声明了两个同名函数时结果会是后面的函数覆盖前面的函数。
二,函数的内部属性
在函数内部,有两个特殊的对象:arguments和this
函数内部的arguments对象:
1,arguments是一个类数组对象,包含着传入函数中的所有参数,它的主要作用是保存函数参数。
2,arguments的callee属性:该属性是一个指针,指向拥有这个arguments对象的函数(即arguments是哪个函数的,这个属性就指向谁)
看下面的阶乘函数用到的递归算法,理解callee属性的作用
function calleeTest(num){
if(num <= 1) {
return 1;
}else{
return num * calleeTest(num-1);
}
}
var truecalleeTest = calleeTest;
calleeTest = function(){
return 0;
};
console.log(truecalleeTest(5));//0
/*这样定义是没有问题的,但是函数的执行与函数名紧紧的耦合在了一起,将函数名修改为truecalleeTest以后,函数体内的calleeTest名字没有被改变,所以会执行后面返回零的函数*/
function calleeTest(){
if(num <= 1){
return 1;
}else{
return num * arguments.callee(num - 1);
}
}
var truecalleeTest = calleeTest;
calleeTest = function(){
return 0;
};
console.log(truecalleeTest(5));//120
/*在这次的函数体内就没有再使用函数名calleeTest,无论引用函数时使用什么函数名,都可以保证正常的递归调用*/
函数内部的this对象:
1,this引用的是函数执行的环境对象,也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的是window。
以下代码中this就指向了不同对象
window.color = "yellow";
var obj = {
color:"red"
};
function showColor(){
alert(this.color);
}
showColor();//yellow
/*showColor函数是在全局作用域中定义的,它引用了this对象,由于在调用之前this的值并不确定,因此this在代码执行过程中可能会引用不同对象。当在全局作用域中调用showColor()时 this引用的是全局对象window。即this.color求值转化成window.color求值*/
obj.addColor = showColor;
obj.addColor();//red
/*当把这个函数赋给对象obj的addColor属性时并调用obj.addColor()时,this引用的
对象是obj。即this.color求值转化成obj.color求值*/
注意:函数的名字仅仅是一个包含指针的变量而已,因此在上面的代码中即使是在不同的环境中执行,但是在代码obj.addColor = showColor之后,代表着全局的showColor()函数和obj.addColor()指向的仍然是同一个函数
函数对象的caller属性:
该属性中保存着调用当前函数的函数的引用,若是在全局作用域中调用当前函数,它的值为null。例如:
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer();
上面代码中outer函数引用了inner函数,inner函数里面的inner.caller保存着调用inner函数的outer函数的引用,即指向的的是outer函数。所以结果是会出现如下的显示outer函数内容的警示框
当然为了实现更松散的耦合,可以使用arguments.callee.caller来访问相同的信息,代码如下:
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);//arguments.callee指向arguments所属函数inner,即还是inner.caller
}
outer();
三,函数属性
————因为函数是对象,所以函数也有属性和方法,每个函数都包含两个属性:length和prototype。
1,length属性:
表示函数希望接收的命名参数的个数,如下:
function funA(name){
alert(name);
}
function funB(num1,num2){
alert(num1 + num2);
}
function funC(){
alert("hello");
}
console.log(funA.length);//1
console.log(funB.length);//2
console.log(funC.length);//0
2,prototype属性:
对于ECMAScript中的引用类型而言,prototype是保存它们所有实列方法的真正所在,诸如toString()和valueOf()等方法实际上都是保存在prototype名下,只不过是通过各自对象的实例进行访问。
五,函数方法
————每个函数都包含了两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象值
1,apply方法:
接收连个参数:在其中运行函数的作用域,参数数组。其中第二个参数可以是Array的实例,也可以是arguments对象。如下:
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]);//传入数组
}
console.log(callSum1(10,20));//30
console.log(callSum2(10,20));//30
上面代码中apply方法的第一个参数传入this作为this值,又因为是在全局作用域中调用的,所以传入的就是window对象。所以将函数sum里面的this指向了window
2,call()方法:
第一个参数还是this值,其余参数都只能直接的一个一个传递给参数,即逐个列举出来。如下:
function sum(num1,num2){
return num1 + num2;
}
function callSum(num1,num2){
return sum.call(this,num1,num2);//此处是逐个传递参数num1,num2
}
console.log(callSum(10,20));//30
3,以下讨论的是这两个方法的重点,apply()和call()方法真正强大的地方是扩大函数赖以运行的作用域
window.name = "I am the window";
var obj = {
name:"I am the obj"
};
function myAge(){
alert(this.name);
}
myAge();//I am the window;执行函数myAge()
myAge.apply(this);//I am the window;利用apply()将myAge函数里面的this指向了this对象,而在全局环境下this默认指向window
myAge.apply(window);//I am the window;利用apply()函数将myAge函数里面的this指向了window对象
myAge.apply(obj);//I am the obj;利用apply()函数将myAge函数里面的this指向了obj
3,ECMAScript5还定义了一个函数的方法:bind()方法:
该方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。如下:
window.food = "vegetables";
var eatFood = {
food:"meat",
eat:function(){
alert(this.food)
}
};
function showFood(){
alert(this.food);
}
var food = showFood.bind(eatFood);//作用和前面讲的方法一样,将showFood函数里面的this值指向了eatFood对象
food();//meat