闭包作用域


  1. 创建函数

    • 开辟一个堆内存
    • 把函数体中的代码当做字符串存储进去
    • 把堆内存的地址赋值给函数名/变量名
    • 函数在哪创建的,那么它执行时所需要查找的上级作用域就是谁
  2. 函数执行

    • 形成一个全新的私有作用域、执行上下文、私有栈内存(执行一次形成一个,同个函数多个之间也不会产生影响)
    • 形参赋值 & 变量提升
    • 代码执行(把所属堆内存中的代码字符串拿出来一行行执行)
    • 遇到一个变量,首先看它是否为私有变量(形参和在私有作用域中声明的变量是私有变量),是私有的就操作自己的变量即可,不是私有的,则向上级作用域查找……一直找到全局作用域为止 ==>作用域链查找机制
    • 私有变量和外界的变量没有必然关系,可以理解为被私有栈内存保护起来了,这种机制其实就是闭包的保护机制
  3. 关于堆栈内存释放问题

    函数执行就会形成栈内存(从内存中分配的一块空间),如果内存都不销毁释放,很容易就会导致内存溢出。堆栈内存的释放问题是学习JS的核心之一。

  • 堆内存释放问题

    1. // =>创建一个引用类型值,就会产生一个堆内存
    2. // 如果当前创建的堆内存不被其他东西所占用了(浏览器会在空闲的时候,查找每一个内存的引用状况,不被占用的都会给回收释放掉),则会释放。
    3. let obj = {
    4. name : 'test'
    5. };
    6. let oop = obj;
    7. // 此时obj和oop都占用着对象的堆内存,如果要释放堆内存,需要手动解除变量和值的关联
    8. obj = null;
    9. oop = 1;
  • 栈内存释放

    1. // => 打开浏览器形成的全局作用域是栈内存
    2. // => 手动执行函数形成的私有作用域是栈内存
    3. // => 基于ES5中的let/const形成的块作用域也是栈内存
    4. // => ...
    5. /*
    6. * 全局栈内存:关掉页面的时候才会销毁
    7. * 私有栈内存:
    8. * 1.一般情况下,函数执行完成,形成的私有栈内存就会被销毁掉(排除出现无限级递归、出现死循环的模式)
    9. * 2.但是一旦栈内存中的某个东西(一般都是堆地址)被私有作用域以外的事物给占用了,则当前栈内存不能立即被释放销毁(特点:私有作用域中的私有变量等信息也保留下来了) =>市面上认为的闭包:函数执行形成不能被释放的私有栈内存,这样的才是闭包
    10. *
    11. */
    12. function fn() {
    13. // ....
    14. }
    15. fn(); // => 函数执行形成栈内存,执行完成栈内存销毁
    16. function X() {
    17. return function(){
    18. // ....
    19. }
    20. }
    21. let f = X(); // =>f栈用了x执行形成的栈内存中的一个东西(返回小函数对应的堆),则X执行形成的栈内存不能被释放了
  1. 闭包的两大作用

    从性能角度讲,我们真实项目中应该减少对闭包的使用(因为闭包会产生不释放的栈内存,过多使用容易导致内存溢出或者降低性能)

  • 保护(私有变量和外界没有必然联系)
  • 保存(形成不销毁的内存,里面的私有变量等信息保存下来了)
  1. JQuery提供了大量的方法供开发人员使用,如果这些方法不保护起来,用户编写的方法很容易和JQuery方法名字相同产生冲突,即全局变量污染。为了防止全局变量污染,JQuery中的方法和变量需要用闭包保护起来。

    1. (function(global, factory){
    2. //...
    3. // typeof window !== 'undefined' ? window : this,验证当前所处环境的全局对象是window还是global等
    4. factory(global); // =>lichao(window);
    5. })(typeof window !== 'undefined' ? window : this, function lichao(window, noGlobal){
    6. //...
    7. var jQuery=function(selector, context) {
    8. // ...
    9. };
    10. // =>通过给全局对象增加属性:jQuery和$,把私有的jQuery方法暴露到全局作用域下,供外面使用(等价于 return jQuery)。
    11. // 外界需要使用函数中私有变量,可以基于window.xxx和return xxx两种方式实现这个需求
    12. window.jQuery = window.$ = jQuery;
    13. });
  2. 在真实项目中,我们一般都要把自己写的内容放到一个闭包中,这样可以有效方式自己代码和别人的代码产生冲突(全局变量污染:真实项目中,要尽可能减少对全局变量的使用);如果需要把自己的东西给别人用,基于return和window.xxx等方式暴露给别人即可。

    1. //.....A写的代码
    2. var xxx = (function() {
    3. return XXX;
    4. })();
    5. //......B写的代码
    6. (function(){
    7. window.xxx=xxx;
    8. })();
    9. //=>JQuery
    10. $(function(){
    11. //...这样写在某些角度上也是为了减少全局的变量
    12. });