JS 紀錄6 - function 2

JavaScript function 紀錄2

IIFE(Immediately Invoked Functions Expressions) - 立即執行函式

有什麼特性

  • IIFE 在執行環境一讀取到就會立即執行,而不像一般的函式需要呼叫才會執行
  • 限制變數存在的作用域,讓變數不輕易成為「全域變數」的方法
  • closure 的應用封裝物件的公用與私有成員
  • 希望不同 execution context 的變數之間不要互相影響

判斷

  • (function () { … })(要執行的)
  • (function () { … }(要執行的))

寫法

  • 函式表達式 + 匿名函式
1
2
3
4
(function (a, b){  
var c = 10;
return a + b + c;
})(10,20); // 40
  • 迴圈遇到 function
    • var i 為 global 每次 for 迴圈都用同個記憶體位置紀錄,所以跑完後的值為 5
    • 非同步執行 不會等待 window.setTimeout 結束後才繼續,而是在執行階段就一口氣跑完
1
2
3
4
5
for(var i = 0; i<5; i++){
window.setTimeout(function() {
console.log(i); // 5 5 5 5 5
},1000)
}
  • IIFE
    • window.setTimeout 透過「立即函式」來包裝
    • 為了保留每一次執行迴圈時那個「當下的」i 將它包覆起來然後 i 作為參數傳入 x
1
2
3
4
5
6
7
8
for( var i = 0; i < 5; i++ ) {
// closure 參數 x 會被內部 function keep 住
(function(x){
window.setTimeout(function() {
console.log(x); // 0 1 2 3 4
}, 1000);
})(i);
}
  • let 宣告
    • 用 let 的 scope 在 for 迴圈鎖住讓每次跑的迴圈都建立到一個新的記憶體位置
1
2
3
4
5
for(let i =0;i<5;i++){
window.setTimeout(function(){
console.log(i) // 0 1 2 3 4
},1000)
}

callback function( heigh order function)

有什麼特性

  • callback function 跟一般的函式沒什麼不同,差別在於被呼叫執行的時機
  • 函式之間的相依過深,ES5 callback 多層之後產生的「波動拳」維護就要用 ES6 Promise, ES7 Async/Await

判斷

  • 把函式當作引數(參數),透過另一個函式來呼叫它(回傳函式)
  • function A 將 function B 當成參數傳入使用,當這個 function A 執行後會觸發 function B 的執行

寫法

  • 將函式當成參數傳遞給其他函式
