自执行函数
自执行函数的好处:匿名函数自执行里面的所有东西都是一个局部的。防止和其他的代码冲突。
自执行函数的四种写法
自执行函数的第一种写法:最前最后加括号
这种写法是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对象,这样一来访问的速度就更快了。
作用域
局部变量
- 在函数内部声明的变量
- 使用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的调用对象放在第二位,最后是全局对象,作用域链的链表的结构是test2——>test1——>window。从链表的开头寻找变量global_variable1,即test2函数内部找变量global_variable1,发现没有,继续向上找,这时分为两种可能:
- 当test1中没有对global_variable1重新声明时,则在test1中也没有找到,继续向上,找到window中的global_variable1变量,结果是10,
- 当test1中对global_variable1重新声明时,则在test1中找到global_variable1变量,结果是1。
闭包
闭包是指可以访问另一个函数作用域中变量的函数,由于在JavaScript中,只有在函数内部的子函数才可以访问局部变量,所以创建闭包的最常见的方式就是在一个函数内创建一个子函数函数,通过这个子函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
闭包的特点:
1.函数内嵌套函数一个或多个子函数
2.内部函数可以引用外层的参数和变量
3.参数和变量不会被垃圾回收机制回收
JavaScript垃圾回收机制请看郑文亮先生的博客:https://www.cnblogs.com/zhwl/p/4664604.html
内存泄漏的情况举例
内存泄漏(memory leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存溢出(out of memory)是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory,比如申请了一个integer,但给它存了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
-
闭包引起的内存泄漏
原因:闭包可以让函数内的局部变量不被垃圾回收机制回收。
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对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>
1234567891011
var timer =setInterval(function(){
$('#ul').empty();//删除ul中的子元素
window.location.reload();//刷新当前页面
console.log($('li'));//当没有清除定时器时会打印出li的元素集(因为是在定时器中打印,所以每个3秒闪烁一次),当有使用clearInterval清除定时器时在不会打印出内容。
},3000);
转自:https://blog.csdn.net/weixin_38233549/article/details/89001742