1. 程式人生 > >JavaScript教程筆記(7)-函式

JavaScript教程筆記(7)-函式

1 概述

函式是一段可以反覆呼叫的程式碼,它接受不同的輸入引數,返回不同的值。

1.1 函式的宣告

有兩種常用的宣告方法。

(1) function命令

function 命令宣告的程式碼區塊,就是一個函式。function 命令後面是函式名,函式名後面是一對圓括號,裡面是傳入函式的引數,函式體放在大括號裡面。

function print(s) {
    console.log(s);
}

上面程式碼命名了一個 print 函式,以後使用 print() 就可以呼叫相應程式碼。這叫做函式的宣告。

(2)函式表示式

這種寫法將一個匿名函式賦值給變數。因為賦值語句的等號右側只能放表示式,所以這個匿名函式又稱為函式表示式。

var print = function(s) {
    console.log(s);
};

需要注意的是,函式表示式需要在語句的結尾加上分號,表示語句結束。

1.2 函式名的提升

JavaScript中函式名相當於變數名,所以採用 function 命令宣告函式時,整個函式會像變數一樣被提升到程式碼頭部。

f();

function f() {}

上面的程式碼不會報錯。

但是,如果採用賦值語句定義函式,這樣就會報錯。

f();

var f = function() {}; // Error

上面程式碼呼叫 f 時,f 還沒有被賦值,因此報錯。

2 函式的屬性和方法

2.1 name屬性

函式的 name 屬性返回函式的名字。

function f1() {}
f1.name // "f1"

name 屬性的一個用處是獲取引數函式的名字。

var myFunc = function() {};
function test(f) {
    console.log(f.name);
}
test(myFunc) // myFunc

2.2 length屬性

函式的 length 屬性返回函式定義之中的引數個數。

function f(a, b) {}
f.length // 2

上面程式碼定義了帶2個引數的空函式 f,不管呼叫時傳入幾個引數,length 屬性始終等於2。

2.3 toString()

函式的 toString 方法返回一個字串,內容是函式的原始碼。包括函式內部的註釋也可以返回。

3 函式的作用域

3.1 定義

作用域(scope)指的是變數存在的範圍。在ES5規範中有兩種作用域。一種是全域性作用域,變數在整個程式一直存在,所有地方都可以訪問;另一種是函式作用域,變數僅在函式內部存在。

函式外部宣告的變數就是全域性變數,它也可以在函式內部訪問。

var a = 1;
function f() {
    console.log(a);
}
f() // 1

上面程式碼表明,函式 f 內部可以讀取全域性變數 a。

在函式內部定義的變數,外部無法讀取,稱為“區域性變數”。

function f() {
    var a = 1;
}
a // Error

上面程式碼表明,變數 a 在函式內部定義,函式外部無法訪問區域性變數 a。

3.2 函式內部的變數提升

函式內部也存在“變數提升”,即變數宣告無論在函式什麼位置,都會被提升到函式體的頭部。

function f(a) {
    if (a > 10) {
        var tmp = a - 10;
    }
}
// 等同於
function f(a) {
    var tmp; // 變數提升
    if (a > 10) {
        tmp = a - 10;
    }
}

3.3 函式本身的作用域

函式本身有自己的作用域,就是其宣告時所在的作用域,而不是其執行時所在的作用域。

var a = 1;
var x = function() {
    console.log(a);
}
function f() {
    var a = 2;
    x();
}
f() // 1

上面程式碼中,函式 x 是在函式 f 的外部宣告的,它的作用域在外層,不會到 f 函式體內取值,所以輸出1,而不是2。

如果去掉第一行的 var a = 1,將會報錯,因為找不到變數 a。

4 函式的引數

4.1 概述

函式執行的時候,有時需要傳入不同的外部資料來得到不同的結果,這種外部資料就叫做引數。

function add(a, b) {
    return a + b;
}
add(1, 2); // 3

上面程式碼中的a和b就是add函式的引數。

4.2 引數的省略

引數不是必需的,即使函式宣告時定義了引數,但執行時無論提供多少個引數(或者不提供),都不會報錯。省略的引數的值等於 undefined。

但是,沒有辦法省略靠前的引數,而只保留靠後的引數。如果一定要省略,只能顯式地傳入 undefined。

function f(a, b) {
    return a;
}
f( , 1); // Error
f(undefined, 1); // undefined

4.3 引數傳遞方式

函式引數如果是原始型別的值(數值、字串、布林值),傳遞方式是傳值。即在函式體內修改引數值,不會影響到函式外部。

如果是複合型別的值(陣列、物件、其它函式),傳遞方式是傳址。即在函式體內修改引數值,將會影響到函式外部。

但是,如果函式內部替換掉整個引數,這時不會影響到原始值。

var obj = [1, 2, 3];
function f(o) {
    o = [4, 5, 6];
}
f(obj); 
obj // [1, 2, 3]

上面程式碼中,obj在函式內部被整個替換成另一個值,這時形參o指向的是另一個記憶體地址,所以原始地址的obj值不會改變。

4.4 同名引數

如果有同名的引數,則取最後出現的那個值。

function f(a, a) {
    console.log(a);
}
f(1, 2) // 2

上面程式碼中,函式f有兩個同名引數a,以後面的a為準。

4.5 arguments 物件

4.5.1 定義

函式允許有不定數目的引數,而 arguments 物件包含了函式執行時的所有引數。arguments[0]是第一個引數,arguments[1]是第二個引數,以此類推。

var f = function(obj) {
    console.log(arguments[0]); // 1
    console.log(arguments[1]); // 2
    console.log(arguments[2]); // 3
}
f(1, 2, 3) 

注意,arguments 物件只有在函式體內部才可以使用。

通過 arguments 物件的 length 屬性,可以判斷函式呼叫時到底帶幾個引數。

function f() {
    return arguments.length;
}
f(1, 2, 3) // 3
f(1) // 1
f() // 0
4.5.2 與陣列的關係

需要注意的是,雖然 arguments 很像陣列,但它實際是一個物件。所以陣列專有的方法(比如slice和forEach),不能在 arguments 物件上直接使用。

注:本文適用於ES5規範,原始內容來自 JavaScript 教程,有修改。