首先先介紹一下作用域等一些基礎(chǔ)概念。
每個JavaScript函數(shù)都是一個對象,對象中有些屬性我們可以訪問,但有些不可以,這些屬性僅供JavaScript引擎存取,[[scope]]就是其中一個。
[[scope]] : 指的就是我們所說的作用域,其中存儲了執(zhí)行期上下文的集合
作用域鏈 : [[scope]] 中所存儲的執(zhí)行期上下文對象的集合,這個集合呈鏈?zhǔn)芥溄?,我們把這種鏈接叫做作用域鏈。
運行期上下文 : 當(dāng)函數(shù)執(zhí)行時,會創(chuàng)建一個稱為執(zhí)行期上下文的內(nèi)部對象(AO)。一個執(zhí)行期上下文定義了一個函數(shù)執(zhí)行的環(huán)境,函數(shù)每次執(zhí)行時對應(yīng)的執(zhí)行環(huán)境都是的,所以多次調(diào)用一個函數(shù)會導(dǎo)致創(chuàng)建多個執(zhí)行上下文,當(dāng)函數(shù)執(zhí)行完畢,它所產(chǎn)生的執(zhí)行上下文被銷毀。
查找變量 :從作用域鏈的頂端依次向下查找。
下面舉一些例子:
-
function a(){
-
function b(){
-
function c(){
-
-
}
-
c();
-
}
-
b();
-
}
-
a();
-
-
-
a defined a.[[scope]] ----> 0 : GO //a定義的時候產(chǎn)生GO對象
-
a doing a.[[scope]] ----> 0 : aAO //a執(zhí)行的時候新產(chǎn)生AO對象
-
1 : GO
-
-
b defined b.[[scope]] ----> 0 : aAO //子級b定義會繼承父級a運行時產(chǎn)生的對象
-
1 : GO
-
b doing b.[[scope]] ----> 0 : bAO //子級b新產(chǎn)生AO對象
-
1 : aAO
-
2 : GO
-
-
c defined c.[[scope]] ----> 0 : bAO //c定義時會繼承b運行時產(chǎn)生的屬性
-
1 : aAO
-
2 : GO
-
c doing c.[[scope]] ----> 0 : cAO //c執(zhí)行時同時又產(chǎn)生新的AO
-
1 ;bAO
-
2 : aAO
-
3 : GO
立即執(zhí)行函數(shù)
之前學(xué)過函數(shù)的定義、函數(shù)表達(dá)式,還有一種函數(shù)叫做立即執(zhí)行函數(shù)。
立即執(zhí)行函數(shù):函數(shù)執(zhí)行過后立即被銷毀。
立即執(zhí)行函數(shù)的官方寫法:
-
// 立即執(zhí)行函數(shù)的官方寫法
-
(function() {} ()); W3C建議此種
-
(function() {})();
針對初始化功能的函數(shù),可以有參數(shù)。
-
var num = function (a,b){
-
return a + b;
-
}(1,2);
-
-
(function abc(){
-
var a = 123;
-
var b = 234;
-
console.log(a+b);
-
}())
只有表達(dá)式才能被執(zhí)行符號執(zhí)行,能被執(zhí)行符號執(zhí)行的表達(dá)式,函數(shù)名字會被自動忽略。
-
function test(){
-
console.log("a");
-
}() 會出現(xiàn)語法解析錯誤,因為括號前面是函數(shù)聲明
-
-
(+ function test( ){
-
console.log('a');
-
}()) -------->打印出a
下面是一道曾阿里面試題
-
function test(a, b, c, d){
-
console.log(a + b + c + d);
-
}(1, 2, 3, 4);
-
-
// 不報錯也沒有執(zhí)行
下面是幾道經(jīng)典的例題,可以參考一下:
-
function test(){
-
var arr = [];
-
for(var i = 0; i < 10; i ++){
-
arr[i] = function (){
-
console.log(i);
-
}
-
}
-
return arr;
-
}
-
var myArr = test();
-
for(var j = 0; j < 10; j++){
-
myArr[j]();
-
}
那么采用立即執(zhí)行函數(shù)呢?會有怎樣的結(jié)果呢?
-
function test(){
-
var arr = [];
-
for(var i = 0; i < 10; i ++){
-
(function(j){
-
arr[i] = function (){
-
console.log(j + " ");
-
}
-
}(i))
-
}
-
return arr;
-
}
-
var myArr = test();
-
for(var j = 0; j < 10; j++){
-
myArr[j]();
-
}
-
// 輸出結(jié)果 0 1 2 3 4 5 6 7 8 9
大家可以自行思考一下。
閉包
閉包的現(xiàn)象:當(dāng)內(nèi)部函數(shù)保存到外部時會產(chǎn)生閉包。
閉包會導(dǎo)致原有的作用域鏈不釋放,造成內(nèi)存泄漏
(內(nèi)存泄漏:內(nèi)存占用(比如:手握沙子,握得越緊手里剩得就越少))
閉包觸發(fā)的情況:
兩個或多個函數(shù)互相嵌套,把里面的函數(shù)保存到外部,這樣的情況一定會產(chǎn)生閉包。從外面還可以調(diào)用里面的函數(shù)。
閉包的作用:
實現(xiàn)公有變量
eg:函數(shù)累加器
可以做緩存(存儲結(jié)構(gòu))
eg:eater
可以實現(xiàn)封裝,屬性私有化
eg:person()
模塊化開發(fā),防止污染全局變量
-
// 函數(shù)累加器
-
function add(){
-
var count = 0;
-
function demo(){
-
count ++;
-
console.log(count);
-
}
-
return demo;
-
}
-
var counter = add();
-
counter();
-
counter();
-
counter();
-
counter();
-
counter();
-
counter();
-
-
-
// eater
-
function test(){
-
var food = "apple";
-
var obj = {
-
eatFood : function (){
-
if(food != ""){
-
console.log("I am eating " + food);
-
food = "";
-
}
-
else{
-
console.log("There is nothing!");
-
}
-
},
-
pushFood : function (myFood){
-
food = myFood;
-
}
-
}
-
return obj;
-
}
-
var person = test();
-
person.eatFood();
-
person.eatFood();
-
person.pushFood('banana');
-
person.eatFood();
附加一個逗號操作符:
先看前面的表達(dá)式,再看后面的表達(dá)式,把后面表達(dá)式的計算結(jié)構(gòu)返回
例題: