本部落格介紹的内容如下:
1、函數的五種聲明方式;
2、函數的name屬性;
3、函數的本質;
4、Function和function的差別;
5、this和arguments;
6、call stack調用棧;
7、作用域;
8、什麼是閉包;
9、逗号操作符
1、函數的五種聲明方式
函數developer.mozilla.org

聲明函數用到關鍵字function,它與var一樣都是關鍵字,用于聲明一個對象,該對象就是函數對象。
與var不同的是它隻能聲明函數,而var可以聲明七種資料類型。
從console.log()的源代碼來了解一個函數。console.log()其實就是
随便列印一個資訊,然後傳回undefined
console
此時如果,輸入console.log(1)就會列印出1,然後傳回undefined。該函數
永遠傳回的是undefined,
它的傳回值和列印出的資訊沒有任何關系。
所有函數都會有一個return undefined,傳回值為undefined,
return如果不寫,函數會預設自動加上return undefined。
函數可以不傳入參數,但是必須有return,忘記寫return,預設也會加上return undefined。
(1)具名函數
function
列印這個名字,會顯示為函數
(2)匿名函數
function
但是
匿名函數不能單獨使用,如果單獨使用就會報錯匿名函數需要指派給另一個變量
var
這樣就不會報錯啦
(3)具名函數指派給一個變量
var
它與直接用具名函數不指派給一個變量的差別是
函數名字y已經無法列印了,會報錯。 這是JavaScript的不一緻性,這需要強行記憶。此時y的作用域被限制在自己函數裡面。
(4)用window.Function函數構造函數指派給一個變量
Functiondeveloper.mozilla.org

該方法很少用。以下代碼的new可以删除也是一樣的效果,因為Function是複雜類型的對象,是以加不加new都不影響它是一個對象。
new
寫入三個參數,而且全是字元串形式傳入
如果指派給一個變量後,傳入兩個參數就可以使用啦
如果return裡面傳入一個值,比如
var n=1
f=new Function('x','y','return x+'+n+'+y')
這樣就可以把n的聲明和指派帶入到函數裡面,函數裡面的n就會變成1,傳入參數1,2後會得到4
如果把n兩邊的單引号變成雙引号就會成為一個字元串
如果把n兩邊的引号去掉,那麼函數裡面就是n本身,但是傳入參數1,2之後結果還是4
MDN上不推薦此方法來構造函數
(5)箭頭建立匿名函數
箭頭函數developer.mozilla.org

可以省略很多代碼比如
sum
使用sum(1,2)就可以得到3
如果花括号{}裡面隻有一句話,一個return,那麼就可以
同時且必須同時删除花括号{}和return使用簡寫。比如
sum=(x,y)=>x+y
這樣代碼變簡單很多
如果參數隻有一個,
比如隻有一個參數n,就還可以繼續簡化,把小括号()也删除掉,比如
sum=n=>n*n
這樣就更加簡單了
箭頭函數在函數和箭頭之間不能換行,不然會報錯
箭頭函數大括号裡面如果有多行,可以用分号分隔
2、函數的name屬性
Function.namedeveloper.mozilla.org

name屬性傳回的都是以字元串形式傳回的。
(1)具名函數的name就是
"函數的名字"(2)匿名函數指派給一個變量後,它的name是這個
"變量名"(3)具名函數指派給一個變量後,它的name屬性是這個
"函數的名字"(4)用new Function構造的函數指派給一個變量後,它的name屬性是
"anonymous"anonymous這個單詞就叫做匿名的意思。
3、函數的本質
函數是一種對象,就是一段能否反複調用( 調用的英語叫call)的代碼塊,函數還能接受不同的參數,不同的參數傳回不同的值。
簡單來說就是一種可以執行代碼的對象,就叫做函數。
一段很長的代碼來求三角形的面積,比如
我們用函數包括進來,然後傳入兩個參數就可以直接調用函數來求三角形面積,而且還可以
反複傳入參數來調用求三角形面積。那麼函數在記憶體中是如何存儲的?是跟簡單類型一樣存儲,還是與複雜類型對象存位址呢?
通過使用toString()可以看到他就把自己當做字元串來儲存,跟對象得到的"[object Object]"不太一樣。
記憶體圖中的顯示可以看到
函數它的公用屬性裡面有一個call()方法:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/calldeveloper.mozilla.org
可以用一個普通對象來是想函數的效果,便于了解,這其中需要用到
JavaScript 标準庫裡面的
eval()這個函數屬性。eval()是全局對象的一個函數屬性。
eval()developer.mozilla.org

