变量的作用域:全局变量和局部变量
var n = 999;
function f1 () {
alert(n)
}
f1(); // 999 ==> 函数内部可以直接读取全局变量
function f2 () {
var n = 999;
}
alert(n) // error ==> 函数外部无法读取函数内部的局部变量
function f3 () {
m = 999;
}
f3();
alert(m); // 999 ==> 如果不声明m,实际上声明了一个全局变量
如何从外部读取局部变量?
// 在函数的内部,再定义一个函数
function f1 () {
var n = 999;
function f2 () {
alert(n); // 读取到f1里的n 999
}
return f2; // f2可以读取f1中的局部变量,只要把f2作为返回值,就可以在f1外部读取到f1内部的变量
}
var result = f1();
result(); // 999
那么f2函数,就是闭包
闭包:就是能够读取其他函数内部变量的函数
在javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数
内部的函数”
本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
闭包用途
1、可以读取函数内部的变量
2、让这些变量的值始终保持在内存中
function f1 () {
var n = 999;
nAdd = function () { n += 1 }
function f2 () {
alert(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
3.1、reuslt实际就是闭包f2函数,运行了两次,第一次999,第二次1000。这证明了,函数f1中的局部变量n一直
保存在内存中,并没有在f1调用后被自动清除
3.2、没有清除的原因:f1是f2的父函数,而f2被赋值给了一个全局变量result,这就导致f2始终在内存中,而f2
的存在依赖于f1,因此f1始终也在内存中,不会再调用结束后,被垃圾回收机制回收
3.3、nAdd没有使用var关键字,是一个全局变量。其次,nAdd的值是一个匿名函数,而这个匿名函数本身也是一
个闭包,所以nAdd相当于是一个setter,可以再函数外部对函数内部的局部变量进行操作
使用闭包的注意点
1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能
问题,在IE中可能导致内存泄露。解决方法,在退出函数前,将不使用的局部变量全部删除
2、闭包会在夫函数外部,改变夫函数颞部变量的值。所以,如果把父函数当作对象使用,把闭包当作它的公共方
法,把内部变量当作它的私有属性。这时一定要小心,不要随便改变父函数内部变量的值
闭包实现单例模式
将外部函数的变量保存在内存中, 利用这一特性,实现类的单例模式
单例模式:
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点
主要解决:一个全局使用的类频繁地创建与销毁
何时使用:想控制实例数目,节省系统资源的时候
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建
var Head = (function () { // 匿名自执行函数
var HeadClass = function () {}; // 声明HeadClass对象,无法在外部直接调用
var instance; // 声明一个instance对象
return function () {
if (instance) { // 如果已存在,则返回instance
return instance;
}
instance = new HeadClass() // 如果不存在 则new一个HeadClass对象
return instance;
}
})();
var a = new Head(); // 构造一个单例模式对象
var b = new Head();
console.log(a === b) // true
代码片段
var name = "The Window";
var obj = {
name; "My Obj",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
alert(object.getNameFunc()());
var object = {
name: "My Object",
getNameFunc2: function () {
var that = this;
return function () {
return that.name;
};
}
};
alert(object.getNameFunc()());