最近在學JS,前幾天看到兩道題,剛開始看懵懵懂懂,這幾天經過各類查資料,慢慢的理解,頓悟了,對匿名函數,閉包,當即執行函數的理解也更深了一點,在此分享給你們個人理解與總結,但願能幫助你們理解.由於這篇文章是我用心總結的,查閱了不少的資料,因此總結的比較細,篇幅較長,若是沒耐心,建議跳出,點個收藏,之後若是要用到,有耐心想看時,方便查閱.另外若是有啥錯誤,還望指正前端
function fn() {
for (var i = 0; i < 2; i++) {
var variate = i;
setTimeout(function () {
console.log("setTimeout執行後:" + variate);
}, 1000);
}
console.log(i);
}
fn();
複製代碼
最後結果是啥呢? es6
分析完了,先不急,咱們先來了解下setTimeout事件編程
理解這個後,答案就很容易得出了瀏覽器
function fn() {
for (var i = 0; i < 2; i++) {
(function () {
var variate = i;
setTimeout(function () {
alert(variate);
}, 1000);
})();
}
console.log(i);
console.log(variate);
}
fn();
複製代碼
先分析下總體結構: 函數體內包含一個for循環體,循環體內又包含一個匿名函數,造成閉包,加上兩個小括號-->(匿名函數)()造成當即執行函數bash
再思考下函數執行過程閉包
i=0時,進入函數體內,由於是當即執行,因此i值進入匿名函數,經過做用域鏈,變量variate得到i值,匿名函數體內的setTimeout中的變量variate得到i值,第一輪循環結束;異步
i=1時,執行與1一樣的過程;函數
i=2,跳出循環,打印i,variate;ui
結果是啥呢? spa
Excuse me?居然有錯誤?
好,那就讓咱們來解決錯誤,錯誤顯示variate is not defined,原來是這樣,沒定義,那分析一波,爲何會顯示未定義呢? 首先咱們看函數內部,內部已經定義了,因此咱們想到做用域的問題
變量和函數的訪問區域,分全局做用域和函數做用域,在es6中添加let關鍵字後有了塊級做用域概念.
變量提高: JS在解析代碼前會先將全部函數體內的變量,提高至函數體頂端,來看個例子
var Gscope = "global";
function t() {
var Gscope;
console.log("這是全局變量:"+Gscope);//這是全局變量:undefined
let Lscope = "local";
console.log("這是局部變量"+Lscope);//這是局部變量local
}
t();
複製代碼
爲何第一個值爲undefined?由於函數體內的Gscope變量被提高至函數體頂端,可是未賦值,so,undefined.
let關鍵字:let用於聲明變量,可是let聲明的變量只在let所在的代碼塊(塊級做用域)有用,OK,show code
for (let i = 0; i < 2; i++) {
let i = 'a';
console.log(i);//a a
}
console.log(i);//i is not defined
複製代碼
什麼是做用域鏈?有什麼用途?怎麼建立起來的?
先引用一句高級程序設計裏的話:
做用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象
個人理解是:
做用域鏈就至關因而溝通執行環境內的各個變量與函數的橋樑,經過做用域鏈,同一執行環境裏面的變量和函數都有權利訪問對方;
那不一樣的執行環境間是怎樣的呢?
不一樣執行環境間的交流仍是經過橋樑(做用域鏈),可是如今橋樑變成單行道了,只能容許內部環境訪問外部環境,但外部環境不能訪問內部環境.內部環境經過橋樑可以向上搜索查詢變量和函數,但外部卻不能向下搜索進入另外一個執行環境.理解這個後,出現題目二的問題,variate is not defined,就很容易理解了:
由於他們兩個壓根不在同一個執行環境,並且,裏面的變量對象經過閉包可以訪問外部環境變量,但外部環境變量無權訪問內部的變量variate.
這時可能又蹦出一個問題了,"橋樑"(執行環境的做用域鏈)怎麼搭建起來的呢?
分析完後,再從新閱讀下做用域的概念,會發現頗有道理!
首先提出幾個問題:什麼是閉包? 爲何要用它?它有啥缺點?怎麼建立?
什麼是閉包?
閉包是指有權訪問另外一個函數做用域中變量的函數
先貼上剛剛那一段代碼
function fn() {
for (var i = 0; i < 2; i++) {
(function () {
var variate = i;
setTimeout(function () {
console.log("setTimeout執行後:"+variate);
}, 1000);
})();//閉包,當即執行函數,匿名函數
}
console.log(i);//2
console.log(variate);//variate is not defined
}
fn();
複製代碼
經過定義能夠知道,閉包本質仍是做用域鏈的問題. 那爲何內部環境能訪問外部環境呢? 那就先探討下,函數調用時會發生什麼吧!
這個例子中函數fn()內部嵌套了一個匿名函數造成閉包,內部的variate變量變爲私有成員變量,因此外部沒法訪問,於是會報錯variate is not defined
爲何用閉包?
閉包有啥缺點呢?
怎麼建立閉包? 在函數內部嵌套使用函數
什麼是匿名函數? 顧名思義,就是沒有名字的函數 如例子中的代碼就是一個匿名函數
function () {
var variate = i;
setTimeout(function () {
console.log("setTimeout執行後:"+variate);
}, 1000);
}
複製代碼
匿名函數優缺點?
優勢:能夠經過var關鍵字建立函數表達式,函數表達式不會出現變量提高的狀況,只有在真正被解釋執行的時候纔會執行到函數表達式所在的代碼行,有效避免了全局污染;
缺點:匿名函數綁定的事件不能解綁
什麼是當即執行函數?有什麼做用?
什麼是當即執行函數?
聲明一個匿名函數,而且立刻調用它{經過加()的形式}
當即執行函數的形式
(匿名函數)();
(function () {
var variate = i;
setTimeout(function () {
console.log("setTimeout執行後:"+variate);
}, 1000);
})()
複製代碼
爲何要用小括號將匿名函數包裹起來?
爲了經過瀏覽器的語法檢查
做用? 建立一個獨立的做用域,避免全局污染
經過兩道題擴展出來知識點,而且總結出來,如今對知識點的基礎概念,以及一些實現原理有了很清晰的認識,這種感受很棒