iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 12
0
Modern Web

JavaScript Note系列 第 12

Function 函式

JavaScript程式碼是由敘述式(statement)所組成的,每行敘述式的結尾都要加上分號(;)表示結束這段指令。
隨著Web的功能愈複雜,程式碼也愈多,這時我們需要有效地管理程式碼與重複使用。
函式(Function)是由敘述式所組成的區塊,它是為特定目的而存在,當我們輸入值,經過內部運算,會回傳結果。

建立函式有3種比較常見的方式:

  • 函式宣告(declaration)
  • 函式表達式(expression)
  • 箭頭函式(arrow)

函式宣告(declaration)

首先要有關鍵字function,後面接著函式名稱,和用小括號( )包圍參數名稱,參數的用途,是我們要輸入值讓函式執行運算,最後是大括號{ },裡面是函式運算的邏輯,如果需要回傳運算結果,需使用return關鍵字。
以下是最簡單的函式,它的作用是要算出2數的平均,並回傳結果。

function avg(x, y) {
    return (x + y) / 2;
}
console.log(avg(2, 3)); //2.5

函式名稱為avg,參數為x、y,內部只有一行指令,算出平均值,再回傳其結果。
呼叫函式使用也是很簡單,只要輸入名稱和小括號,在小括號中輸入需要計算的值,就可以執行了。

函式表達式(expression)

跟函式宣告差不多,不同的地方在於,沒有函式名稱,並利用指定運算式把函式指定給變數,呼叫的方式跟函式宣告一樣。
因為沒有函式名稱,所以又稱匿名函式。

let avg = function (x, y) {
    return (x + y) / 2;
}
console.log(avg(2, 3)); //2.5

函式宣告(declaration)跟函式表達式(expression)差異:
這兩種表面看起來差不多,用起來也是一樣,最大的差異是建立的方式,一個是宣告的方式,一個是賦值(assignment)的方式,這會導致在hoisting呈現不同的結果,關於這點會在hoisting的章節解說。

箭頭函式(arrow)

以更簡單的方式定義函式,無需關鍵字function,在「=>」運算子的左邊,用小括號將參數包起來,右邊是函式本體,如果只有單行敘述,可省略大括號{ }。

let avg = (x, y) => (x + y) / 2;
console.log(avg(2, 3)); //2.5

可以省略return,此運算式會自動回傳結果。
如果只有一個參數,小括號可省略。

let area = r => Number(r * r * Math.PI).toFixed(2);
console.log(area(5));  //78.54

若沒有參數,需保留小括號。

let print = () => console.log('Hello World');
print();

當函式回傳結果之後,它的任務就算結束了,所以return之後的程式碼不會執行。

let avg = function (x, y) {
    return (x + y) / 2;
    console.log('不會執行');
}
console.log(avg(2, 3)); //2.5

參數預設值

假設我們定義一個函式,需要2個參數,萬一呼叫函式,只給定一個引數或是都沒給,為了不影響程式運作,且得到需要的值,就可以對參數設定值,算是防呆設計。

function getArea(w = 1, h = 1) {
    return w * h;
}
console.log(`面積為:${getArea()}`); //面積為:1
console.log(`面積為:${getArea(10)}`); //面積為:10
console.log(`面積為:${getArea(10,5)}`); //面積為:50

我們可以在定義函式的同時,給定參數預設值,如果都沒輸入,直接以預設值運算。
給定一個,會優先套用到第一個參數;全部給定,全部參數就以給定值運算。

「...」運算子

當我們的參數是陣列的時候,可以使用「...」運算子,把陣列給展開。

console.log(Math.max(1, 555, 66, 4, 99)); //555
let ary = [1, 555, 66, 4, 99];
console.log(Math.max(ary)); //NaN
console.log(Math.max(...ary)); //555

Math.max( )可以找出最大值,但無法接受傳入的參數型別為陣列,我們可以使用「...」運算子,將陣列展開,就可以順利執行了。

具名參數

let obj = {
    w: 10,
    h: 10
}
let obj2 = {};

function getArea({w = 1,h = 1}) {
    return w * h;
};
console.log(getArea(obj)); //100
console.log(getArea(obj2)); //1

我們可以設定物件的屬性預設值,當傳入物件有給定值的時候,使用給定值運算。
若為空物件,則使用預設值運算。

針對特定屬性作運算

let obj = {
    w: 10,
    h: 10,
    name: 'table'
}
function getArea({w = 1,h = 1}) {
    return w * h;
};

function getName({name}) {
    return name;
};
console.log(getArea(obj)); //100
console.log(getName(obj)); //table

我們可以針對該物件的w跟h的屬性作運算,或只取出name屬性。

回傳多個值指派給多個變數

如果需要函式回傳多個值,我們可以使用陣列或物件的方式回傳,並使用解構賦值(destructuring assignment)同時指派給多個變數,相當方便。

function getValue(nums) {
    return [Math.max(...nums), Math.min(...nums)];
}
let nums = [10, 99, 265, 5, 48, 100];
let [max, min] = getValue(nums);
console.log(`最大值:${max}`); //最大值:265
console.log(`最小值:${min}`); //最小值:5

遞迴

遞迴函式是指,函式在運算過程中會一直呼叫自己執行運算,直到符合終止條件,常用於階層運算。

function recursive(n) {
    if (n !== 0) {
        return n * recursive(n - 1)
    }
    return 1;
}
console.log(recursive(5)); //120

