一次性了解闭包

什么是闭包

闭包是捆绑在一起(封闭)的函数与其周围状态(词法环境)的引用的组合。—mdn

是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。—红宝书

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。—你不知道的js

闭包形成的本质

JavaScript 闭包的本质源自两点,词法作用域和函数当作值传递。

词法作用域

词法作用域也就是静态作用域,一开始就定义好的.

引擎通过数据结构和算法表示一个函数,使得在代码解释执行时按照词法作用域的规则,可以访问外围的变量,这些变量就登记在相应的数据结构中。

值传递

ECMAScript 中所有函数的参数都是按值传递的。—红宝书

一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。

经典闭包

30秒的例子

const initCounter = (start = 0) => {
  let value = start;
  return {
    get: () => value,
    increment: () => ++value,
    decrement: () => --value,
    reset: () => value = start
  };
}

const counter = initCounter(5);
counter.get(); // 5
counter.increment(); // 6
counter.increment(); // 7
counter.decrement(); // 6
counter.reset(); // 5
复制代码

所有返回对象的属性都使用闭包以某种方式操作initCountervalue变量。

initCounter函数返回包裹的块作用域就是个闭包.

value是一个自由变量,可以在闭包内部使用,这种方法的明显好处是,如果你想通过 定义多个计数器initCounter,你不需要value到处创建多个实例,但它们被返回的对象安全地封装,使用闭包。

你不知道的js

var fn;
function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz; // 将 baz 分配给全局变量
}
function bar() {
fn(); // 妈妈快看呀,这就是闭包!
}
foo();
bar(); // 2
复制代码

mdn例子

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();
复制代码

闭包的使用场景

闭包随处可见,一个Ajax请求的成功回调,一个事件绑定的回调方法,一个setTimeout的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影。

闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7
复制代码

闭包的另一个用处,是封装对象的私有属性和私有方法。

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25
复制代码

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

总结

一句话解释:当一个函数被创建并传递或从另一个函数返回时,它会携带一个背包。背包中是函数声明时作用域内的所有变量。

阮一峰的解释

闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享