ES6新特性及常见用法

1,856 阅读10分钟

let const 和var 的区别

var的使用:
    1)使用var存在无意义的变量提升
    2)如果某个变量的值不希望被修改,无法进行设置
    3)可以重复进行声明操作
    4)全局变量会自动成为顶级对象的属性
let的使用:
    1)无法重复声明
    2)没有变量的提升
    3)全局变量不会成为顶级对象的属性
    4)会形成一个块级作用域,使用let声明的变量只能在块级作用域内有效
    5)使用let声明的变量会形成一个暂时性死区(也就是说这个块级作用域内有了let定义变量,
    这个变量就跟这个作用域块形成了一个绑定,不会再受外界的影响)
const的使用:
    1)通过const声明的是常量,不是变量
    2)一旦变量被声明,他的值就不能被修改(内存中的地址不能被修改)
    3)同样不存在变量的声明提升,同样存在暂时性死区,同样不能重复声明

注意:
    ES5 只有两种声明变量的方法:var命令和function命令。
    ES6 除了添加let和const命令,还添加了另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。

解构赋值的用法(数组,对象)

数组的结构赋值:
    let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值

如果解构不成功,值就是undefined

对象的结构赋值:
    let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"
    对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
    而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

数组字符串新增的方法(常用)

数组新增方法:

1)Array.from():方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
2)Array.of():将一组值,转换为数组

3)数组实例的find():数组实例的find方法,用于找出第一个符合条件的数组成员。(返回的是值)
4)findIndex():数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1(返回的是索引位置)

字符串新增方法:

   1)传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

        includes():返回布尔值,表示是否找到了参数字符串。
        startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
        endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
    2) repeat()
        repeat方法返回一个新字符串,表示将原字符串重复n次。
        
    3)在ES2017中引入了字符串自动补全的功能如果某个字符串不够指定长度,会在头部或尾部补全。
    padStart()用于头部补全,padEnd()用于尾部补全。
    
    4)ES2019对字符串实例新增了trimStart()和trimEnd()这两个方法它们的行为与trim()一致,
    trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

对象新增的方法

属性的遍历
ES6 一共有 5 种方法可以遍历对象的属性。

(1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列。

新增基本数据类型-Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,
一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,
可以保证不会与其他属性名产生冲突。

let s = Symbol();

typeof s
// "symbol"

Set和Map数据结构

Set数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。

Map数据结构:

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
如果你需要“键值对”的数据结构,Map 比 Object 更合适。

Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,
因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,
用在这里表示由它来“代理”某些操作,可以译为“代理器”。

var proxy = new Proxy(target, handler);

Iterator(遍历器)和for of

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:
    一是为各种数据结构,提供一个统一的、简便的访问接口;
    二是使得数据结构的成员能够按某种次序排列;
    三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费

Generator函数的使用方法

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。
返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。
一是,function关键字与函数名之间有一个星号;
二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();


上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。

然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。
不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,
也就是前面介绍的遍历器对象(Iterator Object)。

async函数

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

async函数是对Generator函数的改进:
    1)内置执行器
    2)更好的语义
    3)更广的实用性
    4)返回值是一个Promise对象
async函数必须与await进行配套使用否则就会报错

Class的基本用法

class其实是对function的简化 class的声明:

class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

// 等同于

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

Class的继承

直接使用extend关键字:

class Point {
}

class ColorPoint extends Point {
}
继承:
    如果子类和父类完全一样就使用extend完全继承
    如果子类想要拥有父类的属性,同时还需要拥有自己的父类就需要使用super关键字,
    对于方法而言,如果没有同名的方法直接调用即可,但是如果想使用同名方法需要调用super关键字
    
    class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2

Module 的语法

常用的导入导出

模块功能主要由两个命令构成:
    export和import。
    export命令用于规定模块的对外接口,
    import命令用于输入其他模块提供的功能。
export default{}
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

异步遍历器

前面介绍过,for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

async function f() {
  for await (const x of createAsyncIterable(['a', 'b'])) {
    console.log(x);
  }
}
// a
// b

箭头函数和普通函数的区别,箭头函数可以使用new吗?

箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:

1、函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

2、不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

3、不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

4、不可以使用 new 命令,因为:

没有自己的 this,无法调用 call,apply。
没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 __proto__