天天看點

JavaScript 數組拼接列印_JavaScript核心之函數

本部落格介紹的内容如下:

1、函數的五種聲明方式;

2、函數的name屬性;

3、函數的本質;

4、Function和function的差別;

5、this和arguments;

6、call stack調用棧;

7、作用域;

8、什麼是閉包;

9、逗号操作符

1、函數的五種聲明方式

函數​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

聲明函數用到關鍵字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 
           

列印這個名字,會顯示為函數

JavaScript 數組拼接列印_JavaScript核心之函數

(2)匿名函數

function 
           

但是

匿名函數不能單獨使用,如果單獨使用就會報錯
JavaScript 數組拼接列印_JavaScript核心之函數

匿名函數需要指派給另一個變量

var 
           

這樣就不會報錯啦

JavaScript 數組拼接列印_JavaScript核心之函數

(3)具名函數指派給一個變量

var 
           

它與直接用具名函數不指派給一個變量的差別是

函數名字y已經無法列印了,會報錯。 這是JavaScript的不一緻性,這需要強行記憶。
JavaScript 數組拼接列印_JavaScript核心之函數

此時y的作用域被限制在自己函數裡面。

JavaScript 數組拼接列印_JavaScript核心之函數

(4)用window.Function函數構造函數指派給一個變量

Function​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

該方法很少用。以下代碼的new可以删除也是一樣的效果,因為Function是複雜類型的對象,是以加不加new都不影響它是一個對象。

new 
           

寫入三個參數,而且全是字元串形式傳入

JavaScript 數組拼接列印_JavaScript核心之函數

如果指派給一個變量後,傳入兩個參數就可以使用啦

JavaScript 數組拼接列印_JavaScript核心之函數

如果return裡面傳入一個值,比如

var n=1
f=new Function('x','y','return x+'+n+'+y')
           

這樣就可以把n的聲明和指派帶入到函數裡面,函數裡面的n就會變成1,傳入參數1,2後會得到4

JavaScript 數組拼接列印_JavaScript核心之函數

如果把n兩邊的單引号變成雙引号就會成為一個字元串

JavaScript 數組拼接列印_JavaScript核心之函數

如果把n兩邊的引号去掉,那麼函數裡面就是n本身,但是傳入參數1,2之後結果還是4

JavaScript 數組拼接列印_JavaScript核心之函數

MDN上不推薦此方法來構造函數

JavaScript 數組拼接列印_JavaScript核心之函數

(5)箭頭建立匿名函數

箭頭函數​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數
箭頭函數都是匿名的,需要指派給一個變量

可以省略很多代碼比如

sum
           

使用sum(1,2)就可以得到3

JavaScript 數組拼接列印_JavaScript核心之函數

如果花括号{}裡面隻有一句話,一個return,那麼就可以

同時且必須同時删除花括号{}和return使用簡寫。

比如

sum=(x,y)=>x+y
           

這樣代碼變簡單很多

JavaScript 數組拼接列印_JavaScript核心之函數

如果參數隻有一個,

比如隻有一個參數n,就還可以繼續簡化,把小括号()也删除掉

,比如

sum=n=>n*n
           

這樣就更加簡單了

JavaScript 數組拼接列印_JavaScript核心之函數

箭頭函數在函數和箭頭之間不能換行,不然會報錯

JavaScript 數組拼接列印_JavaScript核心之函數

箭頭函數大括号裡面如果有多行,可以用分号分隔

JavaScript 數組拼接列印_JavaScript核心之函數

2、函數的name屬性

Function.name​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

name屬性傳回的都是以字元串形式傳回的。

(1)具名函數的name就是

"函數的名字"
JavaScript 數組拼接列印_JavaScript核心之函數

(2)匿名函數指派給一個變量後,它的name是這個

"變量名"
JavaScript 數組拼接列印_JavaScript核心之函數

(3)具名函數指派給一個變量後,它的name屬性是這個

"函數的名字"
JavaScript 數組拼接列印_JavaScript核心之函數

(4)用new Function構造的函數指派給一個變量後,它的name屬性是

"anonymous"
JavaScript 數組拼接列印_JavaScript核心之函數

anonymous這個單詞就叫做匿名的意思。

