ES5中要爲參數指定默認值,只能以下所示:數組
function makeRequst(url, timeout, callback) { timeout = timeout || 2000; callback = callback || function () { } }
可是這樣有一個問題若是timeout傳進來的值爲0,則也會賦值變成2000,
因此更加徹底的作法是檢測參數類型安全
function makeRequst(url, timeout, callback) { timeout = (typeof timeout !== 'undefined') ? timeout : 2000; callback = (typeof callback !== 'undefined') ? callback : function () { } }
ES6中直接閉包
function makeRequst(url, timeout = 2000, callback = function () { }) { }
能夠爲任意參數指定默認值,在已指定默認值的參數後能夠繼續聲明無默認值參數app
function makeRequst(url, timeout = 2000, callback) { }
是否使用函數默認值主要依賴於調用函數是實參是否全等於undefined函數
ES5非嚴格模式下,函數命名參數的變化會體如今arguments對象中優化
function mixArgs(first, second) { console.log(first === arguments[0])//true console.log(second === arguments[1])//true first = 'c' second= 'd' console.log(first === arguments[0])//true console.log(second === arguments[1])//true } mixArgs('a', 'b')
而在嚴格模式下,不管參數如何變化,arguments對象再也不隨之改變this
function mixArgs(first, second) { 'use strict' console.log(first === arguments[0])//true console.log(second === arguments[1])//true first = 'c' second = 'd' console.log(first === arguments[0])//false console.log(second === arguments[1])//false } mixArgs('a', 'b')
若是使用了函數默認值,則arguments對象的行爲將與ES5在嚴格模式下保持一致:arguments對象保持與命名參數分離(備註:其實這種分離的特性能夠將參數恢復爲初始值)url
function mixArgs(first, second='b') { console.log(first === arguments[0])//true console.log(second === arguments[1])//false first = 'c' second = 'd' console.log(first === arguments[0])//false console.log(second === arguments[1])//false } mixArgs('a')
函數默認值除了能夠給原始值,還能夠指定函數,只不過只有未傳入參數須要默認值時才能去調用些此函數,也就是說默認參數是在函數調用時才求值
指定默認值爲函數不要忘記小括號,若是忘記小括號則傳入的是對函數的引用而不是函數調用的結果spa
function getValue(value) { return value + 5 } function add(first, second = getValue(first)) { return first + second } console.log(add(1))//7
函數參數有本身的做用域和臨時死區,其與函數體的做用域是各自獨立的,也就是說參數的默認值是不可訪問函數體內聲明的變量prototype
在函數命名參數前添加...三個點代表這是一個不定參數
function pick(obj, ...keys) { let result = Object.create(null) for (let i = 0, len = keys.length; i < len; i++) { result[keys[i]] = object[keys[i]] } return result }
函數的length屬性統計的是函數命名參數的數量,不定參數的加入不會影響length屬性的值
每一個函數最多隻能聲明一個不定參數,並且必定要放在全部參數的末尾
不定參數不能用於對象字面量setter之中,由於對象字面量setter的參數有且只能有一個
let obj = { //Uncaught SyntaxError: Setter function argument must not be a rest parameter set name(...value) { console.log(value) } }
看一個有趣的例子:不管是否使用不定參數,arguments對象老是包含全部傳入函數的參數
默認參數和不定參數的特性一樣適用於Function構造函數
var add = new Function("first", "second=first", "return first+second") console.log(add(1))//2 var pickFirst=new Function("...args","return args[0]") console.log(pickFirst(1,2))//1
舉例來講,Math.max可接受任意數量的參數並返回最大的那個,但若是傳入的是一個數組,則只能使用apply
console.log(Math.max(11, 2, 3, 12, 43, 904, 3543, 43)) let values = [11, 2, 3, 12, 43, 904, 3543, 43] console.log(Math.max.apply(Math, values))
使用展開運算符就變得特別簡單了
let values = [11, 2, 3, 12, 43, 904, 3543, 43] console.log(Math.max(...values))
若是你想限定Math.max返回的最小值爲0,還能夠以下使用
let values = [-11, -2, -3, -12] console.log(Math.max(...values, 0))
函數的name屬性的值不必定引用同名變量,它只是協助調用用的額外信息,因此不能使用name屬性的值來獲取函數的引用
function doSomething() { } console.log(doSomething.name)//doSomething var doSome = function doSomeElse() { } var person = { get firstName() { return 'angela' }, sayName: function () { } } console.log(doSome.name)//doSomeElse console.log(person.sayName.name)//sayName console.log(person.firstName.name)//undefined var doThing = function () { } console.log(doThing.bind().name)//bound doThing console.log((new Function()).name)//anonymous
JS函數中有兩個不一樣的內部方法:[[Call]]和[[Construct]]
當經過new關鍵字調用函數時,執行的是[[Construct]]函數,它負責建立一個一般被稱做實例的新對象,而後再執行函數體,將this綁定到實例上
若是不是經過new關鍵字調用函數,則執行[[Call]]函數,從而直接執行代碼中的函數體
不是全部函數都有[[Construct]]方法,所以不是全部方法均可以經過new來調用,具備[[Construct]]方法的函數被統稱爲構造函數
ES5中想肯定一個函數是否經過new關鍵字被調用
function Person(name) { if (this instanceof Person) { this.name = name } else { throw new Error('必須經過new關鍵字來調用') } }
可是上述方法不是絕對徹底可靠的,比方說以下調用就失效了
var person = new Person('angela') var notAPerson = Person.call(person, 'Shing')//這樣對於函數自己是沒法區分是經過Person.Call、Apply仍是new調用獲得的實例
ES6中能夠以下絕對安全的斷定
function Person(name) { //或者typeof new.target===Person if (typeof new.target !== 'undefined') { this.name = name } else { throw new Error('必須經過new關鍵字來調用') } } var person = new Person('angela') var notAPerson = Person.call(person, 'Shing')//拋出錯誤
當調用函數的[[Construct]]方法時,new.target被賦值爲new操做符的目標,若是調用的是[[Call]]方法,則new.target的值爲undefined
在函數外使用new.target是一個語法錯誤
ES5嚴格模式下,在代碼塊內部聲明函數程序會報錯
在ES6嚴格模式下,能夠在代碼塊中聲明函數,塊級函數聲明會被提高至此代碼塊頂部,超出此塊級做用域,則函數將再也不存在
'use strict' if (true) { console.log(typeof doSomeThing) //function doSomeThing()//---------- function doSomeThing() { console.log('----------') } } console.log(typeof doSomeThing) //undefined
在ES6非嚴格模式下,這些函數再也不提高至代碼塊的頂部而是提高至外圍函數或全局做用域的頂部
if (true) { console.log(typeof doSomeThing) //function doSomeThing()//---------- function doSomeThing() { console.log('----------') } } console.log(typeof doSomeThing) //function
箭頭函數與傳統函數有以下幾點不一樣
當箭頭函數只有一個參數時,能夠直接寫參數名,箭頭緊隨其後,箭頭右側的表達式被求值後便當即返回,即便沒有顯式的返回語句,
若是要傳入兩個或兩個以上參數則須要在參數兩側添加一對小括號
若是函數沒有參數,也要在聲明時寫一組沒有內容的小括號
let sum = (num1, num2) => num1 + num2 //至關於 let sum = function (num1, num2) { return num1 + num2 }
尾調用指的是函數做爲另外一個函數的最後一條語句被調用,其實也就是知足如下三個條件
ES6嚴格模式下,JS引擎纔會進行尾調用自動優化
function f(x){ return g(x); }
function factorial(n) { if (n <= 1) { return 1 } else { //沒法優化,必須在返回後執行乘法操做 return n * factorial(n - 1) } } function factorial(n, p = 1) { if (n <= 1) return 1 * p else { let result = n * p; //優化後 return factorial(n - 1, result) } }