深入理解js闭包

什么是闭包

MDN的解释:闭包是函数和声明该函数的词法环境的组合。

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。

它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的所有局部变量组成。

理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象,这就是闭包的重要概念。

如何产生一个闭包函数

创建闭包最常见方式,就是在一个函数内部创建另一个函数。

function outer() {
    let name = "hello"; // 闭包创建时所能访问的局部变量
    function sayHello() { // 闭包函数
        alert(name);
    }
    return sayHello; // 返回闭包函数
}

let myFunc = outer();
myFunc();

闭包的作用域链包含着它自己的作用域,以及包含它的函数的作用域和全局作用域。
outer有了myFunc的引用,内存一直得不到释放,咋办呢?这样的函数多了是不是会造成内存溢出?
手动释放一下:

myFunc = null;

闭包的注意事项(如何防止内存泄漏)

通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

let add5 = makeAdder(5);
let add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

add5 = null;
add10 = null;

add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。

最后通过 null 释放了 add5 和 add10 对闭包的引用。

在javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收;

如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

闭包中的this对象

let name = "window";
let obj = {
    name: 'object',
    getName: function() {
        return function() {
            return this.name;
        }
    }
}
obj.getName()();  // window

在上面这段代码中,obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。
window才是匿名函数功能执行的环境。

如果想使this指向外部函数的执行环境,可以这样改写:

let name = "window";
let obj = {
    name: 'object',
    getName: function() {
        var that = this;
        return function() {
            return that.name;
        }
    }
}
obj.getName()();

函数内部的定时器

当函数内部的定时器引用了外部函数的变量对象时,该变量对象不会被销毁。

(function() {
    let a = 0;
    setInterval(function(){
        console.log(a++);
    }, 1000)
})()

闭包的用途

模拟块级作用域

var isShow = true;
if(isShow){    
    var a=1000;
    console.log(a);
}
console.log(a); // 在if定义的变量在外部可以访问
(function(){  // a在外部就不认识啦
        var isShow = true;
        if(isShow){    
            var a=10000;
            console.log(a);
        }
  })();  
  console.log(a); // 报错,无法访问

让变量的值始终保持在内存中,对结果进行缓存

function fn(){
    let count = 0;
    return function(){
        count++;
        return count;
    }
}
let add=fn();
add();  // 1
add(); // 2
add(); // 3

封装工具函数

let counter = (function(){
    let privateCounter = 0; // 私有变量
    function change(val){
        privateCounter += val;
    }
    return {
        increment:function(){   // 三个闭包共享一个词法环境
            change(1);
        },
        decrement:function(){
            change(-1);
        },
        value:function(){
            return privateCounter;
        }
    };
})();

counter.value();  // 0
counter.increment();
counter.value();  // 1

你可能感兴趣的:(深入理解js闭包)