我的第一堂 - JavaScript 03 迴圈、函式


Posted by krebikshaw on 2020-06-17

迴圈 loop

do while

先執行一次 do 裡面的動作,接著判斷是否符合 while 的條件,符合就再執行一次,直到不符合條件為止。

var i = 1

do {
    console.log(i)
    i++
} while (i <= 100)
  1. 一開始宣告一個變數 i = 1
  2. 執行 do 裡面的動作: console.log 印出 i 之後,再把 i + 1
  3. 判斷是否符合 while 條件,因為現在的 i = 2 有符合條件,所以再做一次 do 裡面的事情
  4. 直到 i > 100 不符合條件,就會跳出迴圈

while 迴圈

whilt 的條件滿足時,執行 { } 裡面的動作。

var i = 1

while (i <= 100) {
    console.log(i)
    i++
}
  1. 一開始宣告一個變數 i = 1
  2. 判斷是否符合 while 的條件,i = 1 有符合條件
  3. 執行 { } 裡面的動作:console.log 印出 i 之後,再把 i + 1
  4. 再判斷一次是否符合 while 的條件,i = 2 有符合條件
  5. 再繼續執行 { } 裡面的動作
  6. 直到不符合條件為止
  • 補充:
    • continue 可以跳過這圈未完成的動作,直接進到下一圈迴圈
    • break 可以強制跳出迴圈

for loop 迴圈

用法: for ( 起始條件; 終止條件; 每一圈的改變 ) { 每一圈要做的事情 }

  • 範例:
for ( var i=1; i<=100; i++) {
    console.log(i)
}
  1. 起始條件是 i = 1,終止條件是 i <= 100,每一圈的改變是 i = i+1
  2. 符合條件就會執行 { } 裡面的事情。
  • 假設我有一個存放成績的陣列 scores,我想要用 for loop 來把成績加總,我們可以這樣寫:
var scores = [ 80, 75, 73, 89, 91 ]
var scores_sum = 0

for ( var i=0; i<scores.length; i++) {
    scores_sum += score[i] 
}

console.log( scores_sum )

函式 function

函式的基本結構

function <函式名> ( 參數 ) {
    函式內容 
    return 我要回傳的東西
}
  • 我們會設定需要傳什麼參數進去,並在 { } 中寫下函式的內容
  • 以把三個數字相加為例:
function add ( a, b, c ) {
    return a + b + c
}
  1. 我寫了一個叫做 add 的函式,並傳入 a, b, c 三個參數
  2. 這個函式會回傳 a + b + c 的結果

宣告函式的不同種方式

function hello () {
    console.log( "hello" )
}

var hello = function() {
    console.log( "hello" )
}

上面兩種方式,表示都是叫做 hello 的函式

  • Function 參數也可以丟入 Function
var scores = [ 80, 75, 81, 92 ]

function scores_sum ( scores, add ) {
    var sum = 0
    for ( var i=0; i<scores.length; i++) {
        add ( sum, scores )
    }
    return sum
}

console.log(
    scores_sum( scores, function( num1 , num2 ) {
        return num1 += num2
    })
)

引數(Argument)與參數(Parameter)

參數 是函式所接收的數值
引數 是實際傳入函式的數值 (實際引用進來的)

  • 以下面函式為例:

    function fun1 ( a, b ) {
      return a + b
    }
    var c = 1
    var d = 2
    console.log( fun1 ( c, d ))
    
  • 我們可以看到 fun1 接收 a,b 所以 a,b 就是 參數

  • 實際我們呼叫 fun1 時所傳入的值是 c,d,所以 c,d 是 引數

JavaScript 有提供一個功能,可以讓我們在函式中查看傳進來的引數是什麼

function fun1 () {
    console.log (arguments)
}
  • 像這樣 console.log 或者是 return arguments,就可以查看引數的東西。
  • 補充: arguments 是一個物件不是陣列,所以引用的時後要注意一下。

使用 function 要注意的事項

Pass by value

  • 引數傳入 function 之後,有點像是把數值複製一份帶入 function 裡面
function addValue (a, b) {
    return a + b
}

var c = 1
var d = 2

addValue ( c , d )

像這個例子,引數傳入函式 addValue 的時候,概念上會像這樣:

a = c    // 先把 c 的值複製一份給 a
b = d    // 也把 d 的值複製一份給 b
  • 從 code 來看:
function swap ( a, b ) {     // 一個會把 a, b 值互換的函式
    var temp = a
    a = b
    b = temp 
}

