【重溫基礎】4.函數

本文是 重溫基礎 系列文章的第四篇。
今日感受:常懷感恩之心,對人對己。

系列目錄:

本章節複習的是JS中的基礎組件之一,函數,用來複用特定執行邏輯。

1.定義函數

定義函數有兩種方法:函數聲明函數表達式

1.1 函數聲明

也成爲函數聲明,通常格式爲:

function f (a){
    return a + 1;
}

解釋:這裏聲明一個函數 f ,並傳入一個參數 a ,當函數執行以後,通過 return 關鍵字返回了 a+1的值。

參數
當傳入的參數是一個數字/字符串等具體的值的時候,若參數的值被改變,不會影響到全局或調用函數。
但如果參數是個對象,若函數內改變的這個參數的屬性,則函數外部的這個參數原始的值會被修改。

var leo = {
    age:20
}
function f(obj){
    obj.age = 15;
    obj.name = 'leo';
}
f(leo);
console.log(leo); //{age: 15, name: "leo"}

1.2函數表達式

通過定義一個匿名的函數,來賦值給一個變量,通過這個變量來調用這個函數。

var f = function (a){
    return a + 1;
}

但是函數表達式也可以提供函數名,用於函數內部調用,並指代本身,也可以作爲調試器堆棧跟蹤中識別該函數。

var f = function g(a){
    return n < 2 ? 1 : a*g(a-1);
}

另外,函數表達式聲明可以用來根據不同條件,來定義一個函數:

var f;
if(a == 1){
    f = function (){
        return 'when a == 1';
    }
}else {
    f = function (){
        return 'when a != 1';
    }
}

2.函數調用

函數定義完成後不會自動執行,需要我們通過函數名稱來調用,才能真正執行:

var f = function (){
    console.log('ok');
}
f(); // 'ok'

另外,函數也可以調用自身,這就是遞歸過程:

function f (n){
    if( n==0 || n==1) {
        return 1;
    }else {
        return n * f(n-1);
    }
}
// 三目運算
function f (n){
    return (n==0 || n==1)?1: n*f(n-1);
}

3.函數作用域

由於函數只在函數的內部有定義,所以函數內部定義的變量在函數外部不能訪問,函數內部就是這個函數的作用域。
當一個父級函數內,還定義了一個子級函數,則這個子級函數可以訪問父級函數定義的變量。

// 全局作用域 global scope
var a = 1, b = 2;
function f (){
    return a + b;
}
f(); // 3

function g(){
    var a1 = 'leo', b1 = 'pingan';
    function hi (){
        return a1 + '和' + b1
    } 
    return hi();
}
g(); // 'leo和pingan'

3.1 閉包

閉包是 JavaScript 中最強大的特性之一,並且JS允許函數嵌套。
在一個函數內部在嵌套一個函數,而嵌套的這個函數對外面的函數是私有的,則形成一個閉包,閉包是一個可以自己擁有獨立的環境和變量的表達式,通常是函數。
理解一下,前面說的內部函數可以調用外部函數的變量和方法,那麼可以這麼理解:閉包的函數繼承了父級容器函數的參數和變量,即內部函數包含外部函數的作用域
總結一下:

  • 內部函數只能在外部函數中訪問;
  • 內部函數形成閉包:可以訪問外部函數的參數和變量,但外部函數卻不能使用這個內部函數的參數和變量;
function f(a) {
    function g(b){
        return a + b;
    }
    return g;
}
var a1 = f(5);  // ƒ g(b){ return a + b; }
var a2 = a1(6); // 11
var a3 = f(5)(6); // 11

閉包可以給內部函數的變量提供一定的安全保障
另外,閉包還有複雜的用法:

var f = function (name){
    var age ;
    return {
        setName : function (newName){
            name = newName;
        },

        getName : function (){
            return name;
        },

        getAge : function (){
            return age;
        },
        setAge : function (newAge){
            age = newAge;
        }
    }
}

var leo = f('leo');
leo.setName('pingan');
leo.setAge(20);
leo.getName();     // 'pingan'
leo.getAge();      // 20

3.2命名衝突

在同一個閉包作用域下若參數或變量名相同,產生衝突,則優先使用作用域最近

function f(){
    var a = 1;
    function g(a){
        return a + 1;
    }
    return g;
}
f()(3); // 4

4.arguments對象

函數的實際參數會被保存在一個類數組對象 arguments 對象中,通過索引訪問具體的參數:

var a = arguments[i]

arguments的索引從0開始,也有arguments.length屬性獲取長度。
當我們不知道參數的數量的時候,可以使用arguments.length來獲取實際傳入參數的數量,再用arguments對象來獲取每個參數。
例如:

// 拼接所有參數爲一個字符串
// 參數 s 爲分隔符
function f( s ){
    var text = '';
    for(var i = 0;i<= arguments.length; i++){
        text += arguments[i] + s ;
    }
    return text;
}

f('--','leo','pingan','robin');
// "----leo--pingan--robin--undefined--"
f('**','leo','pingan','robin');
// "****leo**pingan**robin**undefined**"

5.函數參數

ES6開始,新增兩個類型的參數:默認參數剩餘參數

5.1默認參數

若函數沒有傳入參數,則參數默認值爲undefined,通常設置參數默認值是這樣做的:

// 沒有設置默認值
function f(a, b){
    b = b ? b : 1;
    return a * b;
}
f(2,3);  // 6
f(2);    // 2

// 設置默認值
function f(a, b = 1){
    return a * b;
}
f(2,3);  // 6
f(2);    // 2

5.2剩餘參數

可以將參數中不確定數量的參數表示成數組,如下:

function f (a, ...b){
    console.log(a, b);
}
f(1,2,3,4); // a => 1 b => [2, 3, 4]

6.箭頭函數

函數箭頭表達式是ES6新增的函數表達式的語法,也叫胖箭頭函數,變化:更簡潔的函數和this

  • 更簡潔的函數
// 有1個參數
let f = v => v;
// 等同於
let f = function (v){return v};

// 有多個參數
let f = (v, i) => {return v + i};
// 等同於
let f = function (v, i){return v + i};

// 沒參數
let f = () => 1;
// 等同於
let f = function (){return 1};

let arr = [1,2,3,4];
arr.map(ele => ele + 1);  // [2, 3, 4, 5]
  • this
    注意這幾點:
    1. 箭頭函數內的this總是指向定義時所在的對象,而不是調用時。
    2. 箭頭函數不能當做構造函數,即不能用new命令,否則報錯。
    3. 箭頭函數不存在arguments對象,即不能使用,可以使用rest參數代替。
    4. 箭頭函數不能使用yield命令,即不能用作Generator函數。

一個簡單的例子:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++;
  }, 1000);
}
var p = new Person(); // 定時器一直在執行 p的值一直變化

參考資料

1.MDN 函數


本部分內容到這結束

Author 王平安
E-mail [email protected]
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 https://github.com/pingan8787/Leo_Reading/issues
ES小冊 es.pingan8787.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章