深入理解ES6 ---- 函式

漓漾li發表於2019-03-25

函式引數預設值

(1) es5中模擬引數預設值:

  function fn(name, count){
    name = name || 'xiao ming'
    count= count|| 1
    console.log(name, count)
}
複製程式碼

這樣可以為沒有傳入的引數,設定為預設值。這種方式的問題:如果count傳入0,即使這個值是合法的,但也會被設定為預設值1。 改進後:

  function fn(name, count){
    name = typeof name !== 'undefined' ?  name : 'xiao ming'
    count = typeof count !== 'undefined' ?  count : 1
    console.log(name, count)
}
複製程式碼

(2) es6的做法:

function fn(name = 'xiao ming', count = 1){
   console.log(name, count)
}
// name使用預設值
fn(undefined, 18)
複製程式碼

掃盲: null是一個有效引數,傳入null並不會使用預設引數

(3) 命名引數與arguments的關係

es5非嚴格模式下,修改命名引數,會同步更新到arguments物件中; 而在es6或者嚴格模式中,命名引數和arguments是分離的

  function fn1(name, count) {
    name = 'li si'
    count = 5
    console.log(arguments[0])
    console.log(arguments[1])
}
fn1('xiao wang', 4)
// 非嚴格模式下
// li si
// 5

// 嚴格模式下或es6環境中
// xiao wang
// 4
複製程式碼

(4) 函式預設表示式

function count(){return 6}
function fn1(name, count = count()) {
    console.log(name)
    console.log(count)
}
fn1('xiao wang')
// 'xiao wang'
// 5
複製程式碼

掃盲: 1. 引數、引數預設值、引數預設表示式都在同一個獨立的作用域內; 2. 引數作用域中存在臨時性死區,即後面的引數可以引用到前面的引數,反之不可以; 3. 引數作用域引用不到函式體內的變數

證明獨立作用域的例子:

function fn(a, cb = () => { a.b = 1; console.log(a) }) {
    var a = { a: 1 }
    cb()
    console.log(a)
}
fn({ c: 3 })
// { c: 3, b: 1 }
// { a: 1 }
複製程式碼

cb執行時,獲取到的物件a並不是函式體內新定義的,而是傳入的引數a,所以他們是在同一獨立作用域的

無名引數

(1) es5中使用arguments獲取不定數量的引數

// 如果沒有註釋,很難發現函式需要傳入無命名引數
function fn(arr){
    // i 從 1開始,需要跳過 arr這個命名引數
    for(let i = 1, len = arguments.length; i < len; i++){
        arr.push(arguments[i])
    }
    return arr;
}
console.log(fn([3],4,5,6,7))  // [3,4,5,6,7]
複製程式碼

存在的問題:

  1. 並不容易發現函式,需要傳入不定量引數
  2. 如果有命名引數,arguments中既包含命名引數也包含無名引數,還需要拆分獲取

(2) es6中,使用不定引數

在引數前加三個點...,表明這是一個不定引數,這個引數為陣列,包含著自它以後傳入的引數。 並且解決了上述es5中的問題

function fn(arr, ...items){
    for(let i = 0, len = items.length; i < len; i++){
        arr.push(items[i])
    }
    return arr;
}
console.log(fn([3],4,5,6,7))   // [3,4,5,6,7]
複製程式碼

不定引數的限制:

  1. 每個函式只能定義一個不定引數,而且只能處於所有引數末尾。錯誤示範:function fn(a, ...b, c){}
  2. 物件的setter函式不能使用不定引數。錯誤示範:let obj = {set name(...value){}}

掃盲:

  1. 無論是否使用不定引數,arguments總是包括所有傳入的引數
  2. 函式的**length屬性**,表示命名引數的數量,跟不定引數無關

函式的name屬性

  1. function fn(){} name為fn
  2. let fn = function (){} name為fn
  3. let fn = function fn1(){} name為fn1因為函式名比被賦值的變數權重高
  4. let obj = {get firstName(){}, sayName(){}}obj.firstName為getter函式,所以name為get firstNameobj.sayNamename為sayName
  5. 通過bind()建立的函式,name會帶有bound字首。fn.bind()name為bound fn
  6. 通過Function()建立的函式,name為anonymousnew Function() name為anonymous

掃盲:函式name屬性不一定能獲取對函式的引用,它只是協助除錯用的額外資訊

函式的多重用途

箭頭函式

相關語法就不再贅述

與普通函式的區別:

  • 沒有this、super、arguments繫結,這些值都由外層的非箭頭函式決定
  • 不能使用new關鍵詞呼叫;沒有原型;函式體內沒有new.target。因為箭頭函式沒有[[construct]]方法,所以不能當做建構函式呼叫。
  • 不可以改變this指向。箭頭函式的this指向,只跟函式定義時的環境有關,跟使用環境無關,call apply bind也無力改變
  • 不可以使用yield命令,因此箭頭函式不能用作 Generator 函式。
  • 不支援重名引數。es6與es5嚴格模式下都不支援函式引數重名。
// es5非嚴格模式下,引數重名情況
 function fn(a,b,a){
   console.log(a)
   console.log(b)
   console.log(arguments[0])
   console.log(arguments[1])
   console.log(arguments[2])
   a = 4
   console.log(arguments[0])  
   console.log(arguments[1]) 
   console.log(arguments[2]) 
 }
 fn(1,2,3)
//3
//2
//1
//2
//3
//1
//2
//4
// 即 arguments[0]與 a解除了相關性
複製程式碼

雙冒號運算子

函式繫結運算子是並排的兩個冒號(::),雙冒號左邊是一個物件,右邊是一個函式。該運算子會自動將左邊的物件,作為上下文環境(即this物件),繫結到右邊的函式上面。

foo::bar;
// 等同於
bar.bind(foo);

foo::bar(...arguments);
// 等同於
bar.apply(foo, arguments);
複製程式碼

相關文章