var num1 = 10
var num2 = 5
swap ( num1, num2 )          // 函式會把帶入的 10, 5 互換
console.log( num1, num2 )    // num1 = 10  num2 = 5
  1. 把 num1, num2 帶入 swap
  2. a = num1 = 10 , b = num2 = 5 把引數的值複製一份給參數
  3. 把 a, b 值互換,所以 a 變成 5,b 變成 10
  4. 回頭看 num1 跟 num2 的值依然沒有改變,num1 = 10, num2 = 5

原始型別 num、string、boolean 都是傳值 pass by value,原來的 num1、num2 並沒有被更改。

pass by reference

  • 「你傳進去的東西就是真的 num1 跟 num2,function 裡面的 a 跟 b 只是別名(alias)而已,改變 a 就會改變 num1」
  • 不適用於 原始型別 num、string、boolean
  • 我們要用 object 來舉例:
function add(obj){
    obj.number++
}

var o = {
    number: 10
}

add(o)
console.log( o.number )   // 11

因為 物件型態 備份的值會是 記憶體位置,所以備份引數的記憶體位置後,物件 o 與物件 obj 會指向同一個記憶體位置,改變函式中物件 obj 的內容,也會改動到函式外的物件 o 的內容。

但是有一個重點!

  • 上面用了 object 來舉出 pass by reference 的特性,但是實際上 object 在 JavaScript 並不是用 pass by referencr。

pass by sharing

function add(obj) {
讓 obj 變成一個新的 object
    obj = {
        number: obj.number + 1
    }
}

var o = {
    number: 10
}
add(o)
console.log( o.number )    // 10
  • pass by sharing 跟 pass by reference 的特性一樣,可以讓函式內與函式外「共享」同一份資料,所以透過裡面的 obj,你可以去修改「共享到的那個 object」的資料。
  • 差別在於如果在 function 裡面把 obj 重新賦值,就代表你要讓這個 obj 指向一個新的 object,所以外面的 o 依舊還是原來的值。(指向的記憶體位置就不同了)

JavaScript 的 primitive type 是 pass by value,object 是 pass by sharing

回傳 & 印出

return 用來將函式的結果回傳出去。

  • 你可以設定不同情況下回傳不同的結果
  • 一但執行 return,會直接跳出函式。
  • 不寫 return 也可以,但是 console.log 要印出這個函式的結果時會印出一個 undefine,表示你沒有回傳值。
function fun1(age) {
    if(age >= 18) return bigger
    if(age < 18) return smaller
}

var age = 20
if( fun1(age) === bigger ) console.log( "成年了" )
if( fun1(age) === smaller ) console.log( "未成年" )
  1. age >= 18 或者 age < 18 兩種不同情況 return 不同的結果
  2. 在滿足 age >= 18 的情況下 return bigger 後,下面的程式就不執行了

return 跟 console.log 的差異是什麼

  • 當這個函需要輸出你想要的結果時,它一定需要有 return,這樣別人來呼叫這個函式的時候,才能拿到他輸出的結果
function fun1( a, b ) {
    var answer = a + b
    return answer
}

var result = fun1 ( 10, 5 )  
//result 的值會等於 fun1 回傳的結果

  • 如果要將某一筆資料印出來查看,可以用 console.log
function fun1( a, b ) {
    var answer = a + b
    console.log( a, b )
    return answer
}

fun1 ( 10, 5 )
  1. 這個函式會先印出 a, b 是什麼
  2. 在回傳 a + b 的答案
  3. 回傳的值,如果沒有另外用 console.log 來顯示,它就不會印出來給你看。

常用內建函式

內建函式是程式語言提供給你,讓你直接可以使用的函式。

數學相關函式

  • Math.ceil() : 無條件進位
  • Math.floor() : 無條件捨去
  • Math.round() : 四捨五入
  • Math.random : 產生 0 ~ 1 的隨機數( 不包括 1 )
  • Math.max(num1, num2, num3) : 丟入一系列數字,回傳最大值
    也可以丟陣列,但寫法要用 展開運算子 ...,例如: Max.max(...arr)
  • Math.min(num1, num2, num3) : 丟入一系列數字,回傳最小值
    • 用法如同 Math.max()
  • Number.MAX_VALUE : 回傳 JavaScript 能存的最大數字。
  • Number.toFixed(x) : 取到小數點後第 x 位數( 無條件捨去 )

  • Math.sqrt() : 開根號

  • Math.log() : 對數
  • Math.pow(x, y) : 次方運算
    • x 的 y 次方

字串、數字間互相轉換

  • 字串轉數字:
    • parseInt(<strin>, x) x 代表你要轉換成幾進位表示
    • Number(<string>)