它的運算過程是這樣的:
第1次return:5 * recursive(4)
第2次return:4 * recursive(3)
第3次return:3 * recursive(2)
第4次return:2 * recursive(1)
第5次return:1 * recursive(0)
第6次return:1
直到不會再呼叫自己為止,開始倒推回去:
第6次return:1
第5次return:1 * recursive(0) => 1*1=1
第4次return:2 * recursive(1) => 2*1=2
第3次return:3 * recursive(2) => 3*2=6
第2次return:4 * recursive(3) => 4*6=24
第1次return:5 * recursive(4) => 5*24=120
使用遞迴切記要設終止條件,不然的話,它會一直呼叫自己,形成無限迴圈。

參考來源:
JavaScript大全
Speaking JavaScript|簡明完整的 JS 精要指南
新一代 JavaScript 程式設計精解


上一篇
堆疊(Stack) & 佇列(Queue)
下一篇
一級函式 & 函式應用
系列文
JavaScript Note31
0
hbdoy
iT邦新手 5 級 ‧ 2018-10-31 19:00:33

若是你認為這是引用,應該附上文章來源吧?

0
hbdoy
iT邦新手 5 級 ‧ 2018-10-31 19:13:27

請問您文章前半段的順序是從哪裡參考的呢?

還是您自己打的嗎?

https://ithelp.ithome.com.tw/articles/10200072

為何具名函數>匿名函數>箭頭函數>接下來是講到參數預設?

請問這個順序是您自己想的還是網路上有參考到?

可以請您告訴我是哪一篇嗎,不然會讓我一直誤會您

0
hbdoy
iT邦新手 5 級 ‧ 2018-10-31 19:14:38

當函式回傳結果之後,它的任務就算結束了,所以return之後的程式碼不會執行。

請問為何這個概念的介紹順序也和上面的連結一樣呢?

0
hbdoy
iT邦新手 5 級 ‧ 2018-10-31 19:26:04

若是我誤會您請您務必回應我或私訊我

看更多先前的回應...收起先前的回應...
WM iT邦新手 5 級‧ 2018-10-31 20:25:30 檢舉

我一併回答
剛剛的問題,的確是參考
新一代 JavaScript 程式設計精解
這本書,請你親自去看

hbdoy iT邦新手 5 級‧ 2018-10-31 20:32:18 檢舉

函數宣告 => 表達 => 箭頭 => return 終止函數 => 參數預設值
所以您的意思是書中的介紹順序就是這樣嗎?

WM iT邦新手 5 級‧ 2018-10-31 20:41:13 檢舉

順序我有自己的安排,我只是剛好跟你一樣,而且順序不構成抄襲吧
重點是內容文字是否有抄襲吧!!!
我們內容明明差那麼多!!!
若有抄襲,請舉證。

hbdoy iT邦新手 5 級‧ 2018-10-31 20:51:06 檢舉

這麼巧啊,順序可以一樣。

重點在於您不是通篇一樣,所以不構成抄襲,但若您是剛好有參考到我的,沒有附上引用是否說不過去呢?

還是我誤會了,單純這麼巧合地順序有辦法一樣,另外還請您回應第一篇的問題
https://ithelp.ithome.com.tw/articles/10201760#response-309017

hbdoy iT邦新手 5 級‧ 2018-10-31 20:56:10 檢舉

我認為參考什麼的都是合情合理,但內容順序應該是要自己整理出來的。

況且我也是當屆參賽者,如果有參考到其他參賽者的文章會不會有點太過?

WM iT邦新手 5 級‧ 2018-10-31 22:51:14 檢舉

先去看書,再釐清你的問題,好嗎!!!
話已至此.................

hbdoy iT邦新手 5 級‧ 2018-10-31 23:03:06 檢舉

呵呵

0
WM
iT邦新手 5 級 ‧ 2018-11-01 15:50:30

大家來心平氣和來談吧,針鋒相對不能解決問題。
function部分,我在參考書籍的時候,內容順序就隨著書的章節排下來,篇排順序跟書一樣。
事件佇列(queue),說實話,我只知道原理,沒特別注意名稱就很自然而然地打出來,
跟你同名,網路又找不到相同的,真的只是單純巧合。
如果我真的要抄襲,不會只抄'事件佇列(queue)'這幾個字,也不會只用一次。
而是把整篇文章COPY過來,然後玩文字遊戲,改改文字,讓文章看起來不要相同就好。
若你真的介意,我可以改掉。
至於字串跟陣列的範例,我有參考MDN,可能才讓你誤解。
直到你告知這件事,我才看過你文章,之前完全沒看過。
不管怎樣,你我都素昧平生,大家都是因為熱愛這技術,才藉由鐵人賽挑戰自己,分享自己所學的,不是嗎?
我希望能把時間花在提升自己,進而為前端的學習風氣,注入正能量。
若因此交惡,相信彼此不都樂見。
我能說的,都說了,接受與否在於你,若接受了,大家就當交個朋友吧。

hbdoy iT邦新手 5 級‧ 2018-11-01 22:00:42 檢舉

事情演變成這樣也不是你我樂見的,「大家都是因為熱愛這技術,才藉由鐵人賽挑戰自己,分享自己所學」,這我認同。

我起初的本意也並非要與您交惡,這件事就到這邊吧,讓我們繼續把精力專注在技術本身。

我要留言

立即登入留言