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) } }