一、理解函数的参数
JS中的函数对于传递进来的参数很宽容,假设你定义的某个函数需要传递一个参数,但是你传递的时候却传递了两个,或者是没传,即使这样,解析器也不会报错。
之所以JS中的函数这么宽容,是因为函数在定义成功后,里面就生成了自动生成了一个对象:arguments。这个对象他长得有点像数组,只是像而已哦,他可以通过方括号语法访问里面的每一个元素,并用length属性判断里面到底传了几个元素。
arguments这个对象保存的是传给这个函数的所有参数。为什么说他长得像数组呢,是因为访问这个对象内的属性(也就是传来的参数),是通过arguments[index]来访问的,方括号里面装的是index序列。所以第一个参数放在索引0处,使用arguments[0]可以引用他。
let test=function(no1,no2){
console.log(typeof arguments[0]);
console.log(arguments[1]);
}
var arr=[1,2,3];
var str="for-fun";
test(arr,str)//object for-fun
-
arguments的意义在于他可以模仿重载。重(zhong,四声)载的意思就是一个函数,传递不同的参数,可以执行不同的方法。而JS是没有重载的。
二、函数对象
函数实际上是对象,每个函数都是Function类型的实例。既然函数式对象,那么函数名实际上就是一个指向函数对象的指针,不会与某个函数绑定,一句话概括:“函数是对象,函数名是指针”。也正因此,一个函数,可以有多个不同的函数名。
例如:
function sum(num1,num2){ return(num1+num2); } var add=sum; console.log(add(19,2),typeof add);//21,function
-
一个函数可以有多个函数名,但是一个指针却不能指向多个函数,这也就是为什么JS没有重载的原因了。
三、函数的内部属性对象this
this引用的函数执行环境的对象,当在网页全局作用域中调用函数时,this对象引用的是window。查看下面代码:
var color="red"; var o={ color:"blue" } function sayColor(){ console.log(this.color); } sayColor();//undefined,这个this指向的是window o.sayColor=sayColor;//必须让这个对象绑定该方法 o.sayColor();//blue
由于函数的名字只是一个包含指针的变量,因此即使在不同的环境中执行函数,全局的sayColor()函数与o.sayColor()函数指向的仍然是同一个函数。
四、函数的属性
由于函数是对象,所以函数必然有它的属性与方法。
函数的属性有两个,length和prototype。length是函数希望接受到的参数的个数,prototype是在创建自定义引用类型和实现继承的时候使用的,不多赘述。
function sum(num1,num2){ return(num1+num2); } console.log(sum.length);//2
五、函数的方法
每个函数都包括两个非继承而来的方法:apply()和call()。这两个方法的用途是在特定的作用域中调用函数,就相当于设置函数体内this的值。
apply():
apply()接受两个参数,一个是作用域,一个是数组。前者是在其中运行函数的作用域,后者是参数数组。
后者这个数组,可以是Array实例,也可以是arguments对象。
function sum(num1,num2){ console.log(num1+num2); } function callSum(num1,num2){ return sum.apply(this,arguments); } callSum(1,5,3)//6
-
call():
call()接受两个参数,第一个是作用域,后面是其他传递进来的参数。与apply方法不同的是,在使用call方法的时候,传递给函数的参数必须逐一列举。
function sum(num1,num2){ return(num1+num2); } function callSum(num1,num2){ return sum.call(this,num1,num2); } console.log(callSum(3,6));//9
-
当然, 传递参数并不是call()和apply()的主要功能,他们最主要的作用还是扩充作用域。
global.color="red"; var obj={ color:"blue", } function sayColor(){ console.log(this.color); } sayColor();//red sayColor.call(obj);//blue
上面这段代码第二个sayColor,由于函数的执行对象变了,所以this就指向了obj。使用call和apply扩展作用域最好的优势,在于对象与方法不需要有任何的耦合关系。像下面这段之前的代码,先把方法传入到对象中,然后再用对象来调用方法,这就将方法与对象紧紧地耦合在了一起。
global.color="red"; var obj={ color:"blue", } function sayColor(){ console.log(this.color); } sayColor();//red obj.sayColor=sayColor; obj.sayColor();//blue
六、bind()
ES5还定义了一个bind()方法,这个方法会创建一个函数的实例,this值会被绑定给传给bind()函数的值。
global.color="red"; var obj={ color:"blue", } function sayColor(){ console.log(this.color); } var objSaycolor=sayColor.bind(obj); objSaycolor();//blue
上面这段代码中,sayColor方法调用bind()传入了对象obj,创建了objSayColor函数,因此该函数的this指向obj。
七、函数绑定
var handle={ message:"this is handle", handleClick:function(){ console.log(this.message); } } var btn=document.getElementById("my-btn"); EventUtil.addHandler(btn,"click",handle.handleClick);
上面这段代码,虽然看上去好像是点击按钮会输出“this is handle”,然而实际上,却是会输出“undefined”,这是因为this指向的是DOM按钮,而不是handle对象。
bind()方法接受一个函数和一个环境,返回一个在给定环境中调用给定函数的函数,并将所有参数原封不动传递过去。
var handle={ message:"this is handle", handleClick:function(event){ console.log(this.message); } } var btn=document.getElementById("my-btn"); EventUtil.addHandler(btn,"click",bind(handle.handleClick,handle));
上面这段代码中,用bind()函数创建了一个保持了执行环境的函数,并将其传递给了EventUtil.addHandler(),event对象也被传递给了该函数。
七、原生bind()
ES5为所有的函数定义了一个原生bind()方法,因此可以直接在函数上调用这个方法。就像上面“六bind()”里的sayColor一样。
var handle={ message:"this is handle", handleClick:function(){ console.log(this.message); } } var btn=document.getElementById("my-btn"); EventUtil.addHandler(btn,"click",handle.handleClick.bind(handle));
如果某个函数指针是以“值”的方式进行传递,同时该函数必须在特定的环境中执行,函数绑定的作用就凸显出来了。他们主要用于事件处理程序已经setTimeout()和setInterval()。
-
扩展:
this的指向是谁调用,就指向谁。对象里面的函数,要想拿出来使用,就必须要重新定义。
堆:先进先出。字符串、数字
栈:先进后出。数组、对象。
函数是对象,函数名是指针。