函式引數的預設值
基本用法
在ES6之前,我們是這樣來做的
function log( x, y){
y = y || 'world'
console.log(x, y)
}
log('hello') //hello world
log('hello', 'china') // hello china
log('hello','') // hello world
複製程式碼
以上寫法會有一個問題,就是當我們傳進去一個y值,但是其對應的布林值為false,例如 log('hello','')
,結果被修改為了預設值! 所以,為了避免這個問題,我們替換如下語句
if(typeof y === 'undefined'){
y = 'world'
}
複製程式碼
在ES6裡面就簡單多了,可以直接在引數設定預設值
function log(x, y = 'world') {
console.log(x ,y)
}
log('hello') //hello world
log('hello', 'china') //hello china
log('hello','') // hello
複製程式碼
有一點需要注意 引數預設值不是傳值的,而是每次重新計算預設值表示式的值,引數預設值是“惰性求值的”
let x = 99
function foo(p = x + 1){
console.log(p)
}
foo() //100
x = 100
foo() //101
//引數 p 的預設值是 x+1 每次呼叫函式 foo 都會重新計算 x+1 ,而不是預設 p 等於 100
複製程式碼
注意:引數變數是預設宣告的,在函式體中,不能再用 let const 進行宣告,也 不允許有同名引數。
結合解構
function foo({x, y = 5}){
console.log(x, y)
}
foo({}) // undefined 5
foo({x:1}) // 1, 5
foo({x: 1, y: 3}) // 1, 3
foo() //報錯
複製程式碼
只有函式的引數是一個物件時,變數x y 才會通過解構賦值生成。如果函式呼叫時,引數不是物件,變數x y 就不會生成,會報錯。
在前面文章 重學ES6 解構我們他提到過函式引數的預設值,jQuery ajax的解構
這裡再用一個新的API寫一遍
function fetch(url,{body = '', metthod = 'GET', headers = {}}){
console.log( method )
}
fetch('http://example.com', {}) // GET
fetch('http://example.com') // 報錯
複製程式碼
這個寫法不能省略第二個引數~那就太不好了。。。所以,我們要再結合函式的預設值,就可以省略第二個引數了
function fetch(url,{body = '', metthod = 'GET', headers = {}} = {}){
console.log( method )
}
fetch('http://example.com', {}) // GET
fetch('http://example.com') // GET
複製程式碼
注意:在函式傳參解構中,只要傳入實際引數,那麼就拿傳入的實際引數進行解構,如果沒有傳入實際引數,且引數寫了預設值,函式引數得到值是 undefined 時,被賦予預設值。
// demo 1
function m1 ({x = 0,y = 0} = {}){
return [x, y]
}
// demo 2
function m2 ({x, y} = {x: 0, y: 0}){
return [x, y]
}
//x y 都無值
m1({}) //[0, 0]
m2({}) // 沒有寫預設值,對實參進行解構,[undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
複製程式碼
預設引數位置
通常,定義了預設值的引數應該是函式的尾引數。這樣可以看出到底省略了哪些引數。
function f(x, y, z = 1){
return [x, y, z]
}
f() // [undefined, undefined, 1]
複製程式碼
函式的 length
指定了預設值以後,函式的 length 屬性將返回沒有指定預設值的引數個數。指定了預設值後,length屬性將失真
(function (a) {}).length //1
(function (a = 5)).length // 0
(function (b ,c ,a = 5)).length // 2
複製程式碼
length 屬性 含義是 “函式預期傳入的參 數個數” ,某個引數指定預設值以後,函式傳入引數個數就不包含這個了,rest引數也不會計入length屬性
作用域
設定了引數的預設值後,函式進行初始化時,引數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法行為在不設定引數時是不會出現的。
var x= 1
function f(x, y = x){ //相當於 let x
console.log(y)
}
f(2)
複製程式碼
// 如果此時全域性 x 不存在,就報錯了
function f(y = x){ //相當於 let y = x
console.log(y)
}
f() // x is nit defined
複製程式碼
var x = 1
function f(x = x){ //相當於 let x = x 暫時性死區
console.log(x)
}
f() // x is nit defined
複製程式碼
如果引數是一個函式
let foo = 'ourter'
function bar (func = () => foo){ //在這裡,函式裡面的foo沒有定義,所以foo指向外層的 foo
let foo = 'inner'
console.log(func())
}
bar()
複製程式碼
又一個例子
此例子,x共有3個,全域性作用域下 foo函式引數作用域 還有 foo函式內部作用域,共三個,互不相同
var x =1
function foo(x, y = function() { x = 2}){
var x = 3
y()
console.log(x)
}
foo() // 3
x // 1
複製程式碼
但是,如果去掉 foo 函式 內部的 var x = 3
的 var,那麼就剩下兩個不同的x,即:全域性下的x 和 函式 foo 引數作用域下的x ,因為函式內部的 x 和 函式引數的x 已經是同一個x
var x =1
function foo(x, y = function() { x = 2}){
x = 3
y()
console.log(x)
}
foo() // 2
x // 1
複製程式碼
兩個例子都不會影響外部全域性變數x的值。
應用
引數預設值,可以指定某一個引數不能省略,如果省略了,就跑出一個錯誤
function throwIfMissing(){
throw new Error('Missing parameter')
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided
}
foo()
複製程式碼
rest 引數
es6 引入了 rest引數,(形式為 ...變數名),用於獲取函式的多餘引數,也就不需要使用 arguments 引數物件了。 rest引數搭配的變數是一個陣列,該變數將多餘的引數放入其中。
function add(...values){
let sum = 0
for(var val of values){
sum += val
}
return sum
}
add(1,2,3) // 6
複製程式碼
幾個例子
之前,用arguments的寫法
// Array.prototype.slice.call(arguments) 將引數轉化為陣列
// array.sort
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort()
}
// rest 引數的寫法
//...numbers 將numbers 轉化為陣列
const sortNumbers = (...numbers) => numbers.sort()
複製程式碼
rest引數改寫 push
function push(array,...items){
items.forEach(function(item){
array.push(item)
})
}
var a = []
push(a,1,2,3)
複製程式碼
rest 引數之後,不能再有其他引數,所以,rest只能是最後一個引數。而且,函式的length,不包括rest引數。