1
2
3
4
5
function get(url,handler){
$.get(url,function(data){
handler(data);
});
}
  • 以具名函式方式傳入 (也可以透過匿名函式傳入)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function callback(data){
$('.result').html(data);
}
get('ajax/test.html’, callback);

/*----------------------------------
name(function(){
console.log('My name is jimmy');
});

function name(callback){
var Greeting = 'Here is some work...';
callback(); // My name is jimmy
}
  • 事件動作(ex.點擊事件)後,呼叫點擊事件內要執行的 function
1
2
// function 可獨立出來
box.addEventListener('click',function(){'box向左移動'},false)
  • 多個動作要進行那就必須控制多個 function 間執行的順序(執行順序的排程)
1
2
3
4
5
6
7
8
9
10
11
12
function logWord(word){
setTimeout(function(){
console.log(word) // b,c,a or c,a,b 沒有固定順序
},Math.floor(Math.random()*100)+1)
}

function wordAll(){
logWord('a')
logWord('b')
logWord('c')
}
wordAll()
  • callback 方式執行順序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function logWord(word,funCallback){
setTimeout(function(){
console.log(word) // 'a','b','c' 固定順序
// 傳入型態判定是否為 function
if(typeof funCallback === 'function'){
funCallback()
}
})
}

function wordAll(){
// 決定執行順序
logWord('a',function(){
logWord('b',function(){
logWord('c',function(){})
})
})
}
wordAll()
  • callback 帶入參數(引數)
1
2
3
4
5
6
7
8
9
function showMessage(greeting, name, callback) {
console.log('you call showMessage') // you call showMessage
if(typeof callback === 'function'){
callback(greeting, name)
}
}
showMessage('Hello!', 'Eddy', function(param1, param2) {
console.log(param1 + ' ' + param2) // Hello! Eddy
})

function 傳入參數預設值( || ) ES5 (=)ES6

有什麼特性

  • 進行預設值的設定
  • 沒有傳入任何參數,回傳 undefined 這個參數

判斷

  • 用 typeof 的回傳值來判斷是否為undefined,或是用 (=) 或 (||)運算符 的預設值設定方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ES5
function myfavorite(phone,name,job){
job = job || 'bnanan'
console.log(phone)
console.log(name)
console.log(job)
}
myfavorite() // undefined undefined banana

/*----------------------------------
// ES6
function myfavorite(phone,name='jimmy',job){
job = job || 'bnanan'
console.log(phone)
console.log(name)
console.log(job)
}
myfavorite() // undefined jimmy banana

function 無名的傳入參數(arguments)

有什麼特性

  • JS 有預設的參數 arguments 可直接帶入,這種參數不須預先設定,所有函式都內建此參數
  • arguments 雖是一個物件資料類型,但它有”陣列”的一些基本特性,不過缺少大部份陣列中的方法,所以被稱作”pseudo-array”(偽陣列)

判斷

  • 不管有沒有參數的 function 都有 arguments
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 無參數
function sum(){
let b = 0
for(i in arguments){
b += arguments[i]
}
return b
}
console.log(sum(1,2,3,5)) // 12

/*----------------------------------
// 有參數
function myfavorite(phone,name,job){
job = job || 'bnanan'
if(arguments.length === 0){
console.log('呵呵呵’) // '呵呵呵'
return
}
console.log(arguments)
}
myfavorite()

/*----------------------------------
// 儲值系統
// 先預設裡面沒錢 myMoney
// 每儲一次 相加
var myMoney = 0
function cashMoney(){
var machsion = 0
for(var i = 0; i < arguments.length; i++){
machsion +=arguments[i];
console.log(machsion) // 10 110 310
}
var machsionMoney = machsion + myMoney
console.log("現在總共儲值”+machsionMoney+”元")
}
cashMoney(10,100,200,500) // "現在總共儲值810元”

function 不固定傳入參數(Variadic)與其餘參數(spread) ES6

有什麼特性

  • 在 function 中定義有三個傳入參數,在呼叫函式時傳入的參數值(arguments)沒有預設的情況下會視為 undefined or NaN,或是多出來的情況是會被直接略過
1
2
3
4
5
6
function sum(x, y, z) {
return x+y+z
}
console.log(sum(1, 2, 3)) //6
console.log(sum(1, 2)) //NaN
console.log(sum(1, 2, 3, 4)) //6
  • 改善1 […arguments] 透過變數宣告
1
2
3
4
5
6
7
8
9
10
function updateSum(){
let num = [...arguments]
let sum = num.reduce(function(item,currItem){
return item + currItem
},0)
console.log(sum)
}
updateSum(1,2,3) // 6
updateSum(1,2) // 3
updateSum(1,2,3,4) // 10
  • 改善 2 (…arg) 透過參數
1
2
3
4
5
6
7
8
9
10
function sum2(...arg){
let total = 0
for(let i = 0; i < arg.length; i++){
total+= arg[i]
}
return total
}
console.log(sum2(1,2,3)) // 6
console.log(sum2(1,2)) // 3
console.log(sum2(1,2,3,4)) // 10

判斷

  • 其餘參數 spread (…)
    • (…參數名稱)
  • spread 只是扮演好參數的角色,代表不確定的其他參數名稱
  • 如果一個函式中的參數值有其他的確定傳入參數名稱,其餘參數名稱應該要寫在最後一個位子,而且一個函式只能有一個其餘參數名稱
  • 將類陣列轉為陣列的方式
    • […spread]
    • Array.from()

範例

  • 其餘參數
1
2
3
4
function moreMoney(...money) {
console.log(money); // 100, 200, 300,400
}
moreMoney(100, 200, 300,400)
  • 使用展開語法,合併陣列
1
2
3
4
5
6
let groupA = ['薯條', '可樂', '漢堡'];
let groupB = ['雞排', '炸雞'];
let groupAll = groupA.concat(groupB)
let groupAll2 = [...groupA,...groupB]
console.log(groupAll) // ["薯條", "可樂", "漢堡", "雞排", "炸雞"]
console.log(groupAll2) // ["薯條", "可樂", "漢堡", "雞排", "炸雞"]
  • 淺複製 (shallow copy)
1
2
3
4
5
6
let groupA = ['薯條', '杰倫', '漢堡'];
let groupB = groupA;
let groupB = [...groupA] // 將值一個一個取出來在 return 回陣列
groupB.push('阿明');
console.log(groupA); // ["薯條", "可樂", "漢堡", "阿明"]
console.log(groupB) // ["薯條", "可樂", "漢堡", "阿明"]
  • 類陣列 Array.from
1
2
3
4
5
6
let doms = document.querySelectorAll('li');
let doms2 = [...doms]
let doms3 = Array.from(document.querySelectorAll('li')) // 轉為陣列
console.log(doms); // NodeList (缺少 array 的方法)
console.log(doms2) // array
console.log(doms3) // array
  • 類陣列 […arguments]
1
2
3
4
5
6
7
8
9
10
11
var originCash = 1000;
function updateEasyCard() {
let arg = arguments // arg.reduce is not a function (類陣列沒有 array 的方法)
let arg = [...arguments] // 轉為陣列
let sum = arg.reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
console.log('我有 ' + sum + ' 元');
}
updateEasyCard(0); // 我有 1000 元
updateEasyCard(10, 50, 100, 50, 5, 1, 1, 1, 500); // 我有 718 元
  • spread 與 arguments 物件的幾個簡單比較

    • spread 只是代表其餘的傳入參數值,而 arguments 物件是代表所有傳入的參數值
    • 其餘參數傳入值是一個標準陣列,可以使用所有的陣列方法
    • arguments 是”偽”陣列的物件類型,不能使用陣列的大部份內建方法
    • spread 需要定義才能使用,arguments 物件不需要定義即可使用,它是隱藏機制

function 遞迴

  • 函式呼叫函式自己的行為
  • 通過呼叫函數本身來重複將問題分解為同類的子問題而解決問題的方法
  • 遞回函式一定記得設定終止條件,否則很容易跳不出來,導致無窮迴圈的情況產生
  • 遞迴函式會收到兩個輸入:結束遞迴的基本情況(base case)或是延續遞迴的遞迴情況(recursive case)
1
2
3
4
5
6
7
8
9
function foo(x) {
// 基本情況
if ( x < 5 ) return x
// 遞迴情況
return foo(x / 2)
}
foo(6)
foo(10)
foo(12)
Step1 Step2 Step3
foo(6) (6 / 2) return 3 (3 < 5) return 3
foo(10) (10 / 2) return 5 (5 / 2) return 2.5 (2.5 < 5) return 2.5
foo(12) (12 / 2) return 6 (6 / 2) return 3 (3 < 5) return 3
0%