李金帅
能将喜欢的东西留在身边,这就是我努力的意义。

ES6新特性

2021-08 ES6
Word count: 3k | Reading time: 10min

ES6新特性

1、let、const

let 特点

  1. 新增块级作用域
  2. 同一作用域不允许重复声明
  3. 不支持变量提升
  4. for采用let;设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
  5. 暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的
  6. let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

const 特点

  1. 声明时必须赋值
  2. 赋值后,无法修改
  3. 其余,同let

注意:对象const的表现。

综上,定义变量的方式有 六种;

  1. var

  2. function

  3. let

  4. const

  5. class

  6. import

顶层对象

JavaScript 语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。但是,顶层对象在各种实现里面是不统一的。

  • 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window
  • 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self
  • Node 里面,顶层对象是global,但其他环境都不支持。

同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性。

  • 全局环境中,this会返回顶层对象。但是,Node.js 模块中this返回的是当前模块,ES6 模块中this返回的是undefined
  • 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined
  • 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么evalnew Function这些方法都可能无法使用。

2、解构赋值

解构赋值是基于模式匹配的。 可以设置默认值。

  1. 数组解构赋值
  2. 对象解构赋值
  3. 函数参数解构赋值
  4. 字符串解构赋值

解构赋值的作用:

  1. 交换变量的值

  2. 函数参数定义

  3. 函数参数设置默认值

  4. 从函数返回多个值

  5. 导入模块的指定方法

  6. 从json中提取数据

  7. 遍历map结构

3、模板字符串

${}中是js表达式。

4、字符串新增方法、数值新增属性、数组新增方法、对象新增方法

5、函数扩展

函数的默认参数 : 函数的length属性,为函数参数的数目,不包含默认参数

函数的剩余参数:…变量名; 剩余参数后不能再有其他参数

函数的name属性: 返回函数的名称;

箭头函数有几个使用注意点:

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  4. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  5. 定义对象的方法时,不要使用箭头函数。

6、扩展运算符

扩展运算符(spread)是三个点(...)。

数组\对象复制、数组\对象合并、与解构赋值结合、将字符串转化为字符数组、将实现了 Iterator 接口的对象转化为数组。

7、对象的扩展

属性(数据属性、方法属性)的简洁写法、属性名表达式、表达式还可以用于定义方法名。

注意,简写的对象方法不能用作构造函数,会报错。

属性的可枚举性

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。

目前,有四个操作会忽略enumerablefalse的属性。

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的所有可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

这四个操作之中,前三个是 ES5 就有的,最后一个Object.assign()是 ES6 新增的。其中,只有for...in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理对象自身的属性。实际上,引入“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for...in操作,不然所有内部属性和方法都会被遍历到。比如,对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for...in遍历到。

属性的遍历

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 键,按照加入时间升序排列。

this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。

链判断运算符

编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取message.body.user.firstName,安全的写法是写成下面这样。

1
2
3
4
5
6
7
8
// 错误的写法
const firstName = message.body.user.firstName;

// 正确的写法
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';

上面例子中,firstName属性在对象的第四层,所以需要判断四次,每一层是否有值。

三元运算符?:也常用于判断对象是否存在。

1
2
const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined

这样的层层判断非常麻烦,因此 ES2020引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。

1
2
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value

下面是判断对象方法是否存在,如果存在就立即执行的例子。

1
iterator.return?.()

8、Symbol类型

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。

比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

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

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

Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

内置的 Symbol 值:除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

Symbol.hasInstance

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Even {
static [Symbol.hasInstance](obj) {
return Number(obj) % 2 === 0;
}
}

// 等同于
const Even = {
[Symbol.hasInstance](obj) {
return Number(obj) % 2 === 0;
}
};

1 instanceof Even // false
2 instanceof Even // true
12345 instanceof Even // false

9、遍历器 和 for-of

ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of循环,作为遍历所有数据结构的统一的方法。

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了MapSet。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是MapMap的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

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

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

Iterator 的遍历过程是这样的。

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

Author: 李金帅

Link: https://lijinshuai21.github.io/Li_JinShuai_Blog/2021/08/01/ES6%E6%96%B0%E7%89%B9%E6%80%A7/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
webStorage存储数据
NextPost >
event对象
CATALOG
  1. 1. ES6新特性
    1. 1.0.1. 1、let、const
    2. 1.0.2. 2、解构赋值
    3. 1.0.3. 3、模板字符串
    4. 1.0.4. 4、字符串新增方法、数值新增属性、数组新增方法、对象新增方法
    5. 1.0.5. 5、函数扩展
    6. 1.0.6. 6、扩展运算符
    7. 1.0.7. 7、对象的扩展
    8. 1.0.8. 8、Symbol类型
    9. 1.0.9. 9、遍历器 和 for-of