3、函數的本質

函數是一種對象,就是一段能否反複調用( 調用的英語叫call)的代碼塊,函數還能接受不同的參數,不同的參數傳回不同的值。

簡單來說就是一種可以執行代碼的對象,就叫做函數。

一段很長的代碼來求三角形的面積,比如

JavaScript 數組拼接列印_JavaScript核心之函數

我們用函數包括進來,然後傳入兩個參數就可以直接調用函數來求三角形面積,而且還可以

反複傳入參數來調用求三角形面積。
JavaScript 數組拼接列印_JavaScript核心之函數

那麼函數在記憶體中是如何存儲的?是跟簡單類型一樣存儲,還是與複雜類型對象存位址呢?

通過使用toString()可以看到他就把自己當做字元串來儲存,跟對象得到的"[object Object]"不太一樣。

JavaScript 數組拼接列印_JavaScript核心之函數

記憶體圖中的顯示可以看到

函數它的公用屬性裡面有一個call()方法

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

可以用一個普通對象來是想函數的效果,便于了解,這其中需要用到

Java​Script 标準庫裡面

eval()這個函數屬性。

eval()是全局對象的一個函數屬性。

eval()​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

通過eval()就

直接執行獲得最後的值

JavaScript 數組拼接列印_JavaScript核心之函數

如果用console.log()隻能到他的字元串,說明這裡并不能執行。

JavaScript 數組拼接列印_JavaScript核心之函數

是以函數也屬于對象,

函數的調用過程就是eval()函數體的過程,也就是把函數體作為一段代碼執行的過程

JavaScript 數組拼接列印_JavaScript核心之函數

是以我們引出了f.call()。

那麼f(1,2)和f.call(undefined,1,2)有什麼不同呢?

他的不同在于f.call(undefined,1,2)可以看出this是什麼。

而他的使用方法就是第一個參數需要一個undefined,然後後面傳入相應的參數,比如

JavaScript 數組拼接列印_JavaScript核心之函數

4、Function和function的差別

(1)function

關鍵字

,類似的關鍵字還有var、if、else等;

ECMAScript 關鍵字​www.w3school.com.cn JavaScript 保留關鍵字​www.w3cschool.cn

JavaScript 數組拼接列印_JavaScript核心之函數

它的作用是第一章已經說到過,聲明函數用到關鍵字function,它與var一樣都是關鍵字,用于

聲明一個對象

,該對象就是函數對象。

與var不同的是它

隻能聲明函數

,而var可以聲明七種資料類型的變量。

(2)Function

ECMAScript Function 對象(類)​www.w3school.com.cn Function​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

他可以作為

一個全局對象

,也可以作為

一個構造函數

,也是window全局對象可以直接通路到它——window.Function。

隻是剛好Function也可以按照他的Function構造函數來

構造一個函數對象

然後用一個變量(比如var f)來引用它。

下面可加new,也可以不加new,因為他是一個複雜類型的對象。

var 
           

前面也說明過MDN不推薦此方法來構造函數。

JavaScript 數組拼接列印_JavaScript核心之函數

5、this和arguments

Arguments 對象​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

this​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

前面章節說到f.call(undefined,1,2)2)可以看出this和arguments是什麼。

this可以得到call的第一個參數undefined;

用arguments可以得到call的第一個參數後面的參數[1,2]。

JavaScript 數組拼接列印_JavaScript核心之函數

(1)arguments

Arguments 對象​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

第一個參數後面的會組成一個僞數組。僞數組說明見前面的部落格:

bomber:JavaScript數組及部分API介紹​zhuanlan.zhihu.com

JavaScript 數組拼接列印_JavaScript核心之函數
JavaScript 數組拼接列印_JavaScript核心之函數

(2)this

函數的this關鍵字在 JavaScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差别。

this​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

(2-1)this在非嚴格模式(一般模式)中

第一個參數是undefined或null會轉換為window全局函數

undefined

JavaScript 數組拼接列印_JavaScript核心之函數

null

JavaScript 數組拼接列印_JavaScript核心之函數

第一個參數是數字、布爾、字元串會轉換為new來包裝的對象

bomber:《Global Object、公用屬性(原型)、prototype和__proto__》​zhuanlan.zhihu.com

