记录一下
ES6
对函数、对象、数组的扩展,作为开发参考。
函数扩展
和解构赋值默认值结合
1 | function foo({x, y = 5}) { |
只有当函数
foo
的参数是一个对象时,变量x
和y
才会通过解构赋值生成。
上面foo
函数使用的是对象解构赋值默认值,而不是函数参数,所以报错了。
提供函数参数默认值:
1 | function foo({x, y = 5} = {}) { |
注意
有默认值的函数参数如果不是尾参数,则不能忽略,否则报错,除非显示传入undefined,可以触发默认值,但是null不可以。
1 | function f(x, y = 5, z) { |
对函数对象的影响
使length属性失真
1
2
3
4(function (a, b, c = 5) {}).length // 2
/*如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。*/
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域(重点)
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(
context
)(不设置参数默认值不会出现这个作用域)先看个栗子
1
2
3
4
5
6
7
8
9var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x - 外部作用域x // 1
解释
foo函数形成一个单独作用域,y的默认值是一个函数,里面的变量x指向同一个作用域(即函数参数形成的单独作用域)的x,而foo函数里面也声明了一个x变量,但是由于不是同一个作用域,因此属于不同的变量。默认值函数也只是改变了同一作用域的x,并没有改变外部作用域和foo函数作用域的值。
改一下
1 | var x = 1; |
会发现默认函数生效了,全局变量依然不受影响。
解释
foo函数内部没有重新定义变量x,因此内部的x指向了函数参数x。
rest参数
形式
...变量名
先上栗子
1 | function push(array, ...items) { |
可以看到rest参数搭配的后面的变量为一个数组。
rest参数是用来代替arguments变量的。
1 | function sortNumbers() { |
箭头函数
使用注意
(1)不能用new
(2)不能作为Generator
函数
(3)不能使用arguments,用rest参数代替(arguments指向外层函数)
(4)this 指向是固定的
箭头函数可以让
setTimeout
里面的this
,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
1 | function Timer() { |
ES6
转成ES5
会发现箭头函数没有自己的this
,只是_this = this
引用了外层的this
。因此bind()、apply()、call()方法会失效。
箭头函数不适用场景
定义对象方法,且内部包括this。
需要动态this的时候。
看一个部署管道机制的栗子(前一个函数的输出是后一个函数的输入)
1 | const pipeline = (...funcs) => |
数组扩展
...
扩展运算符
好比rest的逆运算,讲一个数组转为用都好分割的参数序列
强大的运算符
可放置表达式
1 | const arr = [ |
空数组无效
1 | [...[], 1] |
一个实际应用
1 | // ES5 的写法 |
合并/复制数组为浅拷贝
与解构赋值结合
1 | const [first, ...rest] = [1, 2, 3, 4, 5]; |
像是逆运算一样
注意
扩展运算符用于数组赋值,只能在参数最后一位,否则报错。
Array.from()
基本用法
将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象。
一个类似数组的对象转化为数组
1 | let arrayLike = { |
和...
扩展运算符的区别
...
只能装换Iterable对象,Array.from
方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length
属性。数组的长度由length决定。
1 | Array.from({ length: 3 }); |
另外,Array.prototype.slice.call(obj)
能将具有length
属性的对象转换成数组,也要求
有length属性。
[].prototype.slice.call(obj)
第一眼看我有点懵。看看v8_array
源码[].slice()的解释
1 | let i, cloned = [], |
奥,我好了。
第二个参数
Array.from()
还可以接受第二个参数,用来处理每个元素,Array.from()
最后返回处理后的数组。
一个栗子
1 | Array.from({ length: 2 }, () => 'jack') |
如果第二个参数中用到了this,这个时候就可以利用第三个参数来绑定this了。
一个栗子
1 | const obj = { name: "myname", age: 19, }; |
arr.copyWithin(target, start = 0, end = this.length)
直接看栗子理解
1 | [1, 2, 3, 4, 5].copyWithin(0, 3); |
上面代码表示将从 3 号位直到数组结束的成员(4 和 5),复制到从 0 号位开始的位置,结果覆盖了原来的 1 和 2。
arr.find(callback)
用于找出第一个符合条件的数组成员
arr.fill()
使用指定值,填充数组
1 | ['a', 'b', 'c'].fill(0) |
使用第二、三个参数,制定填充起始位置。
1 | ['a', 'b', 'c'].fill(7, 1, 2) |
注意 起始位置参数和slice
很像,都是[start, end)
遍历数组
1 | for (let index of ['a', 'b'].keys()) { |
ES6还提供了values()
、entries()
来遍历值、键值对。
arr.includes()
返回一个布尔值
1 | [1, 2, 3].includes(2) // true |
如果不支持,自己部署
1 | const contains = (() => |
arr.flat()
“拉平”多维数组
flat()
默认只会“拉平”一层
1 | [1, 2, [3, [4, 5]]].flat() |
传入一个整数,可以指定”拉平”的层数
1 | [1, 2, [3, [4, 5]]].flat(2) |
传入Infinity
,不管几层都变为一维
1 | [1, [2, [3]]].flat(Infinity) |
注意
跳过空位
1 | [1, 2, , 4, 5].flat() |
arr.flatMap()
map() + flat(),flat()参数默认为1,因此只能展开一层
1 | // 相当于 [[2, 4], [3, 6], [4, 8]].flat() |
对象扩展
super关键字
指向当前对象的原型对象
注意
只能用在对象方法中,目前,只有对象方法的简写法可以让 JavaScript 引擎确认,定义的是对象的方法。JavaScript 引擎内部,super.foo
等同于Object.getPrototypeOf(this).foo
。
看个栗子理解一下
1 | const proto = { |
proto.foo绑定的在执行时绑定的是obj,因此返回”world”。
解构赋值
1 | let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; |
注意
(1) 浅拷贝
1 | let obj = { a: { b: 1 } }; |
(2)扩展运算符的解构赋值,只能读取对象自身的属性
1 | const o = Object.create({ x: 1, y: 2 }); |
ES6 规定,变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式,下面写法会报错。
1 | let { x, ...{ y, z } } = o; |
所以上面用newObj做中间变量。