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