JavaScript 數組拼接列印_JavaScript核心之函數

數字

JavaScript 數組拼接列印_JavaScript核心之函數

布爾

JavaScript 數組拼接列印_JavaScript核心之函數

字元串

JavaScript 數組拼接列印_JavaScript核心之函數

(2-2)this在嚴格模式中

嚴格模式​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

放一個特定字元串語句 "use strict"或'use strict'就可以啟動嚴格模式

該模式下面,this不會轉換,call的第一個參數寫的是什麼,就是什麼。

JavaScript 數組拼接列印_JavaScript核心之函數

6、call stack調用棧

Call stack​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

調用棧是解釋器(就像浏覽器中的javascript解釋器)追蹤函數執行流的一種機制。當執行環境中調用了多個函數時,通過這種機制,我們能夠追蹤到哪個函數正在執行,執行的函數體中又調用了哪個函數。

它叫做棧,在前面部落格的資料結構中有說到,

棧就是先進後出

的。

bomber:算法和資料結構​zhuanlan.zhihu.com

JavaScript 數組拼接列印_JavaScript核心之函數
function 
           
  • 調用 一個函數,解釋器就會把該函數添 加進調用棧 并開始 執行
  • 正在調用棧中執行的函數 還調用 了其它函數,那麼 新函數 也将會 被添加進調用棧 ,一旦這個函數 被調用 ,便會 立即執行
  • 目前函數 執行完畢後 ,解釋器将其 清出調用棧 ,繼續 執行 目前執行環境下的 剩餘的代碼

從圖形中來看

JavaScript 數組拼接列印_JavaScript核心之函數

一開始,我們得到一個空空如也的調用棧。随後,每當有函數被調用都會自動地添加進調用棧,執行完函數體中的代碼後,調用棧又會自動地移除這個函數。最後,我們又得到了一個空空如也的調用棧。

簡約的來說就是調用=>被調用的函數加入調用棧=>執行剩餘代碼=>把該函數從調用棧中清除,

并且先進入調用棧的後清除

也可以通過列印的順序來了解

function 
           

運作上面代碼後,從代碼輸出也可以看到先進去的後出來

JavaScript 數組拼接列印_JavaScript核心之函數
函數裡面調用函數自己,就可以實作遞歸,也就是有多個一樣的函數壓入調用棧
function 
           

輸入參數就會顯示結果

JavaScript 數組拼接列印_JavaScript核心之函數

每台電腦的棧的都有一定的容量,

如果壓入的次數太多就會爆掉,就是stack overflow

比如我的電腦在9666次沒有報錯,但是9667次就報錯了。

JavaScript 數組拼接列印_JavaScript核心之函數

也有一個關于stack overflow的網站,專門用來讨論各種語言stack overflow或者程式設計的BUG 的問答

Stack Overflow - Where Developers Learn, Share, & Build Careers​stackoverflow.com

JavaScript 數組拼接列印_JavaScript核心之函數

另一個C語言報錯的叫做segment fault,也有一個開發者社群叫做segment fault

人類身份驗證 - SegmentFault​segmentfault.com

三種調用棧的動态示範

普通調用

普通調用​latentflip.com

嵌套調用

嵌套調用​latentflip.com

遞歸調用

遞歸調用​latentflip.com

7、作用域

作用域屬于前面算法與結構部落格中的樹。

(1)下面左邊代碼中,用圖形表示的紅色和藍色框很像一個樹的結構

1、如果在12行裡面寫了a=3,就代表在全局範圍(作用域)裡面寫的a=3;

2、如果在3到4行之間裡面插入一句a=3,就代表在f1範圍(作用域)裡面寫的a=3。

JavaScript 數組拼接列印_JavaScript核心之函數
(2)如果f1中沒有寫var

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指派。

JavaScript 數組拼接列印_JavaScript核心之函數
(3)如果f1中沒有寫var,并且全局作用域中也沒有聲明a,也就是var a

1、如果把第三行的var删除掉,隻留下a=2,

并且全局範圍(作用域)中沒有var a

,那麼這裡的a是它的樹形結構中全局範圍(作用域)裡面的

聲明a并指派為2

