闭包
闭包的基本概念
闭包(closure)
是JavaScript语言的一个难点,也是JavaScript的一个特色,很多高级的应用都要依靠闭包来实现。
作用域
在js中,函数会形成函数作用域,在函数内部可以直接访问全局变量
var str = "zs";
function fn(){
console.log(str);//访问全局变量
}
fn();//zs
在函数外部却无法访问函数内部的变量
function fn(){
var str = "zs";
}
fn();
console.log(str);//报错 str is not defined
问题:我怎么才能获取到函数内部的变量?
作用域链
在函数内部有一个函数,那么函数内部的函数是可以访问到外部函数的变量的。
解决方法:
function fn(){
var str = "zs";
function f2(){
console.log(str);
}
f2();
}
fn();
在上述代码中,fn中定义的所有变量,对于f2函数都来都是可以访问的。但是现在f2在函数的内部,我们如何在外部访问到f2这个函数呢?
function fn(){
var str = "zs";
function f2(){
console.log(str);
}
return f2;
}
var result = fn();
result();// "zs"
闭包的概念
闭包是函数和声明该函数的词法环境的组合。
在JavaScript中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,产生闭包。
**闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用 **
产生闭包的条件
当内部函数访问了外部函数的变量的时候,就会形成闭包。
闭包的作用:
- 私有变量,保护数据安全
- 持久化维持数据
闭包的应用
计数器
需求:统计一个函数的调用次数
var count = 0;
function fn(){
count++;
console.log("我被调用了,调用次数是"+count);
}
fn();
fn();
fn();
缺点:count是全局变量,不安全。
使用闭包解决这个问题!!!!
function outer(){
var count = 0; // 私有变量, 将count保护起来了
function add(){
count++;
console.log("当前count"+count);
}
return add;
}
var result = outer();
result();
【银行存钱取钱.html】
实现缓存
缓存(cache):数据的缓冲区,当要读取数据时,先从缓冲中获取数据,如果找到了,直接获取,如果找不到,重新去请求数据。
计算斐波那契数列,会有很大的性能问题,因为重复的计算了很多次,因此我们可以使用缓存来解决这个性能问题。
初级优化:
使用缓存的基本步骤:
- 如果要获取数据,先查询缓存,如果有就直接使用
- 如果没有,就进行计算,并且将计算后的结果放到缓存中,方便下次使用。
//缓存
var arr = [];
var fbi = function (n) {
count++;
if (n == 1 || n == 2) {
return 1;
}
if (arr[n]) {
return arr[n];
} else {
var temp = fbi(n - 1) + fbi(n - 2);
arr[n] = temp;//存入缓存
return temp;
}
}
缺点:既然使用缓存,就需要保证缓存的数据的安全,不能被别人修改,因此,需要使用闭包来实现缓存的私有化。
function outer() {
//缓存
var arr = [];
var fbi = function (n) {
if (n == 1 || n == 2) {
return 1;
}
if (arr[n]) {
return arr[n];
} else {
var temp = fbi(n - 1) + fbi(n - 2);
arr[n] = temp;//存入缓存
return temp;
}
}
return fbi;
}
var fbi = outer();
console.log(fbi(40));
闭包存在的问题
闭包占用的内存是不会被释放的,因此,如果滥用闭包,会造成内存泄漏的问题。闭包很强大,但是只有在必须使用闭包的时候才使用。
js的垃圾回收机制
- 内存:计算机中所有程序的运行都是在
内存
中进行的,因此内存的性能对计算机的影响非常大,运行程序需要消耗内存,当程序结束时,内存会得到释放。 - javascript分配内存:当我们定义变量,javascript需要分配内存存储数据。无论是值类型或者是引用类型,都需要存储在内存中。
- 垃圾回收:当代码执行结束,分配的内存已经不需要了,这时候需要将内存进行回收,在javascript语言中,
垃圾回收机器
会帮我们回收不再需要使用
的内存。
引用记数法清除
引用记数垃圾收集:如果没有引用指向某个对象(或者是函数作用域),那么这个对象或者函数作用域就会被垃圾回收机制回收。
var o = {
name:"zs"
}
//对象被o变量引用 ,引用记数1
var obj = o; //变量被o和obj引用,引用记数2
o = 1; //o不在引用对象了, 引用记数1
obj = null; //obj不在引用对象了,引用记数0,可以被垃圾回收了。
标记清除法清除
使用引用计数法进行垃圾回收的时候,会出现循环引用导致内存泄漏的问题。因此现代的浏览器都采用标记清除法来进行垃圾回收。
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象Window)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。
闭包占用内存释放
function outer(){
var count = 0;
function fn(){
count++;
console.log("执行次数"+count);
}
return fn;
}
var result = outer();
result();
result = null;//当函数fn没有被变量引用了,那么函数fn就会被回收,函数fn一旦被回收,那么outer调用形成的作用域也就得到了释放。