引入
系统学习ES6各种特性,了解背后的原理。
笔记
1. 数组的扩展
1.1 扩展运算符
扩展运算符(spread)为...,如同rest参数的逆运算,可以将数组转为用逗号分割的参数序列。
若数组为空则不产生任何效果
主要用于函数调用:
console.log(1, ...[2,3,4], 5); //1 2 3 4 5
const add = (x,y)=>x+y;
add(...[1,3]); // 4
Math.max(...[1,3,5,7]); // 7
还能合并数组
let arr1 = [1,2];
let arr2 = [3,4];
[...arr1, ...arr2, 5, 6]; //[1, 2, 3, 4, 5, 6]
和解构赋值一起使用(注意此时只能将其放在参数的最后一个, 有点类似函数的rest参数)
let [first, ...rest] = [1,2,3,4,5];
first; //1
rest; //[2,3,4,5]
将实现了Iterator接口的对象转化为数组
[..."hi"]; // ['h', 'i'] 而且能够正确识别32bit字符
[document.querySelectorAll('div')] // 将nodeList对象转化为数组
let go = function*(){
yield 1;
yield 2;
yield 3;
}
[...go()]; //[1,2,3] generator函数返回的是遍历器对象,也可以用扩展运算符转换
1.2 Array.from()
Array.from()方法可以将类似数组或者可遍历对象转化为真正的数组(会清除无关属性)
还可以传入第二个参数,作用类似于数组的map方法,可以非常灵活的应用
let arrLike = {1:'a', 2:'b', length:3}; //注意length属性,任何有此属性的对象都可以转换
Array.from(arrLike); //[undefined, 'a', 'b']
Array.from({0:0,1:1,2:2,length:3}, x=>x*x); //[0, 1, 4]
Array.from({length:3}, ()=>"hi"); //['hi', 'hi', 'hi']
1.3 Array.of()
该方法用于将一系列参数转化为数组,目的是弥补Array()构造函数的不足(参数不同效果不同的问题)
Array(); // []
Array(3); // [,,,]
Array(3,2); //[3,2]
Array.of(); // []
Array.of(3); //[3]
Array.of(3,2); //[3,2]
1.4 数组实例的find()、findIndex()
find(func)方法用于找到第一个符合func条件的元素
findIndex(func)方法用于找到第一个符合func条件的位置
func回调函数可以接受3个参数:value(当前元素),index(当前位置),arr(原数组)
[1,3,5,7].find(x=>x>1); //3
[1,3,5,7].findIndex(x=>x>1); //2
1.5 数组实例fill()
可接受三个参数:填充的值、填充的起始位置、结束位置(左闭右开)
Array(3).fill(666); //[666, 666, 666]
[1,2,3].fill(666); //[666, 666, 666]
[1,2,3,4].fill(666, 1, 2); //[1, 666, 3, 4]
1.6 数组实例的entries()、keys()、values()
entries()、keys()、values()方法都用于遍历数组,返回一个迭代器
但区别是分别为键值对数组、键、值的迭代器,可以方便的用for...of遍历
1.7 数组实例的includes()
Array.prototype.includes(搜索的值, 搜索的起始位置=0)
返回bool值,比ES5中indexOf更加适合检查值是否存在,同时还能判断NaN(NaN不能用等号判断)
若搜索起始位置超过数组边界,则位置被重置为0
1.8 数组的空位
如Array(3)返回的是三个空位的数,但空位不等于undefined
如map()方法会跳过空位,for...of方法不会
ES5中大多数情况下会忽略空位,ES6中则是明确将空位转换为undefined
总之建议不要出现空位
2. 对象的扩展
2.1 属性的简洁表示法
ES6允许直接写入变量和函数作为对象的属性和方法,更加简洁
若某方法为Generator函数要在方法名之前加上*
let foo = "bar"
let obj = {
foo, //等同于foo:foo
hi(){
let [x,y] = [1,2];
return {x,y}; //返回参数时也非常简洁方便
}
// * hi(){} generator函数写法
}
obj; //{foo: 'bar', hi: ƒ}
2.2 属性名表达式
ES6中允许在定义对象时,属性名使用表达式(要用[]包裹属性名表达式)
甚至方法的属性名也可以用表达式
但如果表达式为一个对象,默认情况下属性名会转化为字符串'[object objet]'
若有多个重名属性,后面的会覆盖前面的
注意:属性名表达式不能和简洁表示法一起使用
let key = 'foo'
let obj = {
[key]: 'bar',
['a'+'b']: 'abc',
['fu'+'nc'](){console.log('func');}
}
obj; //{foo: 'bar', ab: 'abc', func: ƒ}
2.3 Object.is()
ES5中比较两个值是否相等只能用==和===运算符,都有缺点
前者会自动转换数据类型,后者的正负0不相等。
ES6中提出了Object.is()方法解决这个问题
该方法基本等同于===严格相等
不同之处只有两个:
+0等于-0NaN等于自身
Object.is(NaN, NaN); // true
2.4 Object.assign()
Object.assign(target, src1, src2)常常用来合并对象
将src1, src2中所有可枚举属性(不复制继承属性)赋值到target上,然后返回target
若有相同的属性名,后面的会覆盖前面的。
仅仅是浅拷贝(若有属性的值为对象,仅仅拷贝对象的引用)
若target参数不是对象,会先转化为对象(无法转换时报错),若只有这个参数那么会直接返回
其他参数不是对象,会先转化为对象,无法转换时会跳过
注意若对数组使用,会以对象的形式进行(数组的属性名为位置下标)
2.5 Object.keys()、Object.values()、Object.entries()
注意此方法不在Object.prototype中,没有被其他对象继承
若参数非对象,会尝试转化为对象
这三种方法分别返回对象可枚举的(不含继承的)键、值、键值对,组成的数组
可以方便地使用for...of进行遍历
Object.entries()可以直接用于Map的构造函数来将对象转为Map:
new Map(Object.entries({foo:'bar', baz:42})); // Map(2) {'foo' => 'bar', 'baz' => 42}
2.6 对象的扩展运算符
配合解构赋值使用
解构赋值中的扩展运算将所有可遍历(不包括继承的)的但未被读取的属性分配到指定的对象上
此时必须是最后一个参数,否则报错
注意是浅拷贝,遇到复合类型的数据慎用
let {x, y, ...z} = {x:1, y:2, z:3, a:4, b:66};
z; // {z: 3, a: 4, b: 66}
可以利用解构赋值+扩展运算符来扩展某个函数的形参
function foo({x, y, ...restConfig}){
console.log(x, y, restConfig);
}
foo({x:1,y:2, bar:'abc', baz:'bbb'});
复制、合并对象
等同于使用Object.assign()
let z = {a:3, b:4};
let n = {c:6, ...z}; //{c: 6, a: 3, b: 4}
{...n, a:666, b:777}; //{c: 6, a: 666, b: 777}
//等同于
{...n, ...{a:666, b:777}}; // {c: 6, a: 666, b: 777}
// 后边的会覆盖前面的同名属性
若想要完整复制一个对象(包括对象原型的属性)
const clone = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
)
2.7 Null传导运算符
?.即Null传导运算符
const firstName = message?.body?.user?.firstName || 'default';
只要任意一步返回null或undefined就不再继续运算,而是返回undefined
因此上式加上|| 'default运算可以实现当无法取得firstName时,返回default
有四种用法:
obj?.prop读取对象属性obj?.[expr]使用表达式读取对象属性func?.(...args)函数或对象方法的调用new C?.(...args)构造函数的调用
此外,双问号运算符:如果??前给定的变量值为 null 或者 undefined,则使用双问号后的默认值,否则仍然使用该变量值
比||更加适合与?.运算符搭配使用
const firstName = message?.body?.user?.firstName ?? 'default';