# 闭包

下面是一个简单的闭包

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>