思维导图

36.JS中的闭包机制 - 图1

一、什么是闭包机制

函数执行形成的私有上下文,即能保护里面的私有变量不受外界干扰,也能在当前上下文中保存一些信息(前提:形成的上下文不销毁),上下文中的这种保存和保护机制,就是闭包机制

二、闭包的两大作用

1、保护作用

在这个上下文中会有一些私有的变量AO(XX),这些私有的变量和外界的变量不会冲突(互不影响)

2、保存作用

某些情况下,上下文中的某些内容被外界占用后,当前上下文并不会出栈销毁,这样就会把上下文中的一些信息储存起来

三、闭包的应用

1.利用闭包的保护作用

  • -1).团队协作开发中;

    • A/B共同开发一个页面,最后要把代码合并在一起,为了防止全局变量的冲突污染,我们建议每个开发者,都把自己的代码放置到一个闭包中(自执行函数执行即可,这样就是私有的上下文)保护起来
  1. // A的代码
  2. (function anonymous() {
  3. /* 自执行函数执行,会形成一个私有的上下文,在这里声明+定义的变量或者函数都是私有的 */
  4. var x = 100,
  5. y = 200;
  6. function func() {
  7. // ...
  8. }
  9. })();
  10. // B的代码
  11. ~ function anonymous() {
  12. // console.log(anonymous); //=>函数本身
  13. //=>匿名函数设置的函数名只能在函数里面应用,函数外面是无法访问的
  14. var x = 200,
  15. n = 0;
  16. function func() {
  17. // ...
  18. }
  19. function handled() {
  20. // ...
  21. }
  22. }();
  23. // console.log(anonymous); //=>Uncaught ReferenceError: anonymous is not defined
  24. 复制代码
  • -2).封装一个插件或者类库等;

    • 为了防止我们定义的变量和方法 和 用户定义的冲突,我们也是需要把所有写的代码放到一个闭包中,例如:jQuery
/* JQUERY源码一瞥 */
// typeof window !== "undefined" ? window : this  浏览器端window是存在的,所以为window
(function (global, factory) {
    "use strict";
    // => global===window
    // => factory===function (window, noGlobal){...}
    factory(global);
})(window, function (window, noGlobal) {
    // JQUERY的源码
}); 
//=> 利用闭包的机制,把JQ源码当作自执行函数的形参传给函数,完成执行
复制代码

2.利用闭包的保存作用

  • -1).在某些需求中,我们经常需要形成一个闭包,存储一些值(而且不能销毁),这样来供后面的程序运行使用(例如:惰性函数、柯理化函数、compose函数等JS高阶编程技巧中,就是基于闭包的保存机制实现的)

此时我们发现一个问题:每个人的代码都独立了,那我们每个人都会用到的公共方法,难道也要都单独写一遍?

有此需求,必然得有解决办法:

四、闭包内的值暴露给外面使用的两种方法

需求:我们需要把某一个闭包(私有上下文)中的值暴露到外面

1、基于window.xxx=xxx暴露到全局

这种办法虽然可以实现,但是也会存在冲突,如果我们每一个版块都需要暴露更多的方法,同时都基于这种方法暴露到全局对象GO上,也可能导致方法之间的冲突

(function anonymous() {
    function queryURLParams() {
        // ...
    }
    function sum() {
        // ...
    }
// 想暴露到外面使用,可以暴露到全局上(赋值给全局对象GO =>window)
    window.queryURLParams = queryURLParams;
    window.sum = sum;
})();
queryURLParams(); //=>window.queryURLParams() 
复制代码

2、JS中的设计模式:单例设计模式

每一个版块暴露到全局下只有一个变量而已,所有需要供别人调取的方法都在对象中(这样暴露一个或者多个都无所谓)

  • 避免了全局变量的污染,也同时实现了不同闭包之间方法的公用性
var utils = (function anonymous() {
    // queryURLParams:获取URL地址参数信息
    function queryURLParams() {
        // ...
    }
    // sum:实现任意数求和
    function sum() {
        // ...
    }
    // 把需要供外面访问的变量和方法,赋值给一个对象,最后返回(外层基于VAR utils定义变量来接收即可)
    return {
        queryURLParams: queryURLParams,
        sum: sum
    }; //=>return AAAFFF000;
})();
// console.log(utils); //=>{queryURLParams:函数,sum:函数}
utils.queryURLParams();
utils.sum(); 
复制代码

utils对象中包含了需要供别人调取使用的方法;此时我们把utils称之为“命名空间”,而对象就是一个空间,空间中包含了当前版块中的内容,或者是把当前版块中的内容按照命名空间进行了分组,每一个分组都是一个单独的个体

命名空间

  • 把每一个对象值 赋值给变量 =>可以理解为,给每一个对象的堆内存空间起了个变量名,所以此时可以把这个变量称为 “命名空间”
  • 当前对象中的键值对都是 命名空间 中私有的键值对

五、闭包的优缺点

1、优点

  • 上述我们一直说的保护和保存作用

2、缺点

  • 因为闭包会产生不销毁的上下文,这样导致栈/堆内存消耗过大,有时候也会导致内存泄漏等,影响页面的运行性能,所以在真实项目中,要合理应用闭包(不要滥用)