通過eval()就
直接執行獲得最後的值。
如果用console.log()隻能到他的字元串,說明這裡并不能執行。
是以函數也屬于對象,
函數的調用過程就是eval()函數體的過程,也就是把函數體作為一段代碼執行的過程。
是以我們引出了f.call()。
那麼f(1,2)和f.call(undefined,1,2)有什麼不同呢?
他的不同在于f.call(undefined,1,2)可以看出this是什麼。而他的使用方法就是第一個參數需要一個undefined,然後後面傳入相應的參數,比如
4、Function和function的差別
(1)function
是
關鍵字,類似的關鍵字還有var、if、else等;
ECMAScript 關鍵字www.w3school.com.cn JavaScript 保留關鍵字www.w3cschool.cn
它的作用是第一章已經說到過,聲明函數用到關鍵字function,它與var一樣都是關鍵字,用于
聲明一個對象,該對象就是函數對象。
與var不同的是它
隻能聲明函數,而var可以聲明七種資料類型的變量。
(2)Function
ECMAScript Function 對象(類)www.w3school.com.cn Functiondeveloper.mozilla.org

他可以作為
一個全局對象,也可以作為
一個構造函數,也是window全局對象可以直接通路到它——window.Function。
隻是剛好Function也可以按照他的Function構造函數來
構造一個函數對象。
然後用一個變量(比如var f)來引用它。
下面可加new,也可以不加new,因為他是一個複雜類型的對象。
var
前面也說明過MDN不推薦此方法來構造函數。
5、this和arguments
Arguments 對象developer.mozilla.org

thisdeveloper.mozilla.org

前面章節說到f.call(undefined,1,2)2)可以看出this和arguments是什麼。
this可以得到call的第一個參數undefined;
用arguments可以得到call的第一個參數後面的參數[1,2]。
(1)arguments
Arguments 對象developer.mozilla.org

第一個參數後面的會組成一個僞數組。僞數組說明見前面的部落格:
bomber:JavaScript數組及部分API介紹zhuanlan.zhihu.com
(2)this
函數的this關鍵字在 JavaScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差别。
thisdeveloper.mozilla.org

(2-1)this在非嚴格模式(一般模式)中
第一個參數是undefined或null會轉換為window全局函數
undefined
null
第一個參數是數字、布爾、字元串會轉換為new來包裝的對象
bomber:《Global Object、公用屬性(原型)、prototype和__proto__》zhuanlan.zhihu.com
數字
布爾
字元串
(2-2)this在嚴格模式中
嚴格模式developer.mozilla.org

放一個特定字元串語句 "use strict"或'use strict'就可以啟動嚴格模式
該模式下面,this不會轉換,call的第一個參數寫的是什麼,就是什麼。
6、call stack調用棧
Call stackdeveloper.mozilla.org

