ES6知识详解
简介
ECMAScript
(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标是使 JavaScript 可以编写复杂的大型应用程序,成为企业级开发语言。
let 关键字
ES6 新增了let
命令,用来声明变量。特点如下:
- 不允许重复声明
- 存在块级作用域
- 不存在变量提升
- 不影响作用域链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 不允许重复声明 let num = 10; let num = 10; // Identifier 'num' has already been declared // 存在块级作用域 { let a = 10; var b = 20; } console.log(a); // ReferenceError: a is not defined console.log(b); // 20 // 不存在变量提升 // var的情况 console.log(num); // 输出 undefined var num = 10; // let的情况 console.log(foo); // 报错 ReferenceError: Cannot access 'foo' before initialization let foo = 20; |
以后声明变量,使用let
即可
const 关键字
const
关键字用来声明常量。特点如下:
- 声明必须赋初始值
- 标识符一般大写
- 不允许重复声明
- 值不允许修改(对象属性修改和数组元素变化不会触发错误)
- 块级作用域
声明对象类型使用const
,非对象类型声明选择let
变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// 数组的解构赋值 const arr = ['张学友', '刘德华', '黎明', '郭富城']; let [zhang, liu, li, guo] = arr; console.log(zhang, liu, li, guo); // 张学友 刘德华 黎明 郭富城 //对象的解构赋值 const lin = { name: '林志颖', tags: ['车手', '歌手', '小旋风', '演员'] } let {name, tags} = lin; console.log(name, tags); // 复杂结构 let wangfei = { name: '王菲', age: 18, songs: ['红豆', '流年', '暧昧', '传奇'], history: [ {name: '窦唯'}, {name: '李亚鹏'}, {name: '谢霆锋'} ] } let {songs: [one, two, three], history: [first, second, third]} = wangfei; console.log(one, first); // 红豆 {name: '窦唯'} |
频繁使用对象方法、数组元素,就可以使用结构赋值的形式
模板字符串
模版字符串是增强版的字符串,用反引号(`)标识。特点如下:
- 字符串中可以出现换行符
- 可以使用
${xxx}
形式输出变量
1 2 3 4 5 6 7 8 9 10 |
// 普通的字符串 let str1 = `This is '\n' very good`; // 多行字符串 let str2 = `This is very good`; // 字符串中嵌入变量 let age = 20; let str3 = `tom's age is ${age}`; |
当遇到字符串与变量拼接的情况使用模板字符串
简化对象写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let name = 'seabert' let slogon = '你一定可以的' let say = function() { console.log('hello world'); } // 属性和方法简写 let sxy = { name, slogon, say, change() { console.log('改变世界'); } } console.log(sxy.name); // seabert |
箭头函数
ES6 允许使用“箭头”(=>
)定义函数。
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数
this
指向声明时所在作用域下this
的值 - 箭头函数不能作为构造函数实例化
- 不能使用
arguments
参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 1. 通用写法 let fn = (arg1, arg2, arg3) => { return arg1 + arg2 + arg3; } // 2. 省略小括号的情况 let fn2 = num => { return num * 10; } // 3. 省略花括号的情况 let fn3 = score => score * 20; // 4. this指向声明时所在作用域中 this 的值 let fn4 = () => { console.log(this); } fn4() // Window对象 |
箭头函数不会更改this
指向,用来指定回调函数会非常适合
rest 参数
ES6 引入rest
参数,用于获取函数的实参,用来代替arguments
1 2 3 4 5 6 7 8 9 10 11 |
// 作用与 arguments 类似 function add(...args) { console.log(args); } add(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5] // rest 参数必须是最后一个形参 function minus(a, b, ...args) { console.log(a, b, args); } minus(100, 2, 5, 1, 29) // 100 2 [5, 1, 29] |
rest
参数非常适合不定个数参数函数的场景
spread 扩展运算符
扩展运算符(spread
)也是三个点(...)。它好比 rest
参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 展开数组 let tfboys = ['德玛西亚之力','德玛西亚之翼','德玛西亚皇子']; function fn(...args) { // console.log(arguments); // ['德玛西亚之力', '德玛西亚之翼', '德玛西亚皇子'] console.log(args); // ['德玛西亚之力', '德玛西亚之翼', '德玛西亚皇子'] } fn(...tfboys) // 展开对象 let skillOne = { q: '致命打击', }; let skillTwo = { w: '勇气' }; let skillThree = { e: '审判' }; let skillFour = { r: '德玛西亚正义' }; let gailun = {...skillOne, ...skillTwo, ...skillThree, ...skillFour} console.log(gailun); // {q: '致命打击', w: '勇气', e: '审判', r: '德玛西亚正义'} |
Symbol
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol
的特点如下:
Symbol
的值是唯一的,用来解决命名冲突的问题Symbol
值不能与其他数据进行运算Symbol
定义的对象属性不能使用for...in
循环遍历,但是可以使用Relect.ownKeys
来获取对象的所有键名
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 创建 Symbol let s1 = Symbol() console.log(s1, typeof s1); // Symbol() 'symbol' // 添加标识的 Symbol let s2 = Symbol('宋小羽'); let s2_2 = Symbol('宋小羽'); console.log(s2 === s2_2); // false // 使用 Symbol for 定义 let s3 = Symbol.for('宋小羽'); let s3_2 = Symbol.for('宋小羽'); console.log(s3 === s3_2); // true |
遇到唯一性的场景要想到Symbol
Symbol 内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了11个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
方法 | 说明 |
---|---|
Symbol.hasInstance | 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的 Symbol.isConcatSpreadable 属性等于的是一个 布尔值,表示该对象用于 Array.prototype.concat()时, 是否可以展开。 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.match | 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值 |
Symbol.replace | 当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split | 当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag | 在该对象上面调用 toString 方法时,返回该方法的返回值 |
Symbol.unscopables | 该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。 |
迭代器
遍历器(interator
)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署interator
接口,就可以完成遍历操作。
ES6 创造了一种新的遍历命令for...of
循环,interator
接口主要供for...of
使用。
原生具备interator
接口的数据(可用for...of
遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的
next
方法,指针自动指向数据结构的第一个成员 - 接下来不断调用
next
方法,指针一直往后移动,直到指向最后一个成员 - 每调用
next
方法返回一个包含value
和done
属性的对象
需要自定义遍历数据的时候,要想到迭代器
生成器
ES6 提供的一种异步编程解决方案,语法行为与传统函数不同。
1 2 3 4 5 6 7 8 9 10 |
function * gen() { yield '一直没有耳朵'; yield '一直没有尾巴'; yield '真奇怪'; } let interator = gen(); console.log(interator.next()); // {value: '一直没有耳朵', done: false} console.log(interator.next()); // {value: '一直没有尾巴', done: false} console.log(interator.next()); // {value: '真奇怪', done: false} |
代码说明:
*
的位置没有限制- 生成器函数返回的结果是迭代器对象,调用迭代器对象的
next
方法可以得到yield语句后的值 yield
相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次next
方法,执行一段代码next
方法可以传递实参,作为yield
语句的返回值
Promise
Promise
是 ES6 引入的异步编程的新解决方案。语法上Promise
是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise构造函数:Promise (excutor) {}
- Promise.prototype.then 方法
- Promise.protyotype.catch 方法
特点:
- 对象的状态不受外界影响。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 基本用法 const promise = new Promise(function(resolve, reject) { if(异步操作成功) { resolve(value) } else { reject(error) } }) // then方法 (resolve和reject两个回调函数作为参数) promise.then(function(value) { // success }, function(error) { // failure }) |
Set
ES6 提供了新的数据结构Set
(集合)。它类似于数组,但成员的值都是唯一的,集合实现了interator
接口,所以可以使用「扩展运算符」和「for...of」进行遍历,集合的属性和方法:
size
:返回集合的元素个数add
:增加一个新元素,返回当前集合delete
:删除元素,返回boolean
值has
:检测集合中是否包含某个元素,返回boolean
值clear
:清空集合,返回undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 创建一个空集合 let s = new Set(); // 创建一个非空集合 let s1 = new Set([1, 2, 3, 4, 5]); // 集合属性和方法 // 返回集合的元素个数 console.log(s1.size); // 5 // 添加新元素 console.log(s1.add(6)); // Set(6) {1, 2, 3, 4, 5, 6} // 删除元素 console.log(s1.delete(2)); // true // 检测是否存在某个值 console.log(s1.has(3)); // true // 清空集合 console.log(s1.clear()); // undefined |
Map
ES6 提供了Map
数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map
也实现了interator
接口,所以可以使用「扩展运算符」和「for...of」进行遍历,Map的属性和方法:
size
:返回Map
的元素个数set
:增加一个新元素,返回当前Map
get
:返回键名对象的键值has
:检测Map中是否包含某个元素,返回boolean
值clear
:清空Map,返回undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 创建一个空 Map let m = new Map(); // 创建一个非空 Map let m2 = new Map([ ['name', '宋小羽'], ['slogon', '改变世界'] ]); // 属性和方法 // 返回 Map 的元素个数 console.log(m2.size); // 2 // 添加映射值 console.log(m2.set('age', 18)); // Map(3) {'name' => '宋小羽', 'slogon' => '改变世界', 'age' => 18} // 获取映射值 console.log(m2.get('age')); // 18 // 检测是否有该映射 console.log(m2.has('age')); // true // 清空 console.log(m2.clear()); // undefined |
class 类
ES6 提供了更接近传统语言的写法,引入了Class
(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class
类的知识点:
- class:关键字声明类
- constructor:定义构造函数初始化
- extends:继承父类
- super:调用父级构造方法
- static:定义静态方法和属性
- 父类方法可以重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// 父类 class Phone { // 构造方法 constructor(brand, color, price) { this.brand = brand; this.color = color; this.price = price; } // 对象方法 call() { console.log('我可以打电话'); } } // 子类 class SmartPhone extends Phone { constructor(brand, color, price, screen, pixel) { super(brand, color, price) this.screen = screen; this.pixel = pixel; } // 子类方法 photo() { console.log('我可以拍照!!'); } playGame() { console.log('我可以玩游戏!!'); } // 方法重写 call() { console.log('我可以进行视频通话'); } // 静态方法 static run() { console.log('我可以运行程序'); } static connect() { console.log('我可以建立连接'); } } // 实例化对象 const Nokia = new Phone('诺基亚', '灰色', 230); Nokia.call(); // 我可以打电话 const iPhone6s = new SmartPhone('苹果', '白色', 6088, '4.7inch', '500w'); // 调用子类方法 iPhone6s.playGame(); // 我可以玩游戏!! // 调用重写方法 iPhone6s.call(); // 我可以进行视频通话 // 调用静态方法 SmartPhone.run(); // 我可以运行程序 |
数值扩展
二进制和八进制
ES6 提供了二进制和八进制数值的新写法,分别用前缀0b
和0o
表示。
Number.isFinite() 与 Number.isNaN()
Number.isFinite()
:用来检查一个数值是否为有限的Number.isNaN()
:用来检查一个数值是否为NaN
Number.parseInt() 与 Number.parseFloat()
ES6 将全局方法parseInt
和parseFloat
,移植到Number
对象上面,使用不变。
Math.trunc
用于去除一个数的小数部分,返回整数部分。
Number.isInteger
用来判断一个数是否是整数
对象扩展
ES6 新增了一些对象的方法:
Object.is
:比较两个值是否相等,与“===”行为基本一致Object.assign
:对象的合并,将源对象的所有可枚举属性,复制到目标对象__proto__
、setPrototypeOf
:setPrototypeOf可以直接设置对象的原型
模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处
- 防止命名冲突
- 代码复用
- 高维护
模块化规范产品
ES6 之前的模块化规范由:
- CommJS:NodeJS、Browserify
- AMD:requireJS
- CMD:seaJS
ES6 模块化语法
模块功能主要由两个命令构成:export和import。
- export命令用于规定模块的对外接口
- import命令用于输入其他模块提供的功能