前端|ES6新特性总结

let 1、不能被重复定义

let a let a //报错:Uncaught SyntaxError: Identifier 'a' has already been declared

2、let不存在变量提升,而var存在 var
console.log(a) //undefined var a=1 console.log(a) //1

var在块级作用域中,会将var的声明提前至前面,源代码中,其实真实的代码应该是
var a console.log(a) //undefined a=1 console.log(a) //1

let
console.log(a) //报错:Uncaught ReferenceError: Cannot access 'a' before initialization let a=1 console.log(a)

3、let拥有块级作用域 let命令声明的变量只在其块级作用域中有效,就是{}中,在块级作用域内,若存在用let命令声明的变量,则所在区块对该变量形成封闭作用域,即该变量无视外部的同名变量。
let a=2 { let a=1 console.log(a) //1 }

还有一个网上常说的例子
let items = document.querySelectorAll('.item'); for (var i = 0; i < items.length; i++) { items[i].onclick = function () { items[i].style.background = 'blue'; } }

当我们点击第一,第二,或者第三个块时,类型错误,就是没有找到 item[i] 这个变量,这是由于使用 var 定义的变量。
在for循环中这个块中使用var声明的变量,默认就是全局变量,在 window 上,可以直接访问到。而在for循环这个块中访问到的是全局的变量 i , i++ 在执行完之后,此时在 window.i = 3
这时就用到了,let 声明变量,就会在for循环这个块中,产生一个块级作用域,与全局作用域无关,items[i] 就是我们想要的变量。
const 1、const 声明一个只读的常量。一旦声明,常量的值就不能改变。正是因为其值不能进行修改,因此, ,const一旦声明变量,就必须立即初始化,不能留到以后赋值,const定义的变量潜规则要大写。
2、const与let一样, 只在声明所在的块级作用域内有效
3、const命令声明的常量不存在提升
4、const命令声明的常量同样存在暂时性死区,只能在声明的位置后面使用。
5、const声明的常量,也与let一样不可重复声明
本质:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
简单数据类型( 数值、字符串、布尔值): 值就保存在变量指向的那个内存地址,因此等同于常量
复合数据类型( 对象和数组) : 变量指向的内存地址,保存的只是一个指向实际数据的指针 , const只能保证这个指针是固定的 至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
箭头函数 一个简单的改写案例
let add1 = function(a,b){ return a+b } let add2 = (a,b)=>{ return a+b } console.log(add1(1,2)); //3 console.log(add2(2,3)); //5

与function的区别 1、this的指向问题 箭头函数中,this是静态的,始终指向函数声明时,所在作用域下this的值,所以箭头函数适合与 this 无关的回调
function getname(){ console.log(this.name); } getname2 = ()=>{ console.log(this.name); }//设置 window 对象的 name 属性 window.name = 'window'//设置一个对象中的 name 属性 const obj = { name:'object' }//直接调用 getname()//window getname2() //window//使用 call 方法改变 //call 方法,可以可以编写能够在不同对象上使用的方法,用来调用所有者对象作为参数的方法 getname.call(obj) //object getname2.call(obj) //window

2、不能够作为构造化实例对象
let person = (name,age)=>{ this.name = name this.age = age }let p = new person('pzw',20)//报错:Uncaught TypeError: person is not a constructor

很好理解,因为箭头函数的this指向始终是静态的,所以无法成为一个构造器
3、无法使用argumens 但是可以使用…rest
let fn = ()=>{ console.log(arguments) }fn(1,2,3)//报错:Uncaught ReferenceError: arguments is not definedlet fn = (...rest)=>{ console.log(rest) }fn(1,2,3) //[1,2,3]

4、箭头函数的简写
  1. 当参数只有一个的时候,左边的括号可以不写
  2. 当右边只有一条语句时,函数花括号可以不写,如果函数仅仅语句只有 return 的话,那么 return 必须省略,函数的执行结果就是 return 的值
let pow = n => n*n console.log(pow(3)) //9

扩展运算符 扩展运算符( spread )是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,例如:
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 [...document.querySelectorAll('div')] // [, , ] function push(array, ...items) { array.push(...items); } function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42

替代数组的 apply 方法
// ES5 的写法 Math.max.apply(null, [14, 3, 77]) // ES6 的写法 Math.max(...[14, 3, 77]) //等同于 Math.max(14, 3, 77);

解构赋值 灵活的数组解构赋值
let arr = [1,2,3,4,5,6] let va = arr[4] let [,a,b,c,...rest] = arr console.log(a,b,c,rest) //2,3,4,[5,6]

对象的解构赋值
let p1 = { "name":"zhuangzhuang", "age":25 } let {name,age} = p1; //注意变量必须为属性名 console.log(name,age); //"zhuangzhuang",25

在react中会经常用到
const { cur } = this.state

模板字符串 键盘Tab上的键 ``
ES6 引入新的声明字符串的方式 , 对比 ‘’, “” 的优点:
1、内容可直接换行,不用再使用 ‘+’ 拼接
2、可以直接拼接变量
let white = '小白'; let cat = `
  • 你好,${white}
`; console.log(cat); 直接输出:
  • 你好,${white}

rest参数 rest 参数,用于获取函数的实参,代替 arguments
当我们使用 arguments 时:
function date() { console.log(arguments); console.log(typeof arguments); } date(1, 2, 3, 4);

前端|ES6新特性总结
文章图片

可以看到 arguments 原型中 constructor 属性为 Object,这就说明了 arguments 并不是数组,因此不能使用数组的方法,因此在处理多个参数时,并不能使用更加方便的数组方法进行处理。
接下来,我们再看一下 rest 获取参数的类型:
function date(...rest) { console.log(rest); console.log(typeof rest); } date(1, 2, 3, 4); //打印[1, 2, 3, 4]

迭代器(Iterator) 首先说说for in,for of
简单来说
  • for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
  • for-in总是得到对象的key或数组、字符串的下标。
  • for-of总是得到对象的value或数组、字符串的值,另外还可以用于遍历Map和Set。
const arr = ['red', 'black', 'blue']for(let i of arr) { console.log(i)// 'red' 'black' 'blue' }for(let i in arr) { console.log(i)// 1, 2, 3 }

如何使用 for…of 遍历自定义的数组呢,比如对象中不包含 迭代器(Iterator),但是可以自定义一个迭代器。
// 直接使用 for...of 遍历对象 const obj = { arr: ['red', 'blue', 'green'] }for(let i of obj) { console.log(i) // Uncaught TypeError: obj is not iterable }// 给对象添加一个迭代器 const obj = { color: ['red', 'blue', 'green'],[Symbol.iterator]() { let index = 0 return { next: () => { if (index < this.color.length) { const res = { value: this.color[index], done: false } index++ return res }else { return { value: undefined, done: true } } } } } }for (let i of obj) { console.log(i)// 'red' 'blue', 'green' }

生成器(generator) 在 ES6 中定义一个生成器函数很简单,在 function 后跟上「*」即可:
function* myGenerator() { yield 'hello'; yield 'world'; return 'Generator'; }var g = myGenerator(); g.next(); // { value: 'hello', done: false } g.next(); // { value: 'world', done: false } g.next(); // { value: 'ending', done: true } g.next(); // { value: undefined, done: true }

调用生成器函数会产生一个生成器(generator)。生成器拥有的最重要的方法是 next(),用来迭代。
yield 主要用作代码隔断,生成器(generator)在执行的时候必须使用生成器(iterator)的next() 迭代。一个 next() 只能迭代一段 yield 代码。
我所理解的yield,就是程序执行的一个断点,用来暂停和继续一个生成器函数。
最后一次调用,Generator函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true
实现斐波那契数列例子
function* fab(max) { var count = 0, last = 0, current = 1; while(max > count++) { yield current; var tmp = current; current += last; last = tmp; } }var o = fab(10), ret, result = []; while(!(ret = o.next()).done) { result.push(ret.value); }console.log(result); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Symbol 新增数据类型 ES6 新引入的基础数据类型,表示独一无二的值,是一种类似于字符串的类型。
第七种基本数据类型(Undefined、Null、Boolean、Number,String,Object,Symbol )
Symbol 特点 值是唯一的,用来解决命名冲突
  • 不能与其他数据类型进行运算
  • 定义的对象属性,不能使用 for···in 遍历,但是可以用Reflect.ownKeys 来获取对象的所有键名
简单实例
const s1 = Symbol('1') // 或者: const s2 = Symbol.for('1')// 给对象添加 Symbol 类型的属性,保护对象属性的安全性 const obj = { name: 'xiaobai', [Symbol('say')]: function() { console.log('说话') } } obj.[Symbol('say')]()// 或者 const obj1 = { up: '', dowm: '' } const methods = { up: Symbol(), dowm: Symbol() } obj[methods.up] = function() { } obj[methods.down] = function() { } // 这样创建的 up 和 dowm 方法是唯一的,不会和obj1中的up,dowm 属性发生冲突,保护了属性的安全。

Promise 在开始使用Promise之前,我们首先需要了解Promise的三种状态:
  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
因为 Promise.prototype.thenPromise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。
前端|ES6新特性总结
文章图片

一个Promise的基本案例
let p = new Promise((resolve,reject)=>{ setTimeout(() => {let data = 'https://www.it610.com/article/数据' resolve(data)// let err = '数据读取错误' // reject(err)}, 1000) }).then((data)=>{ console.log(data); },(err)=>{ console.log(err); })

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
Resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
Reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
Promise 封装读取文件
var fs = require('fs')let readfile = new Promise((res,rej)=>{ fs.readFile('./file.md','utf-8',(err,data)=>{ if (err) rej(err) res(data) }) }).then((data)=>{ console.log(data.toString()); },(err)=>{ console.log('读取失败'); })

Promise封装 Ajax请求
let getajax = new Promise((res,rej)=>{ const xhr = new XMLHttpRequest() xhr.open('GET','https://www.baidu.com') xhr.send() xhr.onreadystatechange = ()=>{ if (xhr.readyState === 4){ if (xhr.state >= 200 && xhr.state <= 299){ res(xhr.response) }else{ rej(xhr.status) } } } }).then(data=>{ console.log(data); },err=>{ console.error(err); })

catch语法糖
getajax.catch(err =>{ console.log(err) })

Set集合 ES6 新的数据结构 Set(集合),虽然类似于数组,但是成员的每一个值都是唯一的。集合实现了迭代器(iterator)接口,所以可以使用扩展运算符(…)和for…of进行遍历(让我想到C++的STL的库= 。=)。
集合的属性和方法:
  • size:返回集合的元素个数
  • add:增加一个新元素,返回当前集合
  • delete:删除元素,返回一个Boolean值
  • has:检测集合中是否包含某个元素,返回Boolean值
  • clear:清空集合
const set = new Set(['red', 'blue', 'green'])const res1 = set.has('red') console.log(res1)// trueconst r2 = set.size console.log(r2)// 3set.add('white') console.log(set) // Set(4) {"red", "blue", "green", "white"}set.delete('red') console.log(set) // Set(3) {"blue", "green", "white"}set.clear() console.log(set) // Set(0) {}

Set集合的应用: 数组去重
const arr1 = [1, 2, 3, 3, 4, 2, 5] const res1 = [...new Set(arr1)] console.log(res1)// [1, 2, 3, 4, 5]

两个数组取交集
const arr1 = [1, 2, 3, 3, 4, 2, 5] const arr2 = [4, 5, 6, 7, 5, 6]const res2 = [...new Set(arr1)].filter(item => { const s = new Set(arr2) if(s.has(item)) { return true }else { return false } }) console.log(res2) // [4, 5] // 或者 const res3 = [...new Set(arr1)].filter(item => new Set(arr2).has(item)) console.log(res3) // [4, 5]

两个数组并集(数组合并)
const arr1 = [1, 2, 3, 3, 4, 2, 5] const arr2 = [4, 5, 6, 7, 5, 6] const res4 = [...new Set(arr1), ...new Set(arr2)] console.log(res4) // [1, 2, 3, 4, 5, 4, 5, 6, 7]

数组差集
const arr1 = [1, 2, 3, 3, 4, 2, 5] const arr2 = [4, 5, 6, 7, 5, 6] const res5 = [...new Set(arr1)].filter(item => !new Set(arr2).has(item)) console.log(res5) // [1, 2, 3]

Map ES6 新的数据结构。类似于对象,键值对的集合。但是‘键’的范围不限于字符串。各种类型的值(包括对象)都可以当做键。Map 也实现了 iterator 接口,可以使用扩展运算符(…)和for…of遍历(又是一个STL类型)
Map 的属性和结构
  • size:返回Map元素的个数
  • set:增加一个新元素,返回当前 Map
  • get:返回键名对象的键值
  • has:检测 Map 中是否含某个元素,返回Boolean值
  • clear:清空集合
使用
const map = new Map()map.set('name', 'zhangsan') map.set('age', 18) map.set('say', function() { console.log('hello') })console.log(map)// {"name" => "zhangsan", "age" => 18, "say" => ?}console.log(map.size) // 3const res1 = map.get('name') console.log(res1)// '张三'const res2 = map.has('age') console.log(res2)// trueconst res3 = map.delete('say') console.log(res3)// true

class 概述 ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用ES6的“类”改写,就是下面这样
class stu{ static school = 'HZNU' //还拥有static 静态属性 //静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性 constructor(name,age){ this.name = name this.age = age } print(){ console.log('我的名字是'+this.name+',我的年龄是'+this.age+',我在'+stu.school+'上学'); } }let stu1 = new stu('pzw',21) stu1.print() //我的名字是pzw,我的年龄是21,我在HZNU上学

类的继承
class stu13 extends stu{ static school = 'GDZX' static banji = 13 constructor(name,age,bzr){ super(name,age) this.bzr = bzr } print13(){ console.log('我的名字是'+this.name+',我的年龄是'+this.age+',我在'+stu13.school+'上学,班级是'+stu13.banji+'班,我的班主任是'+this.bzr); } }let stu2 = new stu13('hhb',21,'ZXY') stu2.print13() //我的名字是hhb,我的年龄是21,我在GDZX上学,班级是13班,我的班主任是ZXY

get set 在类里面可以去定义一些getter和setter,getter可以得到一些东西的方法,setter可以设置东西
class classP{ constructor(){ this.arr = [] } get queue(){ return this.arr } set queue(name){ this.arr.push(name) } }let class13 = new classP() class13.queue = 'pzw' class13.queue = 'hhb' console.log(class13.queue) //["pzw", "hhb"]

对象(Object)方法的扩展 Object.is 判断两个值是否相等,相当于全等于(===),但是NaN 和 (-0, 0)判断结果两者不相等
console.log(Object.is(123, 123))// true console.log(Object.is('red', 'red'))// true console.log(Object.is(NaN, NaN))// true===结果为false console.log(Object.is(-0, 0))// false===结果为true

下图所示,展示了使用==,===和Object.is的结果差异:
前端|ES6新特性总结
文章图片

Object的遍历方法
const obj = { book: "Learning ES2017 (ES8)", author: "前端达人", publisher: "前端达人", useful: true }; console.log(Object.keys(obj)) // [ 'book', 'author', 'publisher', 'useful' ] console.log(Object.values(obj)) //[ 'Learning ES2017 (ES8)', '前端达人', '前端达人', true ] console.log(Object.entries(obj)) /* [ [ 'book', 'Learning ES2017 (ES8)' ], [ 'author', '前端达人' ], [ 'publisher', '前端达人' ], [ 'useful', true ] ]*/ //Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。 const entries = new Map([ ['foo', 'bar'], ['baz', 42] ]); Object.fromEntries(entries) // { foo: "bar", baz: 42 }

Object.assign 对象合并,后一个对象中的属性值会覆盖前一个对象上的属性值,如果前一个对象中不存在后一个对象中的某个属性,就会在对象中添加这个属性。
const data1 = { name: 'zhangsan', age: 15, color: 'red' }const data2 = { name: 'zhangsan', age: 15, color: 'blue', height: 60 }const data = https://www.it610.com/article/Object.assign(data1, data2) console.log(data)// {name:"zhangsan", age: 15, color: "blue", height: 60}

数组(Array)方法扩展 includes includes 方法用来检测数组中是否包含某个元素,返回 Boolean 值
const arr = ['red', 'blue', 'green', 'white'] console.log(arr.includes('red')) // true console.log(arr.includes('black')) // false

flat 和 flatMap
const arr1 = [1, 2, 3, [4, 5, 6, [7, 8]]] console.log(arr1.flat())// [1, 2, 3, 4, 5, 6, [7, 8]] // 传参表示深度 console.log(arr1.flat(2)) // // [1, 2, 3, 4, 5, 6, 7, 8]

map map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
let numbers = [4, 9, 16, 25] let sq = numbers.map(value=>{ return Math.sqrt(value) }) console.log(sq); //[ 2, 3, 4, 5 ]

map会对数组的每一项进行处理,返回新数组,返回的新数组包含对之前每一项处理结果;
filter 【前端|ES6新特性总结】filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
let numbers = [4, 9, 16, 25] let sq = numbers.filter(value=>{ return value =https://www.it610.com/article/== 16 }) console.log(sq)//[ 16 ]

every 依据判断条件,数组的元素是否全满足,若满足则返回true
let arr = [1,2,3,4,5] let arr1 = arr.every( (value, index) =>value<3) console.log(arr1) // false let arr2 = arr.every( (value, index) =>value<6) console.log(arr2)// true

reduce arr.reduce(callback, initialValue) 迭代数组的所有项,累加器,数组中的每个值(从左到右)合并,最终计算为一个值
参数: callback: previousValue 必选 --上一次调用回调返回的值
currentValue 必选 --数组中当前被处理的数组项
index 可选 --当前数组项在数组中的索引值
array 可选 --原数组
initialValue: 可选 --初始值
let arr = [0,1,2,3,4] let arr1 = arr.reduce((preValue, curValue) => preValue + curValue ) console.log(arr1)// 10

reduce的高级用法
(1)计算数组中每个元素出现的次数
let names = ['peter', 'tom', 'mary', 'bob', 'tom','peter']; let nameNum = names.reduce((pre,cur)=>{ if(cur in pre){ pre[cur]++ }else{ pre[cur] = 1 } return pre },{}) console.log(nameNum); //{ peter: 2, tom: 2, mary: 1, bob: 1 }

(2)数组去重
let arr = [1,2,3,4,4,1] let newArr = arr.reduce((pre,cur)=>{ if(!pre.includes(cur)){ pre.push(cur) } return pre },[]) console.log(newArr); // [1, 2, 3, 4]

arr.reduceRight(callback, initialValue) 与arr.reduce()功能一样,不同的是,reduceRight()从数组的末尾向前将数组中的数组项做累加。
模块化 模块化是指将一个大的程序文件,拆分成为许多小的文件,然后将文件组合起来。
模块化的好处
  • 防止命名冲突
  • 代码复用
  • 高维护性
ES6模块化语法 模块功能主要由两个命令构成:export和import
  • export命令用于规定模块对外的接口
  • import命令用于输入其他模块提供的功能
默认导入导出 默认导出语法 export default 默认导出成员
//当前文件模块为ml.js //定义私有成员a和c let a = 10 let c = 20 //外界访问不到变量d 因为它没有被暴露出去 let d = 30 function show(){ //将本模块中的私有成员暴露出去 供其他模块使用 export default{ a, c, show } }

默认导入语法 import 接受名称 from ‘模块标识符’
//导入模块成员 import ml from '.ml.js' console.log(ml) //打印结果为 //{a:10, c:20, show:[]function:show]}

按需导出 与 按需导入 按需导出语法 export let s1 = 10
//当前文件模块为m1.js //向外按需导出变量 s1 export let s1 = 'aaa' //向外按需导出变量s2 export let s2 = 'bbb' //向外按需导出方法 say export function say = function(){}

按需导入语法 import { s1 } from ‘模块标识符’
//导入模块成员 import {s1, s2 as ss2, say} from './m1.js' console.log(s1)//打印输出aaa console.log(s2)//打印输出bbb console.log(say)//打印输出[function:say]

async 与 await async 和 await 两种语法结合,可以让异步代码看起来像同步代码。
async async 函数返回值为 promise 对象
promise 对象结果由 async 函数执行的返回值决定
await await 函数必须写在 async 函数中
await 右侧的表达式一般为 promise 对象
await 返回的是 promise 的成功值
await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理
读取文件
const fs = require('fs')let p1 = new Promise((res,rej)=>{ fs.readFile('./file.md',(err,data)=>{ if (err) rej(err) res(data) }) })let p2 = new Promise((res,rej)=>{ fs.readFile('./file2.md',(err,data)=>{ if (err) rej(err) res(data) }) })async function fileread(){ let n1 = await p1 let n2 = await p2 console.log(n1.toString()) //我是文件1里的数据 console.log(n2.toString()) //我是文件2里的数据 }fileread()

Ajax
function Ajax(type='GET', url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open(type, url) xhr.send() xhr.onreadystatechange = function () { if(xhr.readyState === 4) { if(xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) }else { reject(xhr.status) } } } }) }async function xmlajax() { const res1 = await Ajax('GET', 'https://www.baidu.com') const res2 = await Ajax('GET', 'https://www.sina.com') console.log(res1) console.log(res2) }xmlajax()

可选链操作符 ?. 当对象层数比较深,使用可选链操作符,可以免去层级判断。
如果不使用可选链操作符,一般使用 && 来连接判断
let data = https://www.it610.com/article/{ d1:{ d2:{ id:'123' } } }let db = data && data.d1 && data.d1.d2 && data.d1.d2.id console.log(db) //123let content = data?.d1?.d2?.id console.log(content) //123

参考文章:
https://segmentfault.com/a/1190000020934044
https://segmentfault.com/a/1190000020889508
https://www.cnblogs.com/hexiaobao/p/12108572.html

    推荐阅读