自執行函數
自執行函數的好處:匿名函數自執行里面的所有東西都是一個局部的。防止和其他的代碼沖突。
自執行函數的四種寫法
自執行函數的第一種寫法:最前最后加括號
這種寫法是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
