JavaScript
七、函数表达式
- 函数声明
- 函数声明提升
- 全局范围可访问
- 函数表达式
- 按顺序执行,不会进行提升
- 可以是命名的,也可以是匿名的
- 命名
- var factorial=function f(){};
- 匿名
- 又称为Lamada函数
- var factorial=function (){};
- 函数表达式是匿名的
- 将匿名函数赋值给一个变量
- 命名
7.1 递归
- 命名的函数表达式
- 函数表达式有个名称
- ECMAScript 5严格模式下arguments.callee属性无法访问
var factorial=(function f(num){
if(num<=1){
return 1;
}else{
return num*f(num-1);
}
});
7.2 闭包
有权访问另一个函数作用域(一般是外部函数的作用域)中的变量的函数
- 外部函数将内部函数作为对象返回,内部函数拥有外部函数的活动对象
- 活动对象
未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。所以活动对象实际就是变量对象在真正执行时的另一种形式
- 活动对象
- 外部函数的作用域链在外部函数执行完毕后销毁,但其变量对象被闭包所引用,所以还会一直保存在内存中直至闭包不再引用
7.2.1 闭包与变量
function createFunction(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(){
return i;
};
}
//函数数组,函数在执行时先访问本身的函数作用域
//再访问外部函数的变量对象,该对象是同一份
return result;
}
- 闭包拥有外部函数的活动对象,而活动对象是引用类型,随着外部函数的执行会发生变化,而闭包引用的是最终的活动对象
function createFunction(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(num){
return function(){
return num;
};
}(i);
}
return result;
}
- 声明函数后立即执行,并返回执行结果,而执行结果是一个访问外部函数num变量的闭包
- 外部函数每次会重新声明并创建,所以闭包中引用的外部函数的变量对象不是同一份
7.2.2 关于this对象
- 每个函数被调用时,其活动对象会自动获得两个特殊变量,this和arguments
- 函数在搜索这两个变量时,只会在活动对象中搜索
var name="The Window";
var object={
name:"My Object",
getNameFunc:function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());//"The Window"
- object.getNameFunc()得到的是匿名函数
- 匿名函数调用时,执行环境是全局的,其活动对象为全局变量对象
var name="The Window";
var object={
name:"My Object",
getNameFunc:function(){
var that=this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());//"My Object"
- object.getNameFunc()执行并返回匿名函数
- 执行过程中,其作用域为object,所以this指向object
- 匿名函数内部会返回that对象的name属性,由于是that而不是特殊的this,所以会搜索外部函数的变量对象
var name="The Window";
var object={
name:"My Object",
getNameFunc:function(){
return this.name;
}
};
object.getNameFunc();//"My Object"
(object.getNameFunc)();//"My Object"
//函数表达式的值是函数本身
(object.getNameFunc=object.getNameFunc)();//"The Window"
7.2.3 内存泄漏
- 循环引用
- 闭包引用外部函数的变量对象,而外部函数的变量对象会引用闭包
function assignHandler(){
var element= doucment.getElementById("someElement");
element.onclick=function(){
alert(element.id);
};
}
- 消除循环引用
- 值复制,使得闭包不直接引用element
- 但闭包引用的是外部函数的整个活动对象
- 消除外部函数中的变量对闭包的引用
- 值复制,使得闭包不直接引用element
function assignHandler(){
var element= doucment.getElementById("someElement");
var id=element.id
element.onclick=function(){
alert(id);
};
element=null;
}
7.3 模仿块级作用域
(function (){
//块级作用域
})();
- 使用括号包裹函数声明,则会将函数声明看作是函数表达式
- 不加括号,function会被看作是函数声明的开始
- 函数声明后面无法加括号直接进行调用
- 函数表达式立即调用,执行后立即销毁,模仿块级作用域
7.4 私有变量
- 私有变量
所有对象的属性都是公有的,只有函数中的参数、局部变量和内部定义的其他函数只能在函数内部访问 - 特权方法
闭包能访问外部函数的变量对象,若将闭包公开则外部就可以通过闭包访问函数内部的私有变量 - 闭包公开的方式之一
- 作为构造函数的实例属性
- 闭包(特权方法)不能复用
- 私有变量是构造函数的局部变量
- 实例属性
- 作为构造函数的实例属性
function Person(name){
this.getName=function(){
return name;
};
this.setName=function(value){
name=value;
};
}
var person=new Person("Nicholas");
alert(person.getName()); //"Nicholas"
7.4.1 静态私有变量
- 闭包公开的方式之二
- 作为构造函数的原型属性,而构造函数创建出全局对象
- 私有变量是模仿块级作用域中的局部变量
- 变量要私有,则不能作为构造函数的属性
- 私有变量属于块级作用域,存在于变量对象中,由所有实例共享,相当于静态变量
- 虽然块级作用域执行完毕后会销毁,但由于闭包的引用,所以变量对象还保留
- 构造方法是未声明直接初始化的
为声明直接初始化的变量在非严格模式下会被当成全局变量,所以在外界能访问构造函数
(function (){
var name="";
Person=function(value){
name=value;
};
Person.prototype.getName=function(){
return name;
};
Person.prototype.setName=function(value){
name=value;
};
})();
var person=new Person("Nicholas");
alert(person.getName()); //"Nicholas"
7.4.2 模块模式
- 以上创建私有变量的方式是针对自定义类型的
- 模块模式针对的是单例对象,不需要识别类型
- 单例对象
以对象字面量的方式创建 - 闭包公开的方式之二
- 作为函数返回值将闭包(特权方法)公开
- 函数返回值是以对象字面量创建的,为Object类型
- 私有变量是模仿块级作用域中的局部变量
- 由于是单例对象,所以也不存在静态变量还是实例属性的区别
- 作为函数返回值将闭包(特权方法)公开
var singleton=function(){
var privateVariable=10;
function privateFunction(){
return false;
}
return {
publicProperty:true,
publicMethod:function(){
privateVariable++;
return privateFunction();
}
};
};
7.4.3 增强的模块模式
- 单例必须是某种类型,可使用增强的模块模式
- 函数返回值是具体类型的对象
var singleton=function(){
var privateVariable=10;
function privateFunction(){
return false;
}
var object=new CustomType();
object.publicProperty=true,
object.publicMethod=function(){
privateVariable++;
return privateFunction();
};
return object;
};