函数是一段只定义一次,可能被执行或调用任意次的JavaScript代码。
JavaScript函数调用会为形参提供实参的值,每次调用还会拥有本次调用的上下文this。
若一个函数挂载在一个对象上,作为对象的属性,称为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文。
JavaScript中函数即对象,程序可以任意操控。
JavaScript的函数可以嵌套在其他函数中定义,这样嵌套的函数可以访问外层函数作用域中的任何变量。这意味着JavaScript函数构成一个闭包。
函数调用
(1) 函数调用
若函数返回是因为解释器到达结尾,则返回值是undefined。若return语句没有值,也是返回undefined。
以函数方式调用通常不能使用this关键字。
(2) 方法调用
函数作为对象的属性,然后通过对象来调用。
this代表当前对象。this是一个关键字,不允许对this进行赋值。
方法链:当方法返回值是调用对象本身即this,这个时候可以再调用它的方法。
关键字this没有作用域的限制,嵌套函数不会从调用它的函数中继承this。
若嵌套函数作为方法调用,this指向调用它的对象。若嵌套函数作为函数调用,其this值是全局对象(非严格模式),或undefined(严格模式下)。
(3) 构造函数调用
如果函数调用之前带有new关键字就是构造函数调用。
凡是没有形参的构造函数调用括号可以省略。
构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。
构造函数试图初始化这个新创建的对象,并将这个对象作为调用上下文this。即new o.m()中,调用上下午并非o。
构造函数通常不使用return,当构造函数执行完毕,会显示返回初始化的新对象。
若显示return返回一个对象则构造函数返回就是这个值。若没有指定返回值或返回原始值,则返回值被忽略,依旧返回新构造的对象。
(4) 间接调用
JavaScript中函数也是对象,函数对象中包含两个间接调用的方法call()和apply(),两个方法需要显示的指定this值。
函数的形参和实参
JavaScript函数定义未指定函数形参的类型,函数调用也未对传入的实参做任何类型检查,甚至不检查传入参数的个数。
可选形参
当调用函数的时候传入的实参比函数声明时指定的形参个数要少时,剩下的形参都将设置为undefined值。
可变长实参
当调用函数传入的实参个数超过形参个数时,没办法直接获得未命名的引用。在函数内,标示符arguments是指向实参对象的引用,其是一个类数组对象。
函数可以接受任意个数的实参,这种函数称为不定实参函数,arguments[]适合实现这种场景的函数。
arguments并不是真正的数组,是一个实参对象,修改其值可以相应修改传入的实参。
calllee和caller属性
arguments对象除了数组元素,还定义了callee和caller属性,在ES5严格模式下访问这两个属性会产生错误。
callee属性指代当前正在执行的函数。
caller属性非标准,大部分浏览器实现这个属性,指代调用当前正在执行的函数的函数,其可以访问调用栈。
闭包
函数对象可以通过作用链相互关联,函数体内部的变量都可以保存在函数作用域内,这种特性称为闭包。
闭包可以捕捉到局部变量(和参数),并一直保存下来,看起来像这些外部变量绑定到了其中定义的外部函数。
作用域链
在JavaScript最顶层代码,作用域链由一个全局对象组成。
在不包含嵌套函数体内,作用域上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。
当定义一个函数时,它实际上至少保存一个作用域链。
当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的作用域链。
当函数返回的时候,就从作用域链中将这个绑定的变量对象删除。若不存在嵌套函数,也没有其他引用指向这个绑定的对象,则会把垃圾回收。
对于嵌套函数,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域都不同的。
内部函数在每次定义的时候都有微小的差别,在每次调用外部函数时,内部函数代码相同,而关联这段代码的作用域链不相同。
嵌套函数不会将作用域内私有成员复制一份,也不会对所绑定的变量生成静态快照。
var counter(){
var n=0;
return {
count: function(){ return n++;},
reset: function(){ n=0;}
};
}
//每次调用counter都会创建一个新作用域链和一个新的私有变量
var c=counter(),d=counter();
c.count(); //-->1
d.count();//-->1
c.reset();//-->0
d.count();//-->2
函数属性、方法和构造函数
length属性:代表形参的长度,只读属性,arguments.length代表的是实参长度prototype属性:每个函数都有一个prototype属性,当把函数当做构造函数时,新创建的对象会从原型对象上继承属性。
call()和apply()方法:第一个参数是要调用函数的母对象,即调用上下文
ES5中第一个实参都会变成this的值,哪怕传入null和undefined。ES3和非严格模式中,传入null和undefined会被全局对象替换。
call()方法后面接函数传入的参数,aplly()方法第二个参数是一个数组来接收实参。
bind()方法:ES5中新增加方法,主要作用是将函数绑定至某个对象。当函数f()上调用bind()方法传入对象o时,这个方法将返回一个新的函数,调用新的函数时会当做o的方法进行调用。
Function()构造函数
Function()构造函数可以传入任意数量的字符串实参,最后一个实参所表示的文本就是函数体。
Function()构造函数运行JavaScript在运行时动态的创建并编译函数
每次调用Function()构造函数都会解析函数体,并创建新的函数对象。若在循环中创建效率低下。
Function()所创建的函数并不使用词法作用域,相反,函数体代码的编译总是会在顶层函数执行。
可调用对象
函数是可调用对象。RegExp对象。判断一个对象是否为函数和isArray类似。
function isFunction(x){
return Object.prototype.toString.call(x) === "[object Function]";
}