闭包
变量作用域
要理解闭包,首先要理解javascript的特殊的变量作用域。
变量的作用域无非就两种:全局变量和局部变量。
javascript语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
注意点:在函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明的是一个全局变量!
程序设计中作用域的概念:
通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
词法作用域:
词法作用域,也叫静态作用域,它的作用域是指在词法分析阶段就确定了,不会改变。
动态作用域
动态作用域,是在运行时根据程序的流程信息来动态确定的,而不是在写代码时进行静态确定的。
主要区别:词法作用域是在写代码或者定义时确定的,而动态作用域是在运行时确定的。词法作用域关注函数在何处声明,而动态作用域关注函数
// 词法作用域
var abc = 1;
function f1() {
console.log(abc);
}
function f2() {
var abc = 2;
f1();
}
f2();
// 类似动态作用域
function show() {
console.log(this);
}
show();
document.querySeletor(".btn").onclick = function () {
console.log(this);
show();
}
document.querySelector(".btn").onclick = show;
var timer=setTimeout(show,1000);
作用域链
我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。
1.闭包的特性
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
闭包的定义及其优缺点
闭包是指有全访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
闭包的缺点就是会常驻内存,会增大内存的使用量,如果没有使用好会造成内存的泄漏
闭包是JavaScript语言的一大特点,主要应用场合是为了:设计私有方法和变量。
通常在函数执行完毕后局部活动对象就会被销毁,内存中只保存全局作用域,但闭包不变
嵌套函数的闭包
function aaa() {
var a = 1;
return function(){
alert(a++)
};
}
var fun = aaa();
fun();// 1 执行后 a++,,然后a还在~
fun();// 2
fun = null;//a被回收!!
闭包会使变量始终保存在内存中,如果用的不得当就会增大内存的消耗
JavaScript的垃圾回收原理
1.垃圾收集器会按照固定的时间间隔,周期性的找出不再继续使用的变量,然后释放其占用的内存
不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。
全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。
javaScript具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行。
现在各大浏览器通常采用的垃圾回收有两种方法:标记清除、引用计数
**
标记清除
**
工作原理:
当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
- 当变量进入环境时,垃圾收集器会在运行的时候会给存储在内存中的所有变量都加上标记。
- 当变量离开环境时,垃圾收集器会去掉环境中的变量以及被环境中的变量引用的变量的标记。
- 最后垃圾收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。
引用计数
跟踪记录每个值被引用的次数,当引用次数变成0时,就销毁回收内存。
由于js垃圾回收机制,f1每次执行完,变量n会被回收,所以值并没有被保存下来
function fn1(){
var n = 5;
n++;
return n;
}
console.log( fn1() ); //6
console.log( fn1() ); //6
使用闭包的好处
1、具备独立私有变量,私有变量只能在某个作用域内访问,并且可以长期存储 (类似于PHP中static变量)局部变量,也是只能在某个作用域内访问,但是不能长期存储
2、可以防止变量污染,防止名字冲突
出于种种原因,我们有时候需要得到函数内的局部变量,如何从外部读取局部变量?
闭包的应用
function fn1(){//闭包
var n = 5;
function fn2() {
n++;
return n;
}
return fn2;
}
var fn = fn1();
console.log( fn() ); //6
console.log( fn() ); //7
console.log( fn() ); //8
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
console.log( checkscope()() ); //local scope
模块化
一个模块应该具有私有属性、私有方法和公有属性、公有方法。
而闭包能很好的将模块的公有属性、方法暴露出来。
var myModule = (function (window, undefined) {
var myname = "xiaohong";
var myage = 18;
function sayHi() {
alert('hi,大家好,我叫 ' + myname);
}
// return {myname,sayHi};
return {myname: myname,sayHi:sayHi};
})(window);
console.log( myModule.myname ); //xiaohong
myModule.sayHi();
// "return"关键字将对象引用导出赋值给myModule,从而应用到闭包。
(function (window, undefined) {
var myname = "xiaohong";
var myage = 18;
function sayHi() {
alert('hi,大家好,我叫 ' + myname);
}
window.myname = myname;
window.sayHi = sayHi;
})(window);
console.log( myname ); //xiaohong
sayHi();
使用闭包的注意点
(1)使用闭包使得函数中的变量始终在内存中,内存消耗很大,所以不能滥用闭包,否则会造成页面性能问题。,闭包,将变量放到内存中,以后对变量操作是从内存中取出来,所以多次操作是会对变量产生变化。
在IE中会有可能导致内存泄露。大部分浏览器,通过赋值为null,释放内存。