JavaScript-函式特性(Functions)

學習來源:
(Eloquent JavaScript電子書)

提升(Hoisting)

javascript的執行環境分為兩階段,在執行程式碼(exection)之前,會經歷創建階段(creation),在創建階段中會將函式及變數先設置在記憶體中,而這個動作稱作「提升(hoisting)」。

  • 函式提升

    在執行任何程式碼前,JavaScript 會把「函式宣告初始化」放進記憶體裡面,意思是函式{}的內容都會在創建階段放在記憶體中。
  • 變數提升

    在創建階段,電腦的記憶體為a空出記憶體,可是他還不知道他的值為何,會先給予一個undefine的值,等到執行階段賦值時,才會將a的值放進記憶體中。也因此下方案例,印出a並不會出錯,只是它的值為undefine。
    1
    2
    3
    4
    5
    6
    console.log(a);
    b();
    var num = 6; //只要 num 有被宣告,就不會有錯誤
    function b(){
    console.log('b');
    }

陳述式與表達式

  • 表達式 (expression),程式碼「會」產生(回傳)出一個值

1
2
3
4
5
var a=3; //瀏覽器console回傳3,這代表這段程式碼是Expression

10+5;//瀏覽器console回傳15,它也是Expression

a === 3;//回傳true(成立),它也是Expression
  • 陳述式 (statement),程式碼「不會」回傳出一個值

    if()(),需要放入一個表達式a===3,但if()本身不會有回傳值,也無法將它賦值火指向變數,因此它是一個陳述式

    1
    2
    3
    if(a===3){
    console.log('good');
    }
  • 函式陳述式 (function statement)

    程式碼本身不會執行,又稱做函式宣告,在創建階段就先把函式設定進記憶體,等待被呼叫

    1
    2
    3
    funtion doIt(){
    console.log('finished');
    }

    所以即使先程式碼撰寫先後順序是先呼叫do()再定義funtion do()並不會出錯;因為電腦實際運作的順序是

    step0.創建階段先將`funtion do()`設定進記憶體
    step1.讓所有程式碼開始執行,此時呼叫`do()`不會出錯
    
    1
    2
    3
    4
    5
    doIt();//step1.程式碼執行呼叫

    funtion doIt(){//step0.創建階段已經被設定進記憶體
    console.log('finished');
    }
  • 函式表達式 (function expression)

    當程式碼執行到=運算子右邊的程式碼,才建立這個函式物件,將其指向變數do

    1
    2
    3
    var doIt = function(){
    console.log('finish');
    }

在創建階段,會先將var do宣告在記憶體中,但沒有定義其值,因此程式碼在執行到do()時會出錯;電腦實際運作的順序是
step0.在記憶體中開一個洞命名do
step1.讓程式碼開始執行,由上往下先執行do(),因為do()本身尚未被定義assign值,所以出錯
step2.執行到 = function(){}才將此匿名函式assign給do

1
2
3
4
doIt();
var doIt = function(){
console.log('finish');
}

全域與區域

only functions created new scopes

  • 全域 (global)

    宣告在function()之外為全域變數,可以在全域被呼叫,所以在function()內、外呼叫x都不會出錯

    1
    2
    3
    4
    5
    6
    var x = 'x';
    myF();
    function myF(){
    console.log("in:"+x);//in:x
    }
    console.log("out:"+x);//out:x
  • 區域 (local)

    區域變數只能作用在function()區域中,出了function()則無法被呼叫

    1
    2
    3
    4
    5
    6
    7
    8
    9
    myF();
    function myF(){
    let y = 'y';
    var z = 'z';
    console.log("in:"+y);//in:y
    console.log("in:"+z);//in:z
    }
    console.log("out:"+y);//undefine
    console.log("out:"+z);//undefine

若在區域中,重新定義assign給全域變數,則該全域變數的值會改變

1
2
3
4
5
6
7
var x = 'x1';
myF();
function myF(){
x = 'x2';
console.log("in:"+x);//"in:x2"
}
console.log("out:"+x);//"in:x2"

若在區域中,新增一個同名的變數,則其與外部的全域變數為兩個不相干的變數

1
2
3
4
5
6
7
8
console.clear();
var x = 'x1';
myF();
function myF(){
let x = 'x2'
console.log("in:"+x);//"in:x2"
}
console.log("out:"+x);//"in:x1"

區域內的變數,不得與function()中的傳入參數的命名相同

1
2
3
4
function myF(x){
let x = 'x2'//x已經被宣告過了
console.log("in:"+x);
}

引數(argument)與參數(parameter)

  • 引數:用於呼叫函式的傳入「值」

  • 參數:在函式中用來接引數所宣告的「變數」

    1
    2
    3
    4
    plus(1,2,5);// 1,2,5為引數
    function plus(a,b,c){ //a,b,c為參數
    console.log(arguments)
    }

    參數預設值(Default Parameters)

  • 直接在參數中建立預設值
    1
    2
    3
    4
    5
    6
    var plus = function(a=4,b=5){
    return a*b;
    }
    console.log(plus(2,3));//6
    console.log(plus(2,));//10
    console.log(plus());//20

閉包(Closure)

呼叫函式內的函式,將記憶體封存在內層

1
2
3
4
5
6
7
function initial(newMoney){
var money = newMoney || 100
return function(price){// 這裡是一個閉包
money = money - price;//變數money封存於此記憶體內
return money;
}
};

執行結果:
p1與p2兩個變數分別創造兩個函式,因此也創造個別的兩個閉包,而閉包內的money值會分別存於各自的記憶體內

若直接執行initial(),其回傳值是一個函式,因此會獲得該匿名函式

遞迴(Recursion)

在函式中呼叫自身同名函式,呼叫者(外層函式)會被先放入堆疊記憶體中,等到被呼叫者(內層函式)被執行完之後,再從堆疊記憶體中取出之前被放入的函式繼續執行。==堆疊(stack) 是「先進後出」的資料結構== ,也因此,最先被呼叫的函式,會最後被取出執行。

1
2
3
4
5
6
7
8
function fun(num){
if(num===0){
return 1;
}else{
return num*fun(num-1)
}
}
fun(3);//6

執行解讀順序,

1
2
3
4
5
6
7
3*fun(2);
2*fun(1);
1*fun(0);
1;
1*1;
2*1;
3*2;

© 2020 Leah's Blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero