ES6标准入门 学习笔记 (03)

160 阅读6分钟

引入

系统学习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()方法解决这个问题

该方法基本等同于===严格相等

不同之处只有两个:

  1. +0 等于 -0
  2. NaN等于自身
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';

只要任意一步返回nullundefined就不再继续运算,而是返回undefined

因此上式加上|| 'default运算可以实现当无法取得firstName时,返回default

有四种用法:

  1. obj?.prop 读取对象属性
  2. obj?.[expr] 使用表达式读取对象属性
  3. func?.(...args) 函数或对象方法的调用
  4. new C?.(...args) 构造函数的调用

此外,双问号运算符:如果??前给定的变量值为 null 或者 undefined,则使用双问号后的默认值,否则仍然使用该变量值

||更加适合与?.运算符搭配使用

const firstName = message?.body?.user?.firstName ?? 'default';