自执行函数和闭包
自执行函数
自执行函数的好处: 自执行函数之上的所有内容都是本地的。 防止与其他代码发生冲突。
编写自执行函数的四种方法。 编写自执行函数的第一种方法:在第一个和最后一个添加括号。
这种写法是jslint推荐的,可以让读者清楚地看到它是一个整体。
注意:这种写法必须保证js代码末尾以seal结尾,否则会报Uncaught TypeError。
(function(global, factory){
console.log(global+factory)
}(1,2));//3
编写自执行函数的第二种方法:在函数两边添加括号
与第一种写法相比,第二种写法缺乏阅读的完整性。
(function(global, factory){
console.log(global+factory)
})(1,2);//3
编写自执行函数的第三种方法:在函数后面添加运算符,常用的有! 和无效
!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函数
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)
为了正确访问自执行函数中的变量,可以使用外部提供的套接字作为窗口属性或技能。
(function(window,global, factory){
function getGlobalValue(){
return global
}
window.getGlobalValue=getGlobalValue
}(window,2,3));
console.log(getGlobalValue())//2
注意:上面代码中记得传入window,因为window既不是声明的局部变量,也不是压缩时的参数,所以不会被压缩混淆。 不过传入window可以压缩混淆,并且传入window参数,不用沿着作用域链一层层往下查找,直到到达顶层作用域即可获取window对象,因此访问速度会更快。
作用域局部变量 函数内部声明的变量 使用 let 声明的变量 全局变量 函数外部定义的变量 函数内未声明的变量(不使用 var 关键字)
注意:在 JavaScript 中,作用域是可访问变量、对象和函数的集合。
在函数体内,局部变量的优先级低于同名的全局变量。 如果在函数内声明局部变量或者函数参数中的变量与全局变量同名,则全局变量将被局部变量完全覆盖。
作用域链
当声明一个函数时,局部作用域一次包裹一层,这就是作用域链。
1.执行函数时,总是先查找函数内部的局部变量
2、如果内部找不到(该函数没有本地作用域),就会在该函数创建的作用域(父函数作用域)中查找,然后往下查找,直到找到。
var global_variable1=10;
function test1(){
var local_variable1={name:'html'};
var global_variable1=1;
global_variable2=11;
local_variable2=[1,2];
var local_variable2;
function test2(){
console.log(global_variable1);//如果在第4行声明了和全局变量global_variable1同名的局部变量,则输出1,如果没有,则输出10
}
console.log(global_variable1);//如果在第4行声明了和全局变量global_variable1同名的局部变量,则输出1,如果没有,则输出10
console.log(local_variable1);//{name: "html"}
console.log(global_variable2);//11
console.log(local_variable2);//[1,2]js函数中声明的变量(但是不涉及赋值)都被“提前”至函数体的顶部,变量初始化留在原来的位置。
test2();
}
//console.log(local_variable);//Uncaught ReferenceError: local_variable is not defined
test1();
//test2();//Uncaught ReferenceError: test2 is not defined
console.log(global_variable2)//11
注意:上面的代码中,当执行test2时,会创建函数test2的执行环境,并将对象放在数组的开头,然后将函数test1的调用对象放在第二个位置,最后将函数test1的调用对象放在数组的开头。全局对象和作用域链数组的结构是test2——>test1——>window。 从数组开头开始搜索变量global_variable1,即在test2函数内部搜索变量global_variable1。 如果发现没有找到,则继续查找。 有两种可能:
当test1中没有重新声明global_variable1时,在test1中找不到它。 继续向下,找到window.conf中的global_variable1变量。 结果是 10。
当test1中重新声明global_variable1时,在test1中找到global_variable1变量,结果为1。
关闭
闭包是指一个函数可以访问另一个函数范围内的变量。 由于在 JavaScript 中,只有函数内的子函数可以访问局部变量,因此创建闭包的最常见形式是在函数内创建闭包。 子函数函数,通过这个子函数javascript匿名函数自执行,访问这个函数的局部变量。 闭包可以用来突破作用域的作用域,将函数内部的变量和方法传递到外部。
闭包的特点:
1. 一个函数内嵌套一个或多个子函数
2.内部函数可以引用内部参数和变量
3.参数和变量不会被垃圾回收机制回收
JavaScript垃圾回收机制请参见郑文亮先生的博客:
内存泄漏的例子
内存泄漏是指程序中动态分配的堆内存由于某种诱因没有被释放或者未能释放,造成系统内存的浪费,导致程序运行速度变慢甚至无法释放等严重后果。系统崩溃。
内存溢出(Out of Memory)是指程序申请显存时,没有足够的显存空间供其使用,出现内存不足的情况。 比如申请一个整数,但是里面存的是一个可以用long来存储的数字,那就是内存。 溢出。
注意:内存泄漏最终会导致内存溢出。
由意外的全局变量(例如在函数中未使用 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
闭包导致的内存泄漏
原因:闭包可以防止函数内的局部变量被垃圾回收机制回收。
解决方案:在外部定义风暴处理函数并去掉闭包javascript匿名函数自执行,或者删除定义事件处理函数的外部函数中对DOM的引用。
var testObj1={};
setInterval(function (){
var testObj2=testObj1;
var fn=function (){//未使用,但是引用了testObj1,所以为被回收,如果testObj1中的变量是大数据时的,则cpu内存会快速增加,可以使用谷歌的performance检测(不到10秒中,就奔溃了)
if(typeof testObj2 =='object'){
console.log('testObj2是对象')
}
};
testObj1={
name:new Array(100000000).join(','),
operation:function(){
console.log('我在飘啊飘')
}
};
},1000);
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>
var timer =setInterval(function(){
$('#ul').empty();//删除ul中的子元素
window.location.reload();//刷新当前页面
console.log($('li'));//当没有清除定时器时会打印出li的元素集(因为是在定时器中打印,所以每个3秒闪烁一次),当有使用clearInterval清除定时器时在不会打印出内容。
},3000);
clearInterval(timer);