2、a=2優先會找目前所在的f1範圍(作用域)裡面有沒有a的申明,也就是var a,這裡很明顯沒有,是以就去它的父範圍(作用域)找a的聲明,也就是var a,這裡的

父範圍(作用域)是全局範圍(作用域),裡面也沒有a的聲明

,是以是給全局範圍(作用域)的a

聲明并指派為2
JavaScript 數組拼接列印_JavaScript核心之函數

從這個代碼中所在的區域,可以劃分為三個分區1,2,3,并且找聲明都是按照往父級區域中

最近原則

的優先級别找聲明,到最外層的全局範圍(作用域),

如果找到了聲明就代表是該聲明的指派

,如果

找不到聲明

,就說明是給全局範圍(作用域)

聲明并指派

JavaScript 數組拼接列印_JavaScript核心之函數
(4)第一次變形後的代碼看看執行到第五行代碼的時候列印的a是什麼?
var 
           

圖形的第五行

JavaScript 數組拼接列印_JavaScript核心之函數

通過控制台打出來可以看到,這是的答案是undefined

JavaScript 數組拼接列印_JavaScript核心之函數

其實這裡需要考慮到

聲明提升
JavaScript 數組拼接列印_JavaScript核心之函數
(5)第二次變形後,看看console.log(a)列印出應該是什麼?
var 
           

打出來可以看到結果是2

JavaScript 數組拼接列印_JavaScript核心之函數

如果把函數function f4放到全局範圍的下面

var 
           

打出來可以看到結果是1

JavaScript 數組拼接列印_JavaScript核心之函數

這就是前面說的

樹形結構的作用域,子作用域的的a隻在自身範圍或者父作用域有效

(6)第三次變形後,如果f4.call()前面有一句代碼把前面的var a=1 的a指派覆寫掉了,看看console.log(a)列印出應該是什麼?
var 
           

我們可以看到這裡如果被a=2覆寫,那麼列印的結果是2

JavaScript 數組拼接列印_JavaScript核心之函數

是以這裡f4裡面的console.log(a)裡面的

a是第一行的var a,但是它的值是後面的a=2

跟我們用for循環的時候一樣的情況:

JavaScript 數組拼接列印_JavaScript核心之函數

在點選所有選項列印的i結果都是6,

JavaScript 數組拼接列印_JavaScript核心之函數

https://www.zhihu.com/video/1104117865623502848

因為i會聲明提升為全局變量,後面的指派會把前面的指派覆寫,全局變量最後一個指派就是6,那麼點選所有的選項結果都是6。

for循環,在onclick之前已經循環完了,最後一個值就是6

也就是使用者點選的速度肯定比電腦處理的速度要慢。因為電腦是的速度是納秒級别

JavaScript 數組拼接列印_JavaScript核心之函數

聲明提升後的效果

JavaScript 數組拼接列印_JavaScript核心之函數

https://www.zhihu.com/video/1104118185065795584

當然這裡如果改用let,那麼結果就是每次點選的索引下标了,因為let作用在代碼塊内有效,目前的i隻在本輪循環有效,是以每一次循環的i其實都是一個新的變量,是以最後輸出的就是你點選的選項的索引下标。

JavaScript 數組拼接列印_JavaScript核心之函數

https://www.zhihu.com/video/1104119313140645888

8、什麼是閉包

閉包​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數

如果一個函數,使用了它範圍(作用域)外的變量,那麼(這個函數+這個變量)就叫做閉包)

JavaScript 數組拼接列印_JavaScript核心之函數

詳細請看方方的部落格:

方應杭:「每日一題」JS 中的閉包是什麼?​zhuanlan.zhihu.com

JavaScript 數組拼接列印_JavaScript核心之函數

9、逗号操作符

逗号操作符​developer.mozilla.org

JavaScript 數組拼接列印_JavaScript核心之函數
逗号操作符

對它的每個操作數求值(從左到右),并傳回最後一個操作數的值。

JavaScript 數組拼接列印_JavaScript核心之函數

更多函數相關知識可以檢視

你真的懂函數嗎 - 寫代碼啦!​xiedaimala.com

本文為本人的原創文章,著作權歸本人和饑人谷所有,轉載務必注明來源

繼續閱讀