摘要:前言最近在學前幾天看到兩道題剛開始看懵懵懂懂這幾天通過各種查資料慢慢的理解頓悟了對匿名函數閉包立即執行函數的理解也更深了一點在此分享給大家我的理解與總結希望能幫助大家理解因為這篇文章是我用心總結的查閱了很多的資料所以總結的比較細篇幅較長如果
前言
最近在學JS,前幾天看到兩道題,剛開始看懵懵懂懂,這幾天通過各種查資料,慢慢的理解,頓悟了,對匿名函數,閉包,立即執行函數的理解也更深了一點,在此分享給大家我的理解與總結,希望能幫助大家理解.因為這篇文章是我用心總結的,查閱了很多的資料,所以總結的比較細,篇幅較長,如果沒耐心,建議跳出,點個收藏,以后如果要用到,有耐心想看時,方便查閱.另外如果有啥錯誤,還望指正
題目一function fn() { for (var i = 0; i < 2; i++) { var variate = i; setTimeout(function () { console.log("setTimeout執行后:" + variate); }, 1000); } console.log(i); } fn();
最后結果是啥呢?
結果是,先打印2,再打印2個1
為什么呢?
先來梳理下函數執行過程:
首先for循環遍歷i,(0,1)的時候分別將遍歷值傳給variate變量,variate變量最后保存的值為1
當i值為2時,指針跳出循環,執行到打印i值這步,此時i=2
執行函數fn(),執行完畢后,觸發setTimeout事件,因為循環2次,而且最后保存在這個作用域中變量的值為1,所以最后輸出2個1
所以最后的打印的值為2,1,1
分析完了,先不急,我們先來了解下setTimeout事件
setTimeout事件setTimeout事件有兩個參數:事件,時間開始執行時間
setTimeout事件是異步的
當調用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循環體,循環體內又包含一個匿名函數,形成閉包,加上兩個小括號-->(匿名函數)()形成立即執行函數
再思考下函數執行過程
i=0時,進入函數體內,因為是立即執行,所以i值進入匿名函數,通過作用域鏈,變量variate獲得i值,匿名函數體內的setTimeout中的變量variate獲得i值,第一輪循環結束;
i=1時,執行與1同樣的過程;
i=2,跳出循環,打印i,variate;
結果是啥呢?
Excuse me?竟然有錯誤?
好,那就讓我們來解決錯誤,錯誤顯示variate is not defined,原來是這樣,沒定義,那分析一波,為什么會顯示未定義呢?
首先我們看函數內部,內部已經定義了,所以我們想到作用域的問題
作用域
變量和函數的訪問區域,分全局作用域和函數作用域,在es6中添加let關鍵字后有了塊級作用域概念.
變量提升: JS在解析代碼前會先將所有函數體內的變量,提升至函數體頂端,來看個例子
var Gscope = "global"; function t() { var Gscope; console.log("這是全局變量:"+Gscope);//這是全局變量:undefined 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.
這時可能又蹦出一個問題了,"橋梁"(執行環境的作用域鏈)怎么搭建起來的呢?
先創建一個預先包含全局變量對象的作用域鏈,保存在內部的[scope]屬性中
調用函數時,為函數搭建一個執行環境
復制函數的[scope]中的對象構建起執行環境的作用域鏈
創建活動對象,并將活動對象推入執行環境的前端
分析完后,再重新閱讀下作用域的概念,會發現很有道理!
閉包首先提出幾個問題:什么是閉包? 為什么要用它?它有啥缺點?怎么創建?
什么是閉包?
閉包是指有權訪問另一個函數作用域中變量的函數
先貼上剛剛那一段代碼
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();
通過定義可以知道,閉包本質還是作用域鏈的問題.
那為什么內部環境能訪問外部環境呢?
那就先探討下,函數調用時會發生什么吧!
先創建執行環境和作用域鏈;
初始化函數的活動對象(命名參數值,arguments);
在作用鏈中搜索具有相應名字的變量,實現對變量的讀取和寫入;
調用執行完畢,銷毀局部活動對象,僅保存全局作用域.
所以關鍵還是內部函數作用域鏈將外部的活動對象添加到自己作用域中了
這個例子中函數fn()內部嵌套了一個匿名函數形成閉包,內部的variate變量變為私有成員變量,所以外部無法訪問,因而會報錯variate is not defined
為什么用閉包?
因為在閉包內部保持了對外部活動對象的訪問,但外部的變量卻無法直接訪問內部,避免了全局污染;
可以當做私有成員,彌補了因js語法帶來的面向對象編程的不足;
可以長久的在內存中保存一個自己想要保存的變量.
閉包有啥缺點呢?
可能導致內存占用過多,因為閉包攜帶了自身的函數作用域
閉包只能取得外部包含函數中得最后一個值
怎么創建閉包?
在函數內部嵌套使用函數
什么是匿名函數?
顧名思義,就是沒有名字的函數
如例子中的代碼就是一個匿名函數
function () { var variate = i; setTimeout(function () { console.log("setTimeout執行后:"+variate); }, 1000); }
匿名函數優缺點?
優點:可以通過var關鍵字創建函數表達式,函數表達式不會出現變量提升的情況,只有在真正被解釋執行的時候才會執行到函數表達式所在的代碼行,有效避免了全局污染;
缺點:匿名函數綁定的事件不能解綁
立即執行函數什么是立即執行函數?有什么作用?
什么是立即執行函數?
聲明一個匿名函數,并且馬上調用它{通過加()的形式}
立即執行函數的形式
(匿名函數)();
(function () { var variate = i; setTimeout(function () { console.log("setTimeout執行后:"+variate); }, 1000); })()
為什么要用小括號將匿名函數包裹起來?
為了通過瀏覽器的語法檢查
作用?
創建一個獨立的作用域,避免全局污染
通過兩道題擴展出來知識點,并且總結出來,現在對知識點的基礎概念,以及一些實現原理有了很清晰的認識,這種感覺很棒
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94539.html
摘要:也就是說,普通情況下,指向調用函數時的對象。在全局執行時,則是全局對象。故而的方法因為構造函數閉包的關系,指向了構造函數作用域內的。 日常開發中,我們經常用到this。例如用Jquery綁定事件時,this指向觸發事件的DOM元素;編寫Vue、React組件時,this指向組件本身。對于新手來說,常會用一種意會的感覺去判斷this的指向。以至于當遇到復雜的函數調用時,就分不清this的...
摘要:執行返回的內部函數,依然能訪問變量輸出閉包中的作用域鏈理解作用域鏈對理解閉包也很有幫助。早期的版本里采用是計數的垃圾回收機制,閉包導致內存泄露的一個原因就是這個算法的一個缺陷。 關于閉包,我翻了幾遍書,看了幾遍視頻,查了一些資料,可是還是迷迷糊糊的,干脆自己動手來個總結吧 !歡迎指正... (~ o ~)~zZ 1. 什么是閉包? 來看一些關于閉包的定義: 閉包是指有權...
摘要:到底什么是閉包這個問題在面試是時候經常都會被問,很多小白一聽就懵逼了,不知道如何回答好。上面這么說閉包是一種特殊的對象。閉包的注意事項通常,函數的作用域及其所有變量都會在函數執行結束后被銷毀。從而使用閉包模塊化代碼,減少全局變量的污染。 閉包,有人說它是一種設計理念,有人說所有的函數都是閉包。到底什么是閉包?這個問題在面試是時候經常都會被問,很多小白一聽就懵逼了,不知道如何回答好。這個...
摘要:閉包占用大量內存通常,函數的作用域及其所有的變量都會在函數執行結束后被銷毀。也就是說,可以通過閉包創建私有作用域將某些變量作為局部變量,避免使用全局變量而占用過多的內存。 JavaScript——閉包理解 1、閉包是什么,如何使用? 閉包指的是函數對象可以通過作用域鏈相互關聯起來,函數體內部的變量都可以保存在函數作用域內,也就是說閉包有權訪問另一個函數作用域中的變量的函數。 下面是一...
摘要:一般來講,函數執行完畢后,局部活動對象就會被銷毀,內存中僅保存全局作用域,但是閉包的情況有所不同理解閉包的前提先理解另外兩個內容作用域鏈垃圾回收作用域鏈當代碼在執行過程中,會創建變量對象的一個作用域鏈。 閉包是javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包來實現。個人的理解是:函數中嵌套函數。 閉包的定義及其優缺點 閉包是指有權訪問另一個函數作用域中的變量的...
閱讀 2975·2023-04-26 02:04
閱讀 1283·2021-11-04 16:07
閱讀 3707·2021-09-22 15:09
閱讀 682·2019-08-30 15:54
閱讀 1904·2019-08-29 14:11
閱讀 2529·2019-08-26 12:19
閱讀 2259·2019-08-26 12:00
閱讀 760·2019-08-26 10:27