console.log( parseInt( "20" , 10 )   // 20
console.log( parseInt( "101001", 2 )    // 41

console.log( Number( "20" )  //20
  1. 把 "20" 轉換成 10進位 表示的數字 , 也就是 20
  2. 把 "101001" 轉換成 2進位 表示的數字 , 也就是 41
  3. 把 "20" 轉換成數字,也就是 50
  • 數字轉字串
    • <number>.toString()
    • <number + ''
var num = 20
console.log( num.toString() )   // "20"
console.log( num + '' )  // "20"

將數字跟字串相加,系統會自動判別為字串相加,所以可以利用加上空字串 '' 來把數字轉換為字串。

字串相關函式

  • length : 回傳字串的長度
  • toUpperCase() : 將字串全部轉為大寫
  • toLowerCase() : 將字串全部轉為小寫
  • charCodeAt(char) : 取出字元的 ASCII 碼
  • String.fromCharCode(num) : 將 ASCII 碼轉成字元
  • indexOf(keyword) : 找出關鍵字 keyword 有沒有在字串裡
    • 有,回傳 keyword 索引值
    • 沒有,回傳 -1
  • replace(x, y) : 找的第一個 x 字串,用 y 取代
    • 如果要取代所有的 x,要用正規表達式 : replace(/x/g ,y)
  • split(x) : 分割字串成陣列
    • 用 x 作為分割字串的參考,回傳陣列
  • trim() : 去掉前後的空格

字串、陣列間互相轉換

  • 字串轉陣列
    • str.split('') 以空格為切割標的
    • 可自訂要以什麼方式切開,例如 ,-
  • 陣列轉字串
    • arr.join('') 直接合成一個字串,不隔開
    • 可以自訂以什麼方式隔開,例如 ,
var str = "apple, banana"
console.log( str.split(','))  // [ 'apple', ' banana' ]


var arr = [ "apple", "banana" ]
console.log( arr.join(','))   // apple,banana

常用的陣列相關函式 (這部分找時間獨立出來整理)

  • join() : 把所有元素轉成字串,串接回傳結果字串(預設用逗號分割)。
  • reverse() :倒轉陣列元素的順序,並回傳結果
  • sort() :將元素排序
  • concat() :如果引數值有陣列,會把引數值加入並回傳一個新陣列
  • slice() :回傳擷取出的元素
  • splice() :用新的元素取代掉原本的元素
  • push()pop()
    • push() 是將新的元素推進陣列的最後
    • pop() 是將陣列最後一個元素回傳並把它移除掉
  • unshift()shift()
    • unshift() 是將新的元素推到陣列的開頭
    • shift() 是回傳陣列的第一個值並把它移除掉
  • toString() :把陣列元素轉成字串,用逗號分隔輸出一字串

immutable 觀念

  • immutable 不可變:除了 物件型態 以外的變數基本上都不可變
var a = "Hello"   // 在 0x01 存放一個值為 Hello
a = "Yo"          // 在 0x02 存放一個值為 Yo
a = a + "Yo"      // 在 0x03 存放一個新的 HelloYo
  • 當我們重新賦值的時候,不會改動原本記憶體下的值,而是新建一個記憶體空間。

用一個變數去呼叫 function

var a = "hello"
a.toUpperCase()
console.log(a)    //  hello
  • a 是原始型態,用 a 去呼叫函式不會改變原本 a 的值
var a = "hello"
a = a.toUpperCase
console.log(a)      // HELLO
  • 用 a 去呼叫函式,並 賦值 回原本的 a,程式會回傳給 a 新的值並存在新的記憶體位置。
var a = "hello"
var temp = a.toUpperCase()
console.log(temp)  // HELLO
  • 或者是另外宣告一個新的變數,去接收回傳出來的值

物件型態,可以直接於同一個記憶體改變內容

var arr = [ 1, 2, 3 ]    // 0x01
arr.push(4)
console.log(arr)         // [1,2,3,4]  0x01
  • 用 arr 呼叫 push 這個函式,會直接在相同的記憶體底下更改 arr 的內容,不會新增一個新的記憶體位置
    • 除非,函式回傳的值已經不是一個陣列了
對於 字串數字 來說,永遠不會這樣子寫:
var str = "hello"
str.toUpperCase()

這樣子寫是沒有用的

對於 物件陣列 來說,通常也不會這樣子寫:
var arr = [ 1,2,3 ]
arr = arr.slice(2)

這樣回傳回來的值就不是 array 了

  • 關鍵就是要判斷,這個函式是會回傳還是改動原本的值,或者既回傳又改變原本的值。

#迴圈 #函式 #javascript







Related Posts

AI輔導室|製作黑洞效果

AI輔導室|製作黑洞效果

target="_blank" 風險問題處理,加入 rel="noreferrer noopener"

target="_blank" 風險問題處理,加入 rel="noreferrer noopener"

5. Prototype

5. Prototype


Comments