函數的拓展

本系列屬於阮一峯老師所著的ECMAScript 6 入門學習筆記javascript


函數參數的默認值
function Point(x =0,y=0){
  this.x = x
  this.y = y
}

const p = new Point()
p // {x:0,y:0}

// 參數變量是默認聲明的,因此不能用let或const再次聲明
function(x = 5){
  let x = 1 // error
  const x = 2 // error
}

// 使用參數默認值時,函數不能有同名參數
function foo(x,x,y=1){
    // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

// 參數默認不是傳值的,而是每次都從新計算默認值表達式的值。也就是說,參數默認值時惰性求值
let x = 99
function foo(p = x + 1){
  console.log(p)
}

foo() // 100

x=100
foo() // 101
參數默認值與解構賦值默認值結合使用
function(x,y=5){
  console.log(x,y)
}

foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined

// 若是調用foo時沒提供參數,變量x和y就不會生成。經過提供函數參數的默認值,能夠避免
function foo({x,y=5}={}){
  console.log(x,y)
}

foo() // undefined 5
參數默認值的位置
// 一般狀況下,定義了默認值的參數,應該是函數的尾參數。若是非尾部設置默認值,實際上這個參數是無法省略的
// 例一
function(x=1,y){
  return [x,y]
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]

//例二
function f(x,y=5,z){
  return [x,y,z]
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
// 上面代碼中,有默認值的參數都不是尾參數。這時,沒法只省略該參數,而不省略後面的參數,除非顯式輸入undefined,null沒有這個效果
函數的length屬性

指定了默認值以後,函數的length屬性將返回沒有指定默認值的參數個數。java

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

這是由於length屬性的含義是,該函數預期傳入的參數個數。某個參數指定默認值之後,預期傳入的參數個數就不包括這個參數了。同理,rest 參數也不會計入length屬性。es6

(function(...args) {}).length // 0

若是設置了默認值的參數不是尾參數,那麼length屬性也再也不計入後面的參數了。數組

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
做用域

一旦設置參數的默認值,函數進行聲明初始化時,參數會造成一個單獨的做用域(context)。等到初始化結束,這個做用域就會消失。這種語法行爲,在不設置參數默認值時,是不會出現的。瀏覽器

let x = 1
function f(x,y=x){
  console.log(y)
}
f(2) // 2

let x = 1
function f(y=x){
  let x = 2
  console.log(y)
}
f() // 1

// 若此時全局變量x不存在,就會報錯。若參數寫成x=x造成單獨做用域,會造成暫時性死區報錯

若是函數的默認值是一個函數,該函數的做用域也遵照這個規則函數

let foo = 'outer'

function bar(func = () => foo){
  let foo = 'inner'
  console.log(func())
}
bar() // outer
rest參數

ES6引入rest參數(形式爲...變量名),用於獲取函數的多餘參數,這樣就不須要使用arguments對象了。rest參數搭配的變量是一個數組,改變量將多餘的參數放入數組中。學習

function add(...values){
  let sum = 0
  for(let val of values){
    sum += val
  }
  return sum
}

add(2,3,5) // 10

// 注意,rest參數以後不能再有其餘參數,不然會報錯
// 函數的length屬性,不包括rest參數
嚴格模式

從ES5開始,函數內部尅設定爲嚴格模式。ES6作了一點修改,規定只要函數參數使用了默認值、解構賦值、或者拓展運算符,那麼函數內部就不能設定爲嚴格模式,不然會報錯。this

// 報錯
function doSomething(value = 070){
  'use strict'
  return value
}

// 兩種方法能夠規避這種限制
// 1、設定全局性的嚴格模式
'use strict'
function doSomething(a,b=a){
  // code
}

// 2、把函數包在一個無參數的當即執行函數裏面
const doSomething = (function(){
  'use strict'
  return function(value = 42){
    return value
  }
}())
name 屬性

函數的name屬性,返回該函數的函數名。這是一個早就被瀏覽器支持,直到ES6才寫入標準的屬性rest

function foo(){}
foo.name // 'foo'

// 對於匿名函數,ES6的name屬性會返回實際的函數名,ES5則返回空字符串
var f = function(){}

// ES5
f.name // ''

// ES6
f.name // 'f'

// 對於具名函數,ES5和ES6的name屬性都返回這個具名函數本來的名字

// Function構造函數返回的函數實例,name屬性的值爲anonymous
(new Function).name // 'anonymous'

// bind返回的函數,name屬性值會加上bound前綴
function foo(){}
foo.bind({}).name // 'bound foo'
(function(){}).bind({}).name // 'bound '
箭頭函數

ES6容許使用」箭頭「(=>)定義函數。code

var f = v => v

//等同於
var f = function(v){
  return v
}

// 無參數或多參數寫法
var f = () => 5
var sum = (num1,num2) => num1 + num2

// 返回對象
let getTempItem = id => ({id:id,name:'Temp'})

// 只有一行語句,且不須要返回值
let fn = () => void doesNotReturn()

// 與變量解構結合使用
const full = ({first,last}) => first + '' + last

// 與rest參數結合
const numbers = (...nums) => nums
numbers(1,2,3,4,5) // [1,2,3,4,5]

const headAndTail = (head,...tail) => [head,tail]
headAndTail(1,2,3,4,5) // [1,[2,3,4,5]]

使用注意點

箭頭函數有幾個使用注意點。

(1)函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

(2)不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。

(3)不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替。

(4)不可使用yield命令,所以箭頭函數不能用做 Generator 函數。

相關文章
相關標籤/搜索