闭包

下面是一个简单的闭包

const MathTest = function (x, y) {
    var x = x;
    var y = y;

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

 var x = MathTest(3, 4); // 执行

闭包的特点:

  1. 函数内嵌套函数一个或多个子函数
  2. 内部函数可以引用外层的参数和变量
  3. 参数和变量不会被垃圾回收机制回收
  4. JavaScript垃圾回收机制请看郑文亮先生的博客:https://www.cnblogs.com/zhwl/p/4664604.html

匿名函数

所谓匿名函数就是没有名字的函数

function () {
   
}

既然没有名字,那么如何调用呢:

var b=function () {
   
}

b()

自执行函数

自执行函数相当于上面匿名函数两步的结合

(function(){
     //代码
})();

自执行函数的好处:匿名函数自执行里面的所有东西都是一个局部的。防止和其他的代码冲突。

自执行函数的四种写法

自执行函数的第一种写法:最前最后加括号

这种写法是jslint推荐的写法,可以让阅读者清楚的看到这是一个整体。

注意:这种写法必须保证js代码的结尾处以封号结尾,不然会报Uncaught TypeError的错。

(function(global, factory){
    console.log(global+factory)
}(1,2));//3

自执行函数的第二种写法:function外面加括号

第二种写法相比较第一种写法缺少了阅读的整体性。

(function(global, factory){
    console.log(global+factory)
})(1,2);//3

自执行函数的第三种写法:在function前面加运算符,常用的是!和void

!function(global, factory){
    console.log(global+factory)
}(1,2) // 3

+ function(global, factory){
    console.log(global+factory)
}(1,2) // 3

void function(global, factory){
    console.log(global+factory)
}(1,2) // 3

自执行函数的第四种写法:new function

 new function(){
   console.log(1)
 }

 new function(a){
     console.log(a)
 }(1) // 传递参数的情况

访问自执行函数里面的变量

如果直接调用自执行函数中的方法或者变量会报错,比如下面的代码会报:Uncaught ReferenceError: global is not defined。

(function(global, factory){
    var global, factory=3;
}())

console.log(global, factory)

为了能正确的访问自执行函数中的变量,可以将对外提供的接口作为window的属性或者是方法。

(function(window,global, factory){
    function getGlobalValue(){
        return global
    }
    window.getGlobalValue=getGlobalValue
}(window,2,3));

console.log(getGlobalValue()) // 2

注:在上述代码中记得传入window,因为在压缩时window既不是声明的局部变量也不是参数,所以不会被压缩混淆的,但是传入window是可以将其压缩混淆,而且传入window参数,就可以不用沿着作用域链一层层向上查找直到顶层作用域去获取window对象,这样一来访问的速度就更快了。

内存泄漏的情况举例

  • 内存泄漏(memory leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
  • 内存溢出(out of memory)是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory,比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

注:内存泄漏最终会导致内存溢出。

1. 意外的全局变量引起的内存泄漏

比如在函数内没有使用var关键字声明的变量或者使用this声明的变量

  • 原因:全局变量,不会被回收。
  • 解决:使用严格模式(‘use strict’)避免。
'use strict'
function test1(){
    global_variable1='我是全局变量';//这个变量的作用域是window
    console.log(1)
}
test1()//使用严格模式报错:Uncaught ReferenceError: global_variable1 is not defined
function test2(){
    this.global_variable2='我是this创建的变量';
    console.log(2)
}
test2()//使用严格模式报错:Uncaught TypeError: Cannot set property 'global_variable2' of undefined

2.闭包引起的内存泄漏

  • 原因:闭包可以让函数内的局部变量不被垃圾回收机制回收。
  • 解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。
var testObj1={};
setInterval(function (){
    var testObj2=testObj1;
    var fn=function (){ // fn未使用,但是引用了testObj1,所以未被回收,如果testObj1中的变量是大数据时的,则cpu内存会快速增加,可以使用谷歌的performance检测(不到10秒中,就奔溃了)
        if(typeof testObj2 =='object'){
            console.log('testObj2是对象')
        }
    };
    testObj1={
        name:new Array(100000000).join(','),
        operation:function(){
            console.log('我在飘啊飘')
        }
    };
},1000);

performance检测结果

3.定时器未清除

  • 原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。
  • 解决:清除定时器
<ul id='ul'>
    <li>
        <a href="">a标签1</a>
    </li>
    <li>
        <a href="">a标签2</a>
    </li>
    <li>
        <a href="">a标签3</a>
    </li>
</ul>

<script>
var timer =setInterval(function(){
    $('#ul').empty(); // 删除ul中的子元素
    window.location.reload(); // 刷新当前页面
    console.log($('li')); // 当没有清除定时器时会打印出li的元素集(因为是在定时器中打印,所以每个3秒闪烁一次),当有使用clearInterval清除定时器时在不会打印出内容。
},3000);
</script>