一篇文章带你学会整个ES6
ES 介绍
- ES 全称 EcmaScript,是脚本语言的
规范
,而平时经常编写的JavaScript
,是 EcmsScript 的一种实现
,所以 ES 新特性其实是指JavaScript 的新特性
。
- 特点:
- 语法简单,功能丰富。
- 框架开发应用。
变量
let 变量以及声明特性
- 语法:
let a; let b, c, d; let e = 100; let f = 500, g = 'iloveyou', h = []
- 声明特性:
- 变量不能重复声明。
- 块级作用域(只在代码块中才能使用)。
- 不存在变量提升。
- 不影响作用域链。
const 变量以及声明特性
- 语法:
const SCHOOL = 'xxx大学'
- 声明特性:
- 一定要赋初始值。
- 一般变量使用大写(潜规则)。
- 常量的值不能修改。
- 块级作用域。
- 对于数组和对象的元素修改,不算做对常量的修改,不会报错。(原因:数组指向的地址值没有发生改变。)
ES 6 的变量解构赋值
- 数组的解构
const F4 = ['小沈阳', '刘能', '赵四', '宋小宝']; let [xiao, liu, zhao, song] = F4; console.log(xiao) console.log(liu) console.log(zhao) console.log(song)
对象的解构
const zhao = { name: '赵本山', age: '不详', xiaopin: function () { console.log("我可以演小品") } } let { name, age, xiaopin } = zhao console.log(name) console.log(age) console.log(xiaopin) xiaopin()
模板字符串
- 语法:
let str = `我也是一个字符串`
- 声明特性:
- 内容中可以直接出现换行符。
- 变量拼接。
简化对象写法
- ES 6 允许在大括号里面 ,直接写入变量和函数,作为对象的属性和方法。
let name = 'xxx' let change = function () { console,log('我可以改变你') } const school = { name, // 等价于 name: name change // 等价于 change: change improve () { console.log('我们可以提高你的技能') } }
函数
箭头函数以及声明特点
- 语法:
let fn = function () { // 函数体 } let fn = () => { // 函数体 }
特性:
- this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值。
- 不能作为构造函数实例化对象。
- 不能使用 arguments 变量。
- 箭头函数简写:
// 当形参有且仅有一个的时候,可以省略括号 let add = n => { return n + n; } // 当函数体只有一条语句时,此时花括号可以省略,return 必须省略。而且语句的执行结果就是函数的返回值。 let pow = n => n * n;
函数参数的默认值
- ES 6 允许给函数参数赋值初始值。
- 形参初始值
// 形参初始值,具有默认值的参数,一般位置要靠后(潜规则) function add(a, b, c=10) { return a + b +c } add(1, 2, 3) // 6 add(1, 2) // 13
与解构赋值结合
function connect ({ host="127.0.0.1", username, password, port }) { console.log(host) console.log(username) console.log(password) console.log(port) } connect({ host: 'localhost', username: 'root', password: 'root', port: 3306 })
ES 6 的 rest 函数
- ES 6 引入 rest 函数,用于获取函数的实参,用来代替 arguments 。
// ES 5 获取实参的方式: function date () { console.log(arguments); } date('阿娇', '柏芝', '思慧') // ES 6 获取实参 function date(...args) { console.log(args); } date('阿娇', '柏芝', '思慧')
注意事项:
// rest 参数必须放到参数最后 function fn(a, b, ...args) { console.log(a); // 1 console.log(b); // 2 console.log(args); // [3, 4, 5, 6] } fn(1, 2, 3, 4, 5, 6)
扩展运算符
...
:扩展运算符能将数组
转换为逗号分隔的参数序列
。
const tfboys = ['易烊千玺', '王源', '王俊凯'] function chunwan () { console.log(arguments) } chunwan(...tfboys) // 等价于 chunwan('易烊千玺', '王源', '王俊凯')
应用:
// 1. 数组的合并 const tfboys = ['易烊千玺', '王源', '王俊凯'] const kuiazi = ['王太利', '肖央'] const newGroup = [...kuaizi, ...tfboys] // 2. 数组的克隆 const sanzhihua = ['E', 'G', 'M'] const sanyecao = [...sanzhihua] // 浅拷贝 // 3. 将伪数组转为真正的数组 const divs = document.querySelectorAll('div') const divArr = [...divs] // 此时就变成了真正的数组
数据类型
Symbol
- ES 6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
- 特点:
- Symbol 的值是唯一的,用来解决命名冲突的问题。
- Sysbol 值不能与其他数据进行运算。
- Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用
Reflect.ownKeys
来获取对象的所有键名。
- 创建 Symbol 的方式
let s = Symbol() let s2 = Symbol('标识符1') let s3 = Symbol('标识符1') console.log(s2 === s3) // false // Symbol.for 创建 let s4 = Symbol.for('标识符2') let s5 = Symbol.for('标识符2') console.log(s4 === s5) // true
对象添加 Symbol 类型的属性
- 向对象中添加方法 up down
let game = { name: '俄罗斯方块', up: function () {}, down: function () {} } // 1. 声明一个对象 let methods = { up: Symbol(), down: Symbol() } // 2. 将声明的对象中的 up 和 down 添加到 game 中 game[methods.up] = function () { console.log('我可以改变形状') } game[methods.down] = function () { console.log('我可以改变大小') } let youxi = { name: '狼人杀', [Symbol('say')]: funcyion () { console.log('我可以发言') }, [Symbol('zibao')]: funcyion () { console.log('我可以自爆') } }
Symbol 内置值
- 除了定义自己使用的 Symbol 值以外, ES 6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
迭代器
- 迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 itrator 接口,就可以完成遍历操作。
- ES 6 创造了一种新的遍历命令 for...on 循环,Iterator 接口主要提供 for...on 消费。
- 原生具有 Itrator 接口的数据(可用 for...of 遍历)。
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
- 工作原理:
- 创建一个指针对象,指向一个当前数据结构的起始位置。
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员。
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员。
- 每调用 next 方法返回一个包含 value 和 down 属性的对象。
注意:需要自定义遍历数据的时候,要想到迭代器。
自定义遍历数据
const banji = { name: '终极一班', stus: [ '汪大东', '雷婷', '中万钧', '金宝三' ], [Symbol.iterator] () { let index = 0 let _this = this return { next: function() { if (index < _this.stus.length) { const result = { value: _this.stus[index], down: false } index++ return result } else { return { value: undefined, down: true } } } } } } for (let v of banji) { console.log(v); }
生成器函数声明与调用
- 生成器就是一个特殊的函数,来进行异步编程。
function * gen(){ console.log('111') yield '一只没有耳朵' // yield: 函数代码的分隔符 console.log('222') yield '一只没有耳朵' console.log('333') yield '一只没有耳朵' console.log('444') } let iterator = gen() console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next()) // 遍历 for(let v of gen()){ console.log(v) }
生成器函数的参数传递
function * gen(arg){ console.log(arg) // AAA let one = yield 111 // yield: 函数代码的分隔符 console.log(one) // BBB let two = yield 222 console.log(two) // CCC let three = yield 333 console.log(three) // DDD } let iterator = gen('AAA') console.log(iterator.next('BBB')) console.log(iterator.next('CCC')) console.log(iterator.next('DDD'))
生成器函数实例
- 回调地狱
setTimeout(() => { console.log(111) setTimeout(() => { console.log(222) setTimeout(() => { console.log(333) }, 3000) }, 2000) }, 1000)
利用生成器函数解决回调地狱:
function one() { setTimeout(() => { console.log(111) iterator.next() },1000) } function two() { setTimeout(() => { console.log(222) iterator.next() },2000) } function three() { setTimeout(() => { console.log(333) iterator.next() },3000) } function * gen() { yield one() yield two() yield three() } // 调用生成器函数 let iterator = gen() iterator.next()
生成器函数案例
// 获取用户数据, 订单数据, 商品数据 function getUser() { setTimeout(() => { let data = '用户数据' iterator.next(data) }, 1000) } function getOrder() { setTimeout(() => { let data = '订单数据' iterator.next(data) }, 1000) } function getGoods() { setTimeout(() => { let data = '商品数据' iterator.next(data) }, 1000) } function* gen() { let user = yield getUser() console.log(user); let order = yield getOrder() console.log(order); let goods = yield getGoods() console.log(goods); } // 调用生成器函数 let iterator = gen() iterator.next()
Promise
- Promise 是 ES 6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise 构造函数:
Promise(excutor){}
。 - Promise.prototype.then 方法。
- Promise.prototype.catch 方法。
// 实例化 Promise 对象 const p = new Promise(function(resolve, reject){ setTimeout(function(){ // 成功时调用 resolve let data = '数据库中的数据' resolve(data) // 失败时调用 reject let err = '数据读取失败' reject(err) }, 1000) }) // 调用 Promise 对象的 then 方法 p.then(function(value){ console.log(value); }, function(reason){ console.log(reason); })
Promise 封装读取文件
// 读取文件 fs.readFile(文件路径, (err, data) => { // 如果失败,则抛出错误 if(err) throw err // 如果没有出错,则输出内容 console.log(data.toString()) }) // 用 promise 封装读取文件 const p = new Promise(function(resolve, reject){ fs.readFile(文件路径, (err, data)=> { // 判断如果失败 if(err) reject(err) // 如果成功 resolve(data) }) }) p.then(function(value){ console.log(value.toString) }, function(reason){ console.log("读取失败!!") })
Promise 封装 AJAX
- 原生 AJAX:
// 1. 创建对象 const xhr = new XMLHttpRequest() // 2. 初始化 xhr.open("GET", 接口地址) // 3. 发送 xhr.send() // 4. 绑定事件,处理响应结果 xhr.onreadystatechange = function () { // 判断 if (xhr.readyState === 4) { // 判断响应状态码 200-299 if (xhr.status >= 200 && xhr.status < 300) { // 表示成功 console.log(xhr.response); } else { console.error(xhr.status); } } }
Promise 封装 AJAX:
// Promise 封装 AJAX const p = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open("GET", 接口地址) xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.status); } } } }) p.then(function(value){ console.log(value); }, function (reason) { console.log(reason); })
Promise.prototype.then 方法
- 调用 then 方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定。
- 如果回调函数中返回的结果是 非 Promise 类型的属性,状态为成功,返回值为对象的成功的值。
Promise 实践 - 读取多个文件
- 原生:
const fs = require('fs') fs.readFile(文件路径, (err, data1) => { fs.readFile(文件路径, (err, data2) => { fs.readFile(文件路径, (err, data3) => { let result = data1 + '\r\n' + data2 + '\r\n' + data3 console.log(result); }) }) })
Promise 版:
const p = new Promise((resolve, reject) => { fs.readFile(文件路径, (err, data) => { resolve(data) }) }) p.then(value => { return new Promise((resolve, reject) => { fs.readFile(文件路径, (err, data) => { resolve([value, data]) }) }) }).then(value => { return new Promise((resolve, reject) => { fs.readFile(文件路径, (err, data) => { value.push(data) resolve(value) }) }) }).then(value => { console.log(value); })
Promise 的 catch 方法
- 用来捕获 Promise 失败的回调函数,相当于一个语法糖。
const p = new Promise((resolve, reject) => { setTimeout(() => { reject('出错了') }) }) // p.then(function(value){}, function(reason){ // console.error(reason); // }) p.catch(function(reason){ console.warn(reason); })
数据结构
Set
- ES 6 提供了新的数据结构 Set (集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用 【扩展运算符】和 【for...of...】进行遍历,集合的属性和方法:
属性或方法 | 说明 |
size | 返回集合的元素个数 |
add | 增加一个新元素,返回当前集合 |
delete | 删除元素,返回 boolean 值 |
has | 检测集合中是否包含某个元素,返回 Boolean 值 |
应用场景
// 1. 数组去重 let arr = [1,2,2,4,3,5,5,3,2,6] let reasult = [...new Set(arr)] // 2. 交集 let arr2 = [4,4,5,4,6,5] let reasult = [....new Set(arr)].fifter(item => { let s2 = new Set(arr2) if (s2.has(item)) { return true } else { return false } }) // 上面的代码可以简化为:let result = [...new Set(arr)].fifter(item => new Set(arr2).has(item)) // 3. 并集 let union = [...new Set([...arr, ...arr2])] // 4. 差集 let diff = [...new Set(arr)].fifter(item => !(new Set(arr2).has(item)))
Map
- ES 6 tigongle Map 数据结构。它类似于对象,也是键值对的集合。但是“健”的范围不限于字符串,各种类型的值(包括对象)都可以当作荐。Map 也实现了 iterator 接口,所以可以使用【扩展运算符】和 【for...of...】进行遍历。Map 的属性和方法:
属性或方法 | 说明 |
size | 返回 Map 的元素个数 |
set | 增加一个新元素,返回当前 Map |
get | 返回键名对象的键值 |
has | 检测 Map 中是否包含某个元素,返回 Boolean 值 |
clear | 清空集合, 返回 undefined |
class 类
- ES 6 提供了更接近传统语言的写法,引入了 Class (类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES 6 的 class 可以看作只是一个语法糖,它的绝大部分功能, ES 5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
- 知识点:
- class 声明类
- constructor 定义构造函数
- extends 继承父类
- super 调用父级构造方法
- static 定义静态方法和属性
- 父类可以重写
// ES 5 语法 function Phone(brand, price) { this.brand = brand this.price = price } Phone.prototype.call = function () { console.log('我可以打电话'); } // 实例化对象 let Huawei = new Phone('华为', 5999) // ES 6 语法 class Shouji { constructor(brand, price) { this.brand = brand this.price = price } call(){ console.log('我可以打电话'); } } let onePlus = new Shouji('1+', 1999)
class 静态成员
// ES 5 function Phone(){ } Phone.name = '手机' Phone.change = function(){ console.log('我可以改变世界') } Phone.prototype.size = '5.5inch' let nokia = new Phone() console.log(nokia.name) // undefined console.log(nokia.size) // 5.5inch // ES 6 class Phone{ // 静态属性 static name = '手机' static change(){ console.log('我可以改变世界') } } let nokia = new Phone() console.log(nokia.name) // undefined console.log(Phone.name) // 手机
ES 5 利用构造函数实现继承
// ES 5 语法 function Phone(brand, price) { this.brand = brand this.price = price } Phone.prototype.call = function () { console.log('我可以打电话'); } function SmartPhone(brand, price, color, size) { Phone.call(this, brand, price) this.color = color this.size = size } // 设置子级构造函数的原型 SmartPhone.prototype = new Phone SmartPhone.prototype.constructor = SmartPhone // 声明子类的方法 SmartPhone.prototype.photo = function () { console.log("我可以拍照"); } SmartPhone.prototype.playGame = function () { console.log("我可以玩游戏"); } const chuizi = new SmartPhone('锤子', 2499, '黑色', '5.5inch')
ES 6 的类继承
class Phone { constructor(brand, price) { this.brand = brand this.price = price } // 父类的成员属性 call() { console.log('我可以打电话'); } } class SmartPhone extends Phone { constructor(brand, price, color, size) { super(brand, price) this.color = color this.size = size } photo() { console.log('我可以拍照'); } playGame() { console.log('我可以玩游戏'); } } const xiaomi = new SmartPhone('小米', 799, '黑色', '4.7inch')
子类对父类方法的重写
class Phone { constructor(brand, price) { this.brand = brand this.price = price } // 父类的成员属性 call() { console.log('我可以打电话'); } } class SmartPhone extends Phone { constructor(brand, price, color, size) { super(brand, price) this.color = color this.size = size } photo() { console.log('我可以拍照'); } playGame() { console.log('我可以玩游戏'); } call() { console.log('我可以视频通话') } } const xiaomi = new SmartPhone('小米', 799, '黑色', '4.7inch')
class 中的 getter 和 setter 设置
class Phone { get price(){ console.log("价格属性被读取了") return 'iloveyou' } set price(newVal){ console.log("价格属性被修改了") } } // 实例化对象 let s = new Phone() console.log(s.price); // 调用 get 方法,得到的结果是返回值 s.price = 'free' // 调用 set 方法
ES 6 的数值扩展
数值扩展 | 说明 |
Number.EPSILON | 是 JavaScript 表示的最小精度 |
二进制、八进制、十进制、十六进制 | |
Number.isFinite | 检测一个数值是否为有限数 |
Number.isNaN | 检测一个数值是否为 NaN |
Number.parseInt | 字符串转整数 |
Number.parseFloat | 字符串转整数 |
Number.isInteger | 判断一个数是否为整数 |
Math.trunc | 将数字的小数部分抹掉 |
Math.sign | 判断一个数到底为正数,负数还是零 |
ES 6 的对象扩展
对象扩展 | 说明 |
Object.is | 判断两个值是否完全相等 |
Object.assign | 对象的合并 |
Object.setPrototypeOf | 设置原型对象 |
Object.getPrototypeOf |
模块化
- 模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处
- 防止命名冲突。
- 代码复用。
- 高维护性。
模块化规范产品
- ES 6 之前的模块化规范:
- CommonJS => NodeJS\Browserify
- AMD => requireJS
- CMD => seaJS
ES 6 模块化语法
- 模块功能主要由两个命令构成:export 和 import 。
- export 命令用于规定模块的对外接口。
- import 命令用于输入其他模块提供的功能。
使用 babel 对 ES 6 模块化代码转换
- 步骤:
- 安装工具 babel-cli babel-preset-env browserify(webpack)
- npx babel src/js -d dist/js
- 打包 npx browserify dist/js/app.js -o dist/bundle.js