調用棧是解釋器(就像浏覽器中的javascript解釋器)追蹤函數執行流的一種機制。當執行環境中調用了多個函數時,通過這種機制,我們能夠追蹤到哪個函數正在執行,執行的函數體中又調用了哪個函數。
它叫做棧,在前面部落格的資料結構中有說到,
棧就是先進後出的。
bomber:算法和資料結構zhuanlan.zhihu.com
function
- 每 調用 一個函數,解釋器就會把該函數添 加進調用棧 并開始 執行 。
- 正在調用棧中執行的函數 還調用 了其它函數,那麼 新函數 也将會 被添加進調用棧 ,一旦這個函數 被調用 ,便會 立即執行 。
- 目前函數 執行完畢後 ,解釋器将其 清出調用棧 ,繼續 執行 目前執行環境下的 剩餘的代碼 。
從圖形中來看
一開始,我們得到一個空空如也的調用棧。随後,每當有函數被調用都會自動地添加進調用棧,執行完函數體中的代碼後,調用棧又會自動地移除這個函數。最後,我們又得到了一個空空如也的調用棧。
簡約的來說就是調用=>被調用的函數加入調用棧=>執行剩餘代碼=>把該函數從調用棧中清除,
并且先進入調用棧的後清除。
也可以通過列印的順序來了解
function
運作上面代碼後,從代碼輸出也可以看到先進去的後出來
function
輸入參數就會顯示結果
每台電腦的棧的都有一定的容量,
如果壓入的次數太多就會爆掉,就是stack overflow比如我的電腦在9666次沒有報錯,但是9667次就報錯了。
也有一個關于stack overflow的網站,專門用來讨論各種語言stack overflow或者程式設計的BUG 的問答
Stack Overflow - Where Developers Learn, Share, & Build Careersstackoverflow.com
另一個C語言報錯的叫做segment fault,也有一個開發者社群叫做segment fault
人類身份驗證 - SegmentFaultsegmentfault.com
三種調用棧的動态示範
普通調用
普通調用latentflip.com
嵌套調用
嵌套調用latentflip.com
遞歸調用
遞歸調用latentflip.com
7、作用域
作用域屬于前面算法與結構部落格中的樹。
(1)下面左邊代碼中,用圖形表示的紅色和藍色框很像一個樹的結構1、如果在12行裡面寫了a=3,就代表在全局範圍(作用域)裡面寫的a=3;
2、如果在3到4行之間裡面插入一句a=3,就代表在f1範圍(作用域)裡面寫的a=3。
1、如果把第三行的var删除掉,隻留下a=2,那麼這裡的a是它的樹形結構中父範圍(作用域)裡面的var a,
因為a=2優先表示他是一個指派操作,把3指派給a,這裡的a是全局範圍(作用域)裡面var聲明的a;
2、a=2
優先會找目前所在的f1範圍(作用域)裡面有沒有a的聲明,也就是var a,這裡很明顯沒有,是以就去它的父範圍(作用域)找a的聲明,也就是var a,
父範圍(作用域)裡面有a的聲明,是以是給全局範圍(作用域)的a指派。
1、如果把第三行的var删除掉,隻留下a=2,
并且全局範圍(作用域)中沒有var a,那麼這裡的a是它的樹形結構中全局範圍(作用域)裡面的
聲明a并指派為2;
2、a=2優先會找目前所在的f1範圍(作用域)裡面有沒有a的申明,也就是var a,這裡很明顯沒有,是以就去它的父範圍(作用域)找a的聲明,也就是var a,這裡的
父範圍(作用域)是全局範圍(作用域),裡面也沒有a的聲明,是以是給全局範圍(作用域)的a
聲明并指派為2從這個代碼中所在的區域,可以劃分為三個分區1,2,3,并且找聲明都是按照往父級區域中
最近原則的優先級别找聲明,到最外層的全局範圍(作用域),
如果找到了聲明就代表是該聲明的指派,如果
找不到聲明,就說明是給全局範圍(作用域)
聲明并指派。
var
圖形的第五行
通過控制台打出來可以看到,這是的答案是undefined
其實這裡需要考慮到
聲明提升var
打出來可以看到結果是2
如果把函數function f4放到全局範圍的下面
var
打出來可以看到結果是1
這就是前面說的
樹形結構的作用域,子作用域的的a隻在自身範圍或者父作用域有效。
(6)第三次變形後,如果f4.call()前面有一句代碼把前面的var a=1 的a指派覆寫掉了,看看console.log(a)列印出應該是什麼?var
我們可以看到這裡如果被a=2覆寫,那麼列印的結果是2
是以這裡f4裡面的console.log(a)裡面的
a是第一行的var a,但是它的值是後面的a=2。
跟我們用for循環的時候一樣的情況:
在點選所有選項列印的i結果都是6,
https://www.zhihu.com/video/1104117865623502848
因為i會聲明提升為全局變量,後面的指派會把前面的指派覆寫,全局變量最後一個指派就是6,那麼點選所有的選項結果都是6。
for循環,在onclick之前已經循環完了,最後一個值就是6
也就是使用者點選的速度肯定比電腦處理的速度要慢。因為電腦是的速度是納秒級别
聲明提升後的效果
https://www.zhihu.com/video/1104118185065795584
當然這裡如果改用let,那麼結果就是每次點選的索引下标了,因為let作用在代碼塊内有效,目前的i隻在本輪循環有效,是以每一次循環的i其實都是一個新的變量,是以最後輸出的就是你點選的選項的索引下标。
https://www.zhihu.com/video/1104119313140645888
8、什麼是閉包
閉包developer.mozilla.org

如果一個函數,使用了它範圍(作用域)外的變量,那麼(這個函數+這個變量)就叫做閉包)
詳細請看方方的部落格:
方應杭:「每日一題」JS 中的閉包是什麼?zhuanlan.zhihu.com
9、逗号操作符
逗号操作符developer.mozilla.org

對它的每個操作數求值(從左到右),并傳回最後一個操作數的值。
更多函數相關知識可以檢視
你真的懂函數嗎 - 寫代碼啦!xiedaimala.com
本文為本人的原創文章,著作權歸本人和饑人谷所有,轉載務必注明來源