函数声明提升:执行代码之前会先读取函数声明(但是使用函数表达式命名定义函数时,使用前必须赋值)
sayhi(); //错误,函数还未赋值
var sayhi = function(){
alert("hi");
};
闭包
是指有权访问另一个函数作用域中的变量的函数
当 创建函数时,会创建一个预先包含全局变量对象的作用域链。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象,这个作用域链被保存在内部的Scope
属性中。
全局环境的变量对象始终存在,而像函数这样的局部环境的变量对象,则只会在函数执行过程中存在。
当
调用函数时,会为函数创建一个执行环境,通过复制函数的
Scope
属性,构建起执行环境的作用域链,接着创建活动变量对象并推入执行环境作用域链的前端。
function makeFunc() {
var name = "Mozilla";
function displayName() { //内部函数
alert(name);
}
return displayName;
}
在另一个函数的内部定义的函数会将外部函数的活动对象添加到它的作用域链中。即使外部函数执行完毕,其也只会销毁外部函数的作用域链,而不会销毁外部函数的变量对象,因为其变量对象仍然被内部函数引用。只有当内部函数也被销毁时,外部函数的变量对象才会被销毁。
因此内部函数可以访问到外部函数中的变量,这就形成了闭包
var myFunc = makeFunc(); //调用外部函数,返回内部函数
myFunc(); //"Mozilla" 虽然外部函数已经执行完毕,但仍然可以访问到外部函数中定义的变量
闭包只能取得外部函数中任何变量的最后一个值(最终值)
function createFunctions(){
var result = new Array();
for (var i=0; i<10; i++){
result[i] = function(){
return i;
};
}
return result;
}
- 上述返回一个数组,数组的每一项都是一个函数,调用每个函数,似乎应该返回自己的索引值,但其实每个函数返回值都是10。因为当外部函数执行完毕后,其变量对象中的 i 已经变为了10,从而此时闭包访问的 i 值是10
function createFunctions(){
var result = new Array();
for (var i=0; i<10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
- 上述代码,并没有把闭包直接赋予给数组,而是定义了一个匿名函数,立即执行该函数,将结果传入数组,由于参数都是按值传递的,因此最后每个函数都会返回对应的索引值
关于this
对象
匿名函数的执行环境具有全局性,其this对象通常指向window,但由于闭包,可以会不太明显
var name = "The Window";
var object = {
name : "My object",
getName : function(){
return function(){
return this.name;
};
};
};
alert(object.getNameFunc()()); //"The Window"
- 每个函数在被调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止。由于匿名函数执行环境具有全局性,因此永远也不会直接访问到外部函数中的变量。
var name = "The Window";
var object = {
name : "My object",
getName : function(){
var that = this;
return function(){
return that.name;
};
};
};
alert(object.getNameFunc()()); //"My object"
- 上述在定义匿名函数之前,将this对象复制给that,然后在闭包中访问该变量
模仿块级作用域
块级作用域(私有作用域)保证在运行结束后,其中定义的变量将会被销魂,避免内存泄漏和全局作用域中过多的变量
(function(){
//这里是块级作用域
})();
私有变量
任何在函数中定义的变量,包括函数的参数、局部变量和在函数内部定义的其他函数叫做私有变量。利用闭包可以访问到这些私有变量
在构造函数中定义私有变量
function Person(name){
this.getName = function(){
return name;
};
this.setName = function(value){
name = value;
};
}
var person = new Person("zjw");
alert(person.getName()); //"zjw"
person.setName("wjz");
alert(person.getName()); //"wjz"
- 私有变量name只能通过这两个方法访问
静态私有变量
在私有作用域中定义私有变量和函数,与构造函数中定义特权方法的主要区别在于,私有变量和函数是由实例共享的
(function(){
var name= ""; //私有变量
Person = function(value){ //使用函数表达式定义构造函数,函数声明只能创建局部函数
name = value; //不使用var关键字,表明全局变量,使外部能够调用该构造函数
};
Person.prototype.getName = function(){ //在原型中定义方法,使所有实例共享特权方法
return name;
};
Person.prototype.sayName = function(value){
name = value;
};
})();
var person1 = new Person("zjw");
alert(person1.getName()); //"zjw"
person1.setName("wjz");
alert(person1.getName()); //"wjz"
var person2 = new Person("zzz");
alert(person1.getName()); //"zzz"
alert(person2.getName()); //"zzz"
- 什么时候使用实例变量,还是静态私有变量,取决于具体的需求
模块模式
上述通过构造函数和原型中定义的特权方法都是针对所有实例的,而对单个对象创建私有变量和特权方法则使用模块模式
var singleton = function(){
var privateVariable = 10; //定义私有变量
function privateFunction(){ //定义私有函数
return false;
}
return { //返回对象
publicProperty:20, //公有属性
publicMethod:function(){ //特权方法
privateVariable++;
return privateFunction();
}
}
}();
增强的模块模式(适合那些单例对象必须是某种类型的实例,还必须添加某些属性和方法的情况)
var singleton = function(){
var privateVariable = 10; //定义私有变量
function privateFunction(){ //定义私有函数
return false;
}
var object = new customDefineType(); //创建对象
object.publicProperty = true; //公有属性
object.publicMethod = function(){ //特权方法
privateVariable++;
return privateFunction();
};
return object; //返回这个对象
}();