# 十二、 宣告各種函式
###### tags: `JavaScript` `JS101` `2020七月第二週` `進度筆記` `Lidemy心得` `7/8`
---
## function 起手式
一般宣告函式的方式:
function hello() {
console.log('hello')
}
return {
}
函式是 primitive data value , 因此我們可以:
var hello = function() {
console.log('hello')
}
會跟上個式子很相似。
---
而如果:
function print(anything) {
console.log(anything)
}
print(123)
// 會印出 123 。
傳 `print(123)` 參數到 console.log 就會印出 123。
接著:
function print(anything) { //這邊的 anything 有可能是個 function ;
console.log(anything)
}
function hello() { // 假設新增了一個 function 叫作 hello ;
}
print(hello)
// 會印出 Function: hello 。
可以把最後一行 hello 往上傳進去,再將 function 當作參數傳進去。
然後:
function print(anything) { // function print(anything) { 是物件,這邊的 anything 有可能是個 function ;
console.log(anything) // 這裡可以傳一個 function 進來,相等於下面 "會變成:" 式子的 anything() ;
}
function hello() { // function 前面有個 log: ; 等同這行 function 不會被執行,只是被 log ;
console.log('hello') // 'hello' 是第一行的物件 function print(anything) , 當這個函式是物件裡面的 method 時,這時候的 this 就會指稱到包含這個 method 的物件;
print(hello)
// 會印出 Function: hello 。
會變成:
function print(anything) { // 讓 anything() 變成 function hello() ;
anything() // 成為 function 執行他。
}
function hello() {
console.log('hello')
}
print(hello) // 把 hello 往上傳進去
// 會印出 hello 。
必須在 函式(參數) {區塊內才能放另外一個函式} ,其實為:
最後一行的 `print(hello)` 將 hello 參數設為 function 傳進去; 而第一行 `function print(anything) {` 的 anything 也就是一個參數,也是一個 function , 相等於第五行的 `function hello()` ,因此如果將最後一行 `print(hello)` 改成 `print(123)` 就會出錯。
示範:
function transform(x, transformFunction) {
return transformFunction(x) // 這一行意思是執行 function test(x)
}
function test(x) { // x 接受一個參數
return x*2 // 回傳 x*2
}
console.log(
transform(10, test) // 這邊的 (10, test) 即第五行的 function test(x) { ;
)
// 會印出 20 。
由於最後一行 `transform(10, test)` 傳到第一行的 x 相等於 10 , 然後最後一行的 test 被傳到第五行的 function ; 而第一行 transformFunction 相當於參數,也相當第五行的 function , 然後執行 x*2 。
再換一個方式:
`transform([1, 2, 3], double)` 將任何東西都 *2 => [2, 4, 6]。
function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來,這會對 arr 的每個元素都 *2 ;
var result = []
for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行,所以迴圈內的值
result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去,再把他 push 到結果內;
}
return result // 回傳 result 回去;
}
function double(x) { // x 接受一個參數
return x*2 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
}
console.log(
transform([1, 2, 3], double) // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
)
`transform([1, 2, 3], double)` 就會被放到第一行的 `function transform(arr, transformFunction) {`
這是 JS 的重要基礎步驟,結尾的 `console.log()` 不只能回傳數字、字串,還能回傳參數和函式。
## 宣告一個變數等於一個函式:
function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來,這會對 arr 的每個元素都 *3 ;
var result = []
for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行,所以迴圈內的值
result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去,再把他 push 到結果內;
}
return result // 回傳 result 回去;
}
var triple = function() { // 後面這個 function 是沒有名字的,因為它被賦予了變數 triple ,由變數決定;
return x*3
}
function triple(x) { // x 接受一個參數
return x*3 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
}
console.log(
transform([1, 2, 3], triple) // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
)
/// 這印不出來,只是示範將變數後的參數位置移開;
/// 可以將 var triple = function() 移到 transform([1, 2, 3], triple) 取代 triple 的位置:
function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來,這會對 arr 的每個元素都 *3 ;
var result = []
for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行,所以迴圈內的值
result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去,再把他 push 到結果內;
}
return result // 回傳 result 回去;
}
function triple(x) { // x 接受一個參數
return x*3 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
}
console.log(
transform([1, 2, 3], function(x) { // 後面這個 function 是沒有名字的,因為它被賦予了變數 triple ,由變數決定;
return x*3
}) // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
)
// 會印出 [3, 6, 9] 。
將變數等於一個函式,並將它函式的名稱隱藏起來的作法即 "匿名函式 (anonymous function) , 好處是可以直接改最後 return 的值。
[1. 匿名函式](https://pjchender.blogspot.com/2016/03/javascriptfunction-statements-and.html)
[2. 匿名函式和 this](https://pjchender.blogspot.com/2016/03/javascriptthisbug.html)
---
# 十三、參數和引數
## 常常名詞混用的兩種
作 function 的時候常常會不小心誤會 "參數(parameter)" 和 "引數(argument)" , 如 `function transform(arr, transformFunction)` 參數其實是括號內的 arr 和 transformFunction , 即他在 function 長甚麼樣子。
而引數是真正傳進去的東西,如:
function add(a, b) { // function add(a, b) 括號內的為參數;
return a+b
}
add(3, 5) // 真正傳進去的值為引數;
宣告後的引數:
function add(a, b) { // function add(a, b) 括號內的為參數;
return a+b
}
var c = 10
var d = 20
add(c, d) // 真正傳進去的值為引數;
在 call function 的為引數,會包含所有你放入function中的參數值。
但參數和引數其實都是傳進去的值。
---
## 背後的影武者: arguments
JS 在每個 function 後加入了 arguments:
function add(a, b) {
console.log(arguments) // 會把 2, 5 引數印出來;
return a+b
}
console.log(add(2, 5))
會得到 [Arguments] { '0': 2, '1': 5 }
7
Argument 的第 0 個為第一行的 a , 第一個為 b 即最後一行的回傳值 (2, 5) ; 因此第一行的括號內也可以甚麼都不寫:
function add() {
console.log()
return arguments[0] + arguments[1] // 會把 2, 5 引數回傳,即上式的 return a+b ;
}
console.log(add(2, 5))
答案也是 7 。
[1、參考引數和參數的解說:](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
[2、參考 arguments 物件解說:](https://ithelp.ithome.com.tw/articles/10192368)
[3、[筆記] 談談JavaScript中函式的參數(parameter),arguments和展開運算子(spread)](https://pjchender.blogspot.com/2016/04/javascriptparameterargumentsspread.html)
---
# 十四、 引數是啥?
---
## Arguments 印出所有參數
引數是個 object 物件,並不是陣列:
function add (a,b ) {
console.log(arguments)
return a + b
}
console.log(add(2, 5))
會印出 `[Arguments] { '0': 2, '1': 5 }` 7 , 且答案可以相加是 7 。
---
數字 0 跟字串 '0' 是一樣的沒有區別:
function add (a,b ) {
console.log(arguments[]) // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
return a + b
}
console.log(add(2, 5))
var a = { // → 假設有個物件長這樣;
b: '123'
}
a.b // 或是可以用 a['b'] ;
這印不出來。
---
function add (a,b ) {
console.log(arguments[]) // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
return a + b
}
console.log(add(2, 5))
var a = { // → 假設有個物件長這樣;
0: '123'
}
a[0] // 可以這樣存取;
但其實 a 是一個物件不是陣列:
function add (a,b ) {
console.log(arguments) // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
return a + b
}
console.log(add(2, 5))
var a = { // → 假設有個物件長這樣;
0: '123'
}
a[0] // 可以這樣存取;
console.log(a[0])
會印出 123 。
## Arguments 還有個屬性是 .length
一共可以傳幾個參數進來:
function add (a,b ) {
console.log(arguments.length) // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
return a + b
}
console.log(add(2, 5))
var a = { // → 假設有個物件長這樣;
0: '123'
}
a[0] // 可以這樣存取;
console.log(a[0])
會印出:
2 → 表示有兩個參數傳進來;
7 → 因為 return 2 + 5 ;
123 → 宣告 a 被賦予一個物件,而物件內 0= '123' 字串。
---
清楚一點表示:
function add(a, b) {
console.log(arguments.length)
return a + b
}
add(2, 5)
會印出 2 , 表示有兩個參數回傳,是一個長得很像 array 的 obj , array-like 。
---
# 十五、 function 傳參數的運作機制
## 用個函數交換 2 變數:
function swap(a, b) { // 傳 a 和 b 進去函式;
/*
var a = number1 → 因為 a 和 b 是複製來的,所以記憶體位置跟 number1 和2 不同;
var b = number2 → 傳進來後 a 和 b 的確變了,但不會影響外部 var number1 = 10 和 var number2 = 20 的值。
其 value 一樣,但是是不同的 key 變數。
*/
var temp = a // a 賦予暫存變數;
a = b
b = temp // 交換 2 變數需有第三方暫存,;
console.log('a, b:', a, b) // 這邊的確有交換 , a, b: 20 10 ;
}
var number1 = 10
var number2 = 20
console.log(number1, number2) // 這裡為 10 20 , 這邊為最開始傳進去第一行函式時期時有複製一份;
swap(number1, number2)
console.log(number1, number2) // 印出來為 10 20 ;
印出來為:

如果第五行 `console.log('a', 'b')` 的 (a, b) 這樣子直接印會出現:

而如果是字串 ('a', 'b') 則是:

都是一樣的表達方式,只是印出來的值不同。
---
## 這樣回傳是 11 還是 10 ?
function addValue(obj) { // 傳個 obj 進來;
/*
其實這裡有點像是物件的記憶體存取方式,第一行 var obj = a (外部的 number: 10)
會指向同一個值。
*/
obj.number++ // 回傳變數(屬性) +1 ; 也因指向同一個記憶體位置,裡面的值會影響外面宣告的變數值;
return 1
}
var a = {
number: 10
}
addValue(a)
console.log(a)
簡單型別如數字、字串、布林是直接 copy value 因此改不到,但物件和變數型態比較不同。
如變數並賦予給值時,變數會指向值在記憶體中的位置,若以這個值為參照,指定另一變數指向這個值時,電腦會在記憶體中新增(複製)一個新值,讓後來的這個變數指向新的值。
因此會印出一個物件: { number: 11 } 。
也因此使用函式時,數值內部的東西更動時,可能會與外部的宣告賦予變數連動,也因此有幾種形式:
pass by value
pass by sharing (屬於在 pass by value 底下)
pass by reference (JS 內沒有)
[參考文件: 深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/)
---
function addValue(obj) {
obj = {
number: 100
}
return 1
}
var a = {
number: 10 // 而這邊新的物件產生,指到了另一塊記憶體;
}
addValue(a) // 這行沒有作用,但表示下行 a 會被加入到 function addValue(obj) { 內;
console.log(a)
因 `obj = {number: 100}` 是個物件,存在一個獨立記憶體中; 而僅賦予 obj 是物件,但因為函式中沒有運算,也因此回傳 1 不起作用; 最後宣告 `var a = {number: 10}` 是個物件,也是個變數,但沒有指向與第三行的 obj 同個記憶體,因此可以回傳到第一行起作用,會印出: { number: 10 } 。