函数的拓展
参数的默认值
在ES6之前,函数传入的参数没有默认值这一说法,所以我们经常要进行一些参数校验
function log(x, y) { if (typeof y === 'undefined') { y = 'world' } console.log(x, y) } log('hello')// hello world log('hello', '') // hello log('hello', 'abc')//hello abc复制代码
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'world') { console.log(x, y) } log('hello') //hello world log('hello', '')//hello log('hello', 'abc')//hello abc复制代码
与解构赋值默认值结合使用
参数默认值可以与解构赋值的默认值,结合起来使用。
function foo({ x, y = 5}) { console.log(x, y) } foo({x:1,y:2}) //1,2 foo({})// undefined 5 foo()// TypeError: Cannot destructure property `x` of 'undefined' or 'null'.复制代码
函数的length属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2 复制代码
rest参数
ES6 引入rest参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...val) { let sum = 0 sum = val.reduce(((pre, cru) => { return pre + cru })) console.log(sum)}add(1, 2, 3) } add(1,2,3)复制代码
name属性
函数的name属性,返回该函数的函数名
function add(...val) { ···} add.name //add复制代码
箭头函数
let f = v => v //相当于 let f = function (v) { return v }复制代码
如果需要多个参数,则可以把参数放入圆括号中
let add = (num1, num2) => num1 + num2
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sum = (num1, num2) => { return num1 + num2; }
注意点
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
- 不可以使用yield命令,因此箭头函数不能用作Generator函数。
尾递归
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120复制代码
上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度O(n)。
如果改写成尾递归,只保留一个调用记录,复杂度O(1)。
function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120复制代码
数组的拓展
拓展运算符
含义
扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
console.log(...[1,2,3]) //1 2 3
console.log(1,...[2,4,5],6) //1 2 4 5 6复制代码
替代apply方法
下面是一个利用Math.max求最大值的例子
//ES5
Math.max.apply(null, [1, 2, 3]),//3
//ES6
Math.max(...[1, 2, 3])//3复制代码
拓展运算符的运用
- 复制数组
let arr1 = [1,2] let arr2 = arr1 arr2[1] = 'a' console.log(arr1,arr2) //[ 1, 'a' ] [ 1, 'a' ]复制代码
数组是一个复杂类型,直接用等号赋值的话实际上只是复制了它的指针,即arr1、arr2指向的是同一块内存。
//ES5 let arr1 = [1,2] let arr2 = arr1.concat() //拓展运算符 let arr1 = [1,2] let arr2 = [...arr1]复制代码
- 合并数组
扩展运算符提供了数组合并的新写法。
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]复制代码
不过,这两种方法都是浅拷贝,使用的时候需要注意。
const a1 = [{ foo: 1 }]; const a2 = [{ bar: 2 }]; const a3 = a1.concat(a2); const a4 = [...a1, ...a2]; a3[0] === a1[0] // true a4[0] === a1[0] // true复制代码
Array.from
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
下面是一个类似数组的对象,Array.from将它转为真正的数组。
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5的写法 var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c'] // ES6的写法 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']复制代码
Array.of()
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1复制代码
entries(),keys() 和 values()
ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) { console.log(index); } // 0 // 1 for (let elem of ['a', 'b'].values()) { console.log(elem); } // 'a' // 'b' for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"复制代码