函数表达式
递归
在函数中调用身,但是要有调用终止的判断,否则会无限调用,消耗内存空间
使用arguments.callee,指向正在执行的函数的指针,用来实现函数的递归调用
function factorial(num){
if(num <= 1){
return 1
}else{
return num*arguments.callee(num-1)
}
}
闭包
闭包是指有权访问另一个函数作用域中的变量的函数,因此可以通过在一个函数内部创建另一个函数,来创建闭包。
当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性。然后使用this、arguments和其他明明参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位等。
后台每个执行环境都有一个变量对象。全局环境的变量对象始终存在,局部环境的指针函数执行过程中存在。创建函数时会创建一个预先包含全局变量对象的作用域链,被保存在内部的[[Scope]]属性中。调用函数时·,会创建一个执行环境,通过对[[Scope]]属性中的对象进行赋值构建起环境的作用域链。此后,一个活动对象被创建呗推入执行换进作用域链的前端。并且,作用域链本质上是一个指向变量对象的指针列表,只引用但不包含变量对象。
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量,而且一般在函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。
但是在闭包中,在另一个函数内部定义的函数将包含函数(外部函数)的活动对象添加到它的作用域链中。所有在内部的匿名函数的作用域链中,实际上将会包含外部函数的活动对象。
当匿名函数被外部函数返回后,它的作用域链别初始化为包含外部函数的活动对象和全局变量对象。所有匿名函数就可以访问外部函数的变量,外部函数再执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用同这个活动对象,会被保存在内存中,直到匿名函数被销毁。
作用域链会有一个问题就是,闭包只能取得包含函数中任何变量的最后一个值。因此需要返回每一个变量的值,需要使用一个局部变量对变量进行保存其副本。
在闭包中使用this对象:this对象时在运行时基于函数的执行环境绑定的,而且匿名函数执行环境具有全局性,其this对象通常指向wiindow。每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索其活动对象为止,因此永远不可能直接访问外部函数中的两个变量。但是可以把外部作用域中的this对象保存在一个闭包能访问的变量里,就可以让闭包访问该对象。因此可以在匿名函数之前,把this保存在一个变量里,再在匿名函数中调用。
内存泄漏:如果闭包的作用域链保存一个HTML元素,如果不对该元素消除处理,该元素将无法被销毁。因为如果匿名函数保存了对外部函数的活动对象引用,如果匿名函数不被消除,对元素的引用数就不会为零,因此占用的内存永远不会被释放。所以在匿名函数调用完之后,应该把该元素设置为null,释放其在被匿名函数引用的包含函数的活动对象占有的内存。
类似块级作用域
js中是没有块级作用域这一概念的。因此在块语句中定义的变量,是在包含函数中而不是语句中创建的。例如在一个函数的for循环中,变量i在js中,是定义在包含函数的活动对象中。而java等语言中,变量i是定义在for循环的语句块中。
JavaScript中对后续声明的变量视而不见。因此可以使用匿名函数来模仿块级作用域解决这个问题,实现一个私有作用域,从而限制向全局作用域添加过多变量和函数。
(function(){
})()
第一个括号表示一个函数表达式,最后一个括号调用这个函数
私有变量
任何函数中定义的变量都可以认为是私有变量。可以通过在这个函数内部创建一个闭包,闭包通过自己的作用域链访问这些变量,从而可以创建访问私有变量的共有方法
因此为了访问私有变量,我们可以创建一个特权方法。
1、在构造函数中创建,对于不想开放的私有变量,可以通过自己规定的方法进行获取和修改
function MyObject(){
var privateItem = 'private';
function privateFun(){
return false
}
// 通过特权方法获得
this.getPrivate = function(){
alert(privateItem)
return privateFun()
};
// 通过特权方法修改
this.setPrivate = function(arg){
privateItem+=arg
}
}
2、使用静态私有变量可以避免使用构造函数创建私有变量带来的缺点:每个实例都会创建同样一组新方法,导致方法的冗余。
(function(){
var age = 1;
Person = function(arg){
age = arg
}
// 获得私有变量
Person.prototype.getAge = function(){
return age
}
// 修改私有变量
Person.prototype.setAge = function(arg){
age = arg;
}
})()
共有方法在原型上定义,保证实例都是用同一个函数。而且在定义构造函数没有使用函数声明,只是使用函数表达式,并且没有使用var关键字。因为初始化未经声明的变量,总是会创建一个全局变量,所有这里的Person就变成一个全局变量,但是这种做法只适用于在非严格模式下。
3、模块模式
该模式主要是为单例创建私有变量和特权方法
一般使用一个对象字面量作为函数的值返回,这个对象字面量只包含公开的方法,可以使用公开的方法对私有变量进行访问操作
var modle = function(){
// 私有变量
var age = 1;
function addAge(arg){
age+=arg
}
return {
getAge: function(){
return age
},
setAge: function(arg){
return addAge(arg)
}
}
}