Es6
ES6
本文主要介绍 Javascript Es6 语法特性,为初学 JS 时期整理的笔记,格式较为混乱,待二次整理。
ECMAScript 2015 ,是脚本语言的规范
谁在维护ECMA-262
TC39是推荐ECMAScript发展的委员会
阮一峰ES6电子书
https://es6.ruanyifeng.com/
let变量
let
1.let变量不能重复声明
2.块级作用域
3.不存在变量提升
4.不影响作用域链
其他
let声明的变量不会存储在window对象中 在函数内部、作用域、循环内部声明局部变量 局部作用域
用于循环内,不影响循环外的变量
在相同的块级作用域中,不能使用let关键词来重置let/var变量
在相同的块级作用域中,不能使用var关键词来重置let变量
但可重新赋值 num=1
let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的
let变量没有提升,不可提前作用域声明
使用var声明的变量存储到了window对象中 可通过window.a引用
全局作用域,作为window对象的属性保存
let作用域案例
for(var i = 0; i<items.length; i++) {
// 此处为var的话,会污染全局作用域,点击事件修改语句会去全局找i
// 此时i叠加为了3,报错
items[i].onclick = function(){
// this.style.background = 'pink'
items[i].style.background = 'pink'
}
}let的暂时性死区
存在全局变量
tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}const常量
const
1.一定要初始化,赋初始值
2.一般常量使用
大写(潜规则)3.常量的值不能修改
4.块儿级作用域
5.对于数组和对象的元素修改,不算做对常量的修改,不会报错
使用const定义的对象或数组,其实是可变的
注意
let和const在不同作用域,不同块级作用域中是可以重新声明赋值的
var
var会提前声明,全局作用域
函数内使用 var 声明的变量只能在函数内访问,是【局部变量】,
如果不使用 var 则是全局变量function fn(){ let a=10; var b=10; } // console.log(a);// 无法引用 局部变量
变量解构赋值
解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值
这被称为解构赋值
数组的解构
const F4 = ['1','2','3','4']
let [w,s,a,d] = F4
console.log(w) // 1对象的解构
const zhao = {
name: 'sun',
age: '2',
fn: function(){
...
}
}
let {name,age,fn} = zhao
fn()...解构剩余参数
let arr = [1,2,3,4,5,6,7,8,9,0]
// ...c将剩下的元素都交给c
let [a, b ,...c] = arr // 三个变量
console.log(c); // 3,4,5,6,7,8,9,0
// 用于传递多个参数给函数
let arr2 = [123,456,789]
// 传为函数参数...arr2
console.log(sum(...arr2))
// 将arr解构进对象
...{arr}默认值
解构赋值允许指定默认值
let [foo = true] = []
foo // true字符串的解构赋值
字符串也可以解构赋值,因为此时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello'
a // "h"
b // "e"解构重命名
解构赋值,并重命名解构变量
通过:把a解构变量重命名为b
const { a:b } = res连续解构赋值
注:通过连续解构赋值,是没有获取到外侧属性的,为解构最内层的
let obj = {
a: {
b: { c: 1 }
}
}
const { a: { b: { c } } } = obj
// 重命名c为data
// const { a: { b: { c:data } } } = obj
console.log(c) // 1用途
// 交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x]
// 从函数返回多个值
function example() {
return [1, 2, 3]
}
let [a, b, c] = example()模板字符串
ES6引入新的声明字符串的方式 ``
1.let = `` 声明
2.内容中可以出现换行符
3.可以使用
${}拼接变量
对象的简写形式
ES6允许在大括号内,直接写入变量和函数,作为对象的属性和方法
let name = 'xx'
let change = () => {}
const school = {
name,
change,
imcc(){ // 函数简写
...
}
}箭头函数
箭头函数
1.this是静态的,this始终指向函数声明时所在作用域下的this的值
2.不能作为构造函数实例化对象
3.不能使用arguments变量传递实数
let fn = (参数){
...
}箭头函数的简写
省略小括号,当形参有且只有一个的时候
let add = n => {
return n + n
}省略花括号,当代码体只有一条语句的时候,此时return必须省略
箭头后的表达式作为函数返回值
let pow = n => n*2用途
箭头函数适合与this无关的回调,定时器,数组的方法回调
箭头函数
不适合与this有关的回调 事件的回调,因为需要指向事件源
对象的方法,需要指向对象当前的属性,箭头函数指向外侧作用域了
{ name:'xx', getname:()=>{ this.name } }当然在vue中用于获取外层this
函数参数的默认值
函数参数默认值
ES6允许给函数参数赋值初始值
形参初始值:具有默认值的参数,一般位置都要靠后(潜规则) 比如初始值被实参覆盖后,剩下的又是undefined了
形参初始值
function num(a,b,c=10){
return a + b + c
}
num(1,2)结合解构赋值
配合解构,提取出传入的需要的指定参数
还可再配合默认值
function connect({host,user,pwa = '666',port}){
console.log(host)
}
connect({
host:'zhiyu.ltd',
user:'zhiyu',
pwa:'111',
prot:8080
})其他
NaN:不合法的数
数字
+/*其他
rest参数
rest
ES6引入rest参数,用于获取函数的实参,用来替代
arguments
...args
rest可以使用数组的一些方法,提高了参数处理的灵活程度
rest参数必须要放到参数(多个参数)最后
// rest参数
function data(...args){
console.log(args)
}
data(1,2,3)
// arguments参数
function arg(){
console.log(arguments)
}
arg(1,2,3)其他
arguments返回的是类数组对象,rest返回的是一个真实数组
arguments是有length属性的
扩展运算符
扩展运算符
...扩展运算符能将【数组】转换为逗号分隔的【参数序列】
const phone = ['mi','ios','huawwei']
function fn(){
console.log(arguments)
}
fn(...phone) // 就像被解构进去了数组的合并
const kai = ['0']
const fa = ['1']
const kaifa = [...kai,...fa]
console.log(kaifa) // ['0','1']数组的克隆
const on = ['0']
const no = [...on]
console.log(no) // ['0']将伪数组转为真正的数组
const divs = document.querySelectorAll('button') // 伪数组,原型上还是对象
const divArr = [...divs]
console.log(divArr) // 数组拓展-浅拷贝、深拷贝
数组的克隆如果有引用类型数据,也是浅拷贝
浅拷贝,只复制指针,不复制对象本身,共享内存地址,指向同一个堆
复制对象改变,原数据也会改变深拷贝,直接来个新的对象,把堆内存中的数据复制一份,然后重新在堆中开辟一块自己的空间存储数据,
不影响原对象
arguments也可以转为真正的数组,但有rest参数,故没有必要
Symbol
Symbol
Es6引入了一种新的原始数据类型
Symbol,表示独一无二的值。它是javascript语言的第七种数据类型,是一种类似于字符串的数据类型
Symbol特点1.
Symbol的值是唯一的,可以用来解决属性名命名冲突的问题2.
Symbol的值不能与其他数据进行运算,其他Symbol也不行3.
Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名,或Object.getOwnPropertySymbols()创建Symbol
Symbol
Symbol 值通过
Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。Symbol.for()
它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果
有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局,拥有全局登记特性 故这种方法创建通过描述字符串得出唯一Symbol值,会相等
sym.description 直接返回Symbol的描述
配合阮一峰大神的书学习
https://es6.ruanyifeng.com/#docs/symbol
// 创建Symbol,并添加x描述字符串
let s = Symbol('x')
console.log(s,typeof s) // Symbol('x') symbol
let s2 = Symbol('xxx')
let s3 = Symbol('xxx')
console.log(s2 === s3) // false
// 上面是函数,下面是对象,函数对象
// Symbol.for()创建
// 通过这种方式创建的可以通过描述字符串得出唯一Symbol值
let s4 = Symbol.for('xxx')
let s5 = Symbol.for('xxx')
console.log(s4,typeof s4)
console.log(s4 === s5) // trueSymbol作为属性名
Symbol的值是唯一的,可以用来解决属性名命名冲突的问题以下摘自
阮一峰ES6 由于每一个 Symbol 值都是
不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。注意,Symbol 值作为对象属性名时,不能用点运算符。
因为点运算符后面总是字符串,所以不会读取
mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。 同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
let mySymbol = Symbol()
// 第一种写法
let a = {}
a[mySymbol] = 'Hello!'
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
}
// 注意,Symbol 值作为对象属性名时,不能用点运算符。
a.mySymbol = 'Hello!'
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"Symbol.for() 与 Symbol()
Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。
用途
场景:有一个对象,有好多属性
简单,高效,安全地创建,不会冲突,破坏原有属性
let game = {}
// 简单,高效,安全地创建,不会冲突,破坏原有属性
let methods = {
// 独一无二
up: Symbol()
}
game[methods.up] = function(){
console.log(1)
}
console.log(game)
// 第二种添加方式
let yx = {
name: 'xxx',
// 表达式
[Symbol('say')]: function(){ // 这样好像没法调用,遍历?
console.log('yx')
}
}Symbol的内置值
除了定义自己使用的
Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法拥有它的
自动执行场景,并自动触发作为对象的属性设置,
来改变对象特点场景的表现,扩展对象功能
Symbol.hasInstance
当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
可以传入参数(param)
可改变特点场景的表现
class Person{
static[Symbol.hasInstance](param){
console.log(param)
console.log('用来检测啦')
return true // 改变结果
}
}
let o = {}
console.log(o instanceof Person)Symbol.isConcatSpreadable
对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()合并数组时,是否可以展开
不展开,指整体合并,不拆散
['1','2',['合并的','合并的']]
const arr = ['1','2','3']
const arr2 = ['4','5']
arr2[Symbol.isConcatSpreadable] = false
console.log(arr.concat(arr2))拓展-各种数据类型
USONB
u undefined
s string symbol
o object
n null number
b boolean
迭代器
迭代器
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要有部署iterator接口,就可以完成遍历操作1.ES6创造了一种新的遍历命令for...of循环,iterator接口主要供for...of消费
2.原生具备
iterator接口的数据(可用for...of遍历)Array Arguments Set Map String TypedArray NodeList3.
迭代器的工作原理1.创建一个指针对象,指向当前数据结构的起始位置
2.第一次调用
对象身上的next方法,指针自动指向数据结构的第一个成员3.接下来
不断调用next方法,指针一直往后移动,直到指向最后一个成员4.每调用
next方法便返回一个包含value和done属性(表示循环是否遍历完成,完成true)的对象Object { value: undefined, done: true }注:需要自定义遍历数据时,需要联想到迭代器
示例
对象身上具有
Symbol.iterator函数,即可完成遍历iterator 对象的一个属性
done 一个遍历的完成时
const arr = ['1','2','3','4']
// 具有Symbol.iterator函数的对象,可以完成遍历
let iterator = arr[Symbol.iterator]()
// 调用对象的next方法
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
//Object { value: undefined, done: true } // done:true 遍历完成for...of无法遍历没有迭代器的普通对象,解决方法
const school = {
name: 'Student',
student:[
'a','b','c','d','e'
],
// 手动实现迭代器
[Symbol.iterator](){
let that = this // 改变this指向,也可以使用箭头函数
let i = 0
return {
next: function(){
if(i < that.student.length){
const result = {value:that.student[i],done:false}
i++ // 自增遍历下标
return result // 返回
}else{
return {value:undefined,done:true}
}
}
}
}
}
// for...of不能遍历普通对象,没有iterable接口,具有迭代器接口的对象才可以使用
for(let v of (school)){
// 也可使用kes和values方法,获取键名、键值
// for(let v of Object.keys(school.student)){
// for(let v of Object.values(school.student)){
console.log(v);
}拓展方法
for...in 保存键名,for...of保存键值
for...in适合对象,for...of适合数组
Object.keys返回一个数组,成员是参数对象自身的(不含继承)
所有可遍历属性的键名
Object.values返回一个数组,成员是参数对象自身的(不含继承)
所有可遍历属性的键值
Object.entries返回一个数组,成员是参数对对象自身的(不含继承)
所有可比遍历属性的键值对数组
Object.fromEntries()是
Object.entries()的逆操作,用于将一个键值对数组转为一个对象Object.fromEntries([ ['foo', 'bar'], ['baz', 42] ]) // { foo: "bar", baz: 42 }
Generator 生成器函数
生成器函数(Generator 函数)是ES6提供的一种异步编程解决方案,语法与传统函数完全不同
异步编程新的解决方案之前是使用纯回调函数 node fs ajax mongodb
yield表达式
1.遇到
yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值(相当于函数的分割符,yield分割产生多块代码分别执行)2.下一次调用
next方法时,再继续往下执行,直到遇到下一个yield表达式(一次next()只执行一块代码)3.如果没有再遇到新的
yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值4.如果该函数没有
return语句,则返回的对象的value属性值为undefined5.
yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行7.Generator生成器函数可以不用
yield表达式,这时就变成了一个单纯的暂缓执行函数function * f() { console.log('执行了!') } var generator = f() // 如果是普通函数,此时已经执行log setTimeout(function () { generator.next() }, 2000)上面代码中,函数
f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个 Generator 函数,就变成只有调用next方法时,函数f才会执行
for...of循环
for...of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法,即可依此显示yield表达式的值
示例
// 异步编程新的解决方案
function * gen(){
// yield 函数代码的分割符,分割yield之前代码,产生四块代码
// 一次next,只输出一块代码内容
// 通过多次next(),控制代码向下执行
console.log('第1块')
yield '1'
console.log('第2块')
yield '2'
console.log('第3块')
yield '3'
console.log('第4块')
}
let iterator = gen()
// log结果没有出现,但本身是一个迭代器对象,可以调用next方法
// console.log(iterator)
// console.log(iterator.next()) // 输出next()返回的对象{value done}
iterator.next() // 1
iterator.next() // 2
// 遍历该可迭代对象
for(let i of gen()){
console.log(i) // 1/2/3 当前yield后的表达式的迭代值及该块代码的log
// 如果加个定时器,只会多次yield,log并不会多次出现,因为不是迭代对象吧
}生成器函数next()参数
next()方法可以传入实参,实参作为yield语句返回结果,yield语句需使用变量接收返回结果第一次调用next(),传递的参数是无效的
function * gen(arg){ console.log(arg) let b = yield 1 // 接收返回结果 console.log(b) let c = yield 2 console.log(c) yield 3 } // 传递参数 let iterator = gen('a') console.log(iterator.next()) // 第一个next()无法传递参数 // next方法可以传入实参,实参作为yield语句返回结果,yield语句需使用变量接收返回结果 console.log(iterator.next('b')) console.log(iterator.next('c')) console.log(iterator.next())
异步实例1
异步操作,隔x秒打印x
定义函数,并将函数
放入yield语句中
将下一步的next()交给该函数,完成异步调用自动执行
// 异步编程 文件操作 网络操作(ajax,request) 数据库操作
// 生成器函数方法
function one(){
setTimeout(()=>{
console.log('one')
// 将下一步的next()交给该函数,完成自动执行
iterator.next()
},1000)
}
function two(){
setTimeout(()=>{
console.log('two')
iterator.next()
},2000)
}
function three(){
setTimeout(()=>{
console.log('three')
},3000)
}
// 将函数放入yield语句中
function * gen(){
yield one()
yield two()
yield three()
}
let iterator = gen()
iterator.next() // next()执行
// 原先套娃做法,回调地狱,不断向前推进
// setTimeout(()=>{
// console.log(1)
// setTimeout(()=>{
// console.log(2)
// setTimeout(()=>{
// console.log(3)
// },3000)
// },2000)
// },1000)异步实例2
next()方法可以传入实参,实参作为yield语句返回结果,yield语句需使用变量接收返回结果
// 模拟获取依次执行 用户数据->订单数据->商品数据
function user(){
setTimeout(() => {
let data = '用户数据'
iterator.next(data) // next()传参 往下执行
},1000)
}
function orders(){
setTimeout(() => {
let data = '订单数据'
iterator.next(data)
},1000)
}
function data(){
setTimeout(() => {
let data = '商品数据'
iterator.next(data)
},1000)
}
function * gen(){
let Guser = yield user()
console.log(Guser)
let Gorders = yield orders()
console.log(Gorders)
let Gdata = yield data()
console.log(Gdata)
}
let iterator = gen()
iterator.next()Promise对象
Promise是ES6引入的异步编程的新解决方案。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果1.Promise构造函数:Promise(excutor){}
2.Promise.prototype.then方法
3.Promise.prototype.catch方法
Promise对象及then方法
Promise(承诺)的三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)1.只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
2.一旦状态改变,就不会再变,任何时候都可以得到这个结果
3.
Promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject4.
resole函数的作用是将Promise对象的状态从'未完成'变为'成功'
reject函数的作用是,将Promise对象的状态从'未完成'变为'失败'5.
Promise实例生成后,可以用then方法分别指定resolved状态和rejected状态的回调函数
then方法1.
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态为resolved时调用,第二个回调函数是Promise对象的状态为rejected时调用。这两个函数都是可选参数,不一定要提供。他们都接受Promise对象传出的值作为参数2.状态为成功调用then方法的第一个函数,状态为失败则调用第二个函数,函数的形参接受
Promise对象传出的值
// 实例化Promise对象
// Promise的三个状态:进行中,成功,失败
// 参数是个函数类型值,函数有两个形参,resolve,reject
const p = new Promise(function(resolve,reject){
setTimeout(function(){
// let data = '数据库调用成功'
// 调用resolve和reject来改变Promise的状态
// resolve(data)
let err = '数据库调用失败'
reject(err) // 传出err值
},1000)
})
// 成功则调用Promise对象的then方法
// then方法接收两个函数参数,函数参数成功失败的值分别为value和reason
// 状态为成功调用then方法的第一个函数,状态为失败则调用第二个函数
p.then(function(value){
console.log(value)
},function(reason){
console.error(reason);
})Promise封装读取文件
// 1.引入fs模块
const fs = require('fs')
// 3.使用Promise封装
const p = new Promise((resolve,reject) => {
fs.readFile('./为学.md',(err,data) => {
if(err) reject(err)
resolve(data.toString())
})
})
p.then((value)=>{
console.log(value)
},(reason) => {
console.log("读取失败")
})
// 2.调用方法读取文件
/* fs.readFile('./为学.md',(err,data)=>{
// 失败,抛出错误
if(err) throw err
// 成功,输出内容
console.log(data.toString())
}) */Promise封装AJAX
// 接口地址:https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.初始化
xhr.open("GET", "https://api.apiopen.top/getJoke")
// 3.发送
xhr.send()
// 4.绑定事件,处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功调用resolve
resolve('成功' + JSON.parse(xhr.response))
} else {
// 失败调用reject
reject('error ' + xhr.status)
}
}
}
})
// 指定回调
p.then((value) => {
console.log(value)
},(reason) => {
console.log(reason)
})Promise.prototype.then方法
then方法返回结果也是一个Promise对象,该Promise对象的状态由回调函数的执行结果决定1.如果回调函数中返回的结果是
非Promise类型的对象,状态为成功,返回值为对象成功的值,不写return默认返回一个undefined,不是Promise对象,状态也为成功2.如果回调函数中的返回结果是
一个Promise对象,那这个返回对象的状态就决定了then方法返回的Promise的状态,值也是返回对象结果Promise的值(就是全看儿子了)3.
then方法可以链式调用,根据then返回的Promise对象决定是否继续,用来执行嵌套异步任务
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据')
},1000)
})
const result = p.then(value => {
console.log(value)
// 1.非Promise类型的属性
// return 1
// 2.是Promise对象
return new Promise((resolve, reject) => {
// 内部Promise决定外部
reject('404')
})
// // 3.抛出错误
throw new Error('500')
throw '500'
},reason => {
console.warn(reason)
})
// 查看result接受的then方法返回值
console.log(result)
// 可以链式调用,根据then返回的Promise对象决定是否继续
// 嵌套异步任务
p.then(value => {
return new Promise((resolve,reject)=>{
// 失败,返回结果决定下一个then走失败2
reject('400')
})
},reason => {
console.error('失败1')
}).then(value => {
console.log('成功')
},reason => {
console.error('失败2')
})Promise封装读取多个文件
// 1.引入fs模块
const fs = require("fs")
// 3.使用Promise实现
const p = new Promise((resolve, reject) => {
fs.readFile("./为学.md", (err, data) => {
resolve(data)
})
})
p.then(
(value) => {
console.log(value)
return new Promise((resolve, reject) => {
fs.readFile("./测试2.md", (err, data) => {
// 合并两个md并向下传递
resolve([value,data])
})
})
},
(err) => reject(err)
).then((value) => {
return new Promise((resolve, reject) => {
fs.readFile("./测试3.md", (err, data) => {
// 压入第三个md
value.push(data)
resolve(value)
})
})
}).then((value) => {
// join拼接数组为字符串并换行,内部会转为字符串,不需要toString()
console.log(value.join('\n'))
})
// 2.调用方法读取多个文件,具有层级关系
// fs.readFile("./为学.md", (err, data1) => {
// fs.readFile("./测试2.md", (err, data2) => {
// fs.readFile("./测试3.md", (err, data3) => {
// let result = data1 + "\n" + data2 + "\n" + data3
// console.log(result)
// })
// })
// })catch方法
catch方法,相当于then方法中的失败状态回调,语法糖可以直接跟在then方法后
// catch方法,语法糖,相当于then方法中的失败状态回调
const p = new Promise((resolve, reject) => {
reject('error')
})
p.catch((reason) => {
console.warn(reason)
})finally方法
finally方法,用于指定不管Promise对象最后的状态如何,都会执行的操作。ES2018可以用来做收尾工作
promise.then(result => {···})
.catch(error => {···错误})
.finally(() => {···收尾})try/catch/finaly
try/catch/finally语句用于处理代码中可能出现的错误信息1.
try语句允许我们定义在执行时进行错误测试的代码块2.
catch语句允许我们定义当try代码块发生错误时,所执行的代码块3.
finally语句在try和catch只会无论有无异常都会执行4.在
try中可使用throw语句来抛出异常5.不能捕获异步抛出异常
function myFunction() {
var message, x;
message = document.getElementById("message");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
// 错误则抛出异常
if(x == "") throw "为空";
if(isNaN(x)) throw "不是一个数字";
if(x > 10) throw "太大";
if(x < 5) throw "太小";
}
catch(err) {
// err接受try的异常返回值
message.innerHTML = "输入的值 " + err;
}
finally {
// 清空input
document.getElementById("demo").value = "";
}
} // 来自菜鸟教程Set
Set-集合
ES6提供了新的数据结构
Set(集合)。它类似于数组,但成员的值都是唯一的,没有重复的值(自动去重)。集合实现了iterator接口,所以可以使用扩展运算符和for...of进行遍历。集合的属性和方法:1.
size返回集合的元素个数2.
add增加一个新元素,返回当前集合3.
delete删除元素,返回boolean值4.
has检测集合中是否包含某个元素,返回boolean值5.
clear清空Set6.Set结构的实例拥有
四个遍历方法,可以用于遍历成员
Set.prototype.keys():返回键名的遍历器Set.prototype.values():返回键值的遍历器Set.prototype.entries():返回键值对的遍历器Set.prototype.forEach():使用回调函数遍历每个成员7.可用于对数组的去重
let s = new Set()
// 成员值都是唯一的,会自动去重
let s2 = new Set(['1','1','2','3','4','5'])
console.log(s2,typeof s2) // 类型Object
// 元素个数
console.log(s2.size)
// 添加新的元素
s2.add('6')
// 删除元素
s2.delete('6')
// 检测元素,返回boolean值
console.log(s2.has('1')) // true
console.log(s2.has('100')) // false
// 清空
// s2.clear()
for(let v of s2){
// 可迭代
console.log(v)
}
// 可使用扩展运算符展开
let arr = [...s2]用途
let arr = [1, 2, 3, 4, 5, 6]
// 1.数组去重
// 使用扩展运算符展开
// let result = [...new Set(arr)]
// 2.交集
let arr2 = [4, 5, 6, 5, 6]
let mix = [...new Set(arr)].filter(item => { // filter过滤
let s2 = new Set(arr2) // 去重arr2
if (s2.has(item)) { // 使用has判断包含
return true
} else return false
})
// 交集简化
// let mix = [...new Set(arr)].filter(
// // 省略花括号,此时return必须省略
// // 箭头后的表达式作为函数返回值
// item => new Set(arr2).has(item)
// )
console.log(mix)
// 3.并集
// 使用扩展运算符合并数组,再使用Set将数据展开到数组中,数据类型最终为数组
let union = [...new Set([...arr,...arr2])]
console.log(union)
// 4.差集,互相间没有的
// 差集为交集的逆运算,不在arr2的即为差集数 !s2.has(item)
let diff = [...new Set(arr)].filter( item => !new Set(arr2).has(item))
console.log(diff)Map
Map
ES6提供了
Map数据结构。它类似于对象,也是键值对的集合。但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用扩展运算符和for...of进行遍历。Map的属性和方法:1.
size返回Map的元素个数2.
set增加一个新元素,返回当前Map3.
get返回键名对象的键值4.
delete删除某个键,返回boolean值4.
has检测Map中是否包含某个元素,返回boolean值5.
clear清空合集,返回undefined6.作为构造函数,
Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组const map = new Map([ ['name', '张三'], ['title', 'Author'] ]);
示例
// 声明Map
// 接受一个数组作为参数,该数组的成员是一个个表示键值对的数组
let m = new Map([
['key','value'],
])
// 添加元素
m.set('name','school') // 键值对
m.set('change',function back(){
console.log('change')
})
let key = {
school: 'to'
}
// 各种类型的值都可以是键,对象key作为键
m.set(key,['1','2','3','4','5'])
console.log(m)
// 个数
console.log(m.size)
// 删除
m.delete('name')
// 获取,返回键名对象的键值
console.log(m.get(key))
// 清空
// m.clear()
// 遍历
for (let v of m) {
// 返回的是一个个键值对数组
console.log(v)
}
// 扩展
let arr = [...m]Class类
class
ES6提供了更接近传统语言的写法,引入了
Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分概念,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
基本语法
声明一个class 类名{},内部包含
constructor()构造方法,该构造方法名字不能修改,会自动执行,在class内部定义prototype原型方法,必须使用对象简写形式
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类必须有constructor()方法与方法之间,不需要逗号分隔
ES6的类,完全可以看作构造函数的另一种写法
使用的时候,也是直接对类使用
new命令,跟构造函数的用法完全一致
// class
class Phone{
// 构造方法 名字不能修改 自动执行
constructor(brand,price){
this.brand = brand
this.price = price
}
// 方法必须使用该语法(对象简写形式),不能使用ES5的对象完整形式
call(call){
console.log(`class ${call}`)
}
}
let onePlus = new Phone('一加',3999)
onePlus.call('一加') // __proto__
console.log(onePlus)
// ES5构造函数实例化对象
// function Phone(brand,price){
// this.brand = brand
// this.price = price
// }
// 添加方法
// Phone.prototype.call = function(){
// console.log('call')
// }
// 实例化对象
// let Huawei = new Phone('华为',5999)
// Huawei.call()
// console.log(Huawei)class静态成员static
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上
static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就被称为静态方法静态成员,属于函数对象不属于实例对象,属于类不属于实例
// class
// static标注的属性和方法,属于类,不属于实例
class Phone{
// 静态属性
static name = 'mi'
static change(){
console.log('change')
}
}
let mi = new Phone()
console.log(mi.name) // undifined
console.log(Phone.name) // mi
// ES5构造函数静态对象
function Phone(){}
Phone.name = 'mi' // 属于函数对象不属于实例对象,静态成员,属于类不属于实例
let nokia = new Phone()
// undifined,实例对象与函数对象的属性是不通的
console.log(nokia.name)
// 想通可以定义到原型prototype上去
Phone.prototype.name = 'mi'
console.log(nokia.name) // mi
// class Test {
// a = 1 // 内部的对象属性需new获取
// }
// new Test().a // 1
// 类属性,不用new即可获得
// Test.b = 12对象extends继承父类
Class实现:Class可以通过
extends关键字实现继承,继承父类属性方法子类必须在
constructor方法中调用super方法,否则新建实例时会报错,子类会找不到thisclass 子类 extends 父类 { constructor(){ super(...) } }
super关键字有两种使用方法,既可以当作函数使用(同上),也可以当作对象使用
作为
函数使用,代表父类的构造函数,super代表父类的constructor方法作为
函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错class B extends A { m() { // 需是constructor super(); // 报错 } }作为
对象使用,在普通方法中,指向父类的原型对象;在静态方法中,指向父类// 普通方法 class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 ,指向父类原型 // super.p()就相当于A.prototype.p() } } // 静态方法 super在静态方法之中指向父类,在普通方法之中指向父类的原型对象 class Parent { static myMethod(msg) { console.log('static', msg); } myMethod(msg) { console.log('instance', msg); } } class Child extends Parent { static myMethod(msg) { super.myMethod(msg); } myMethod(msg) { super.myMethod(msg); } } Child.myMethod(1); // static 1 var child = new Child(); child.myMethod(2); // instance 2ES5实现:
于子级构造函数中,调用
phone并通过call改变指向(同ES6异同)Phone.call(this,brand,proce)设置子级函数构造函数的原型,使子级包含父级原型方法
SmartPhone.prototype = new Phone校正,将子级函数的constructor从原型指回构造函数
SmartPhone.prototype.constructor = SmartPhone声明子类的方法属性
SmartPhone.prototype.photo = xxx
ES5和class的异同:于子函数调用父函数生成,call方法改变this指向
由于使用call方法,和class还是有点不一样的
比起ES5的通过修改原型链实现继承,class要清晰和方便很多
子函数原型上也有两个父函数缔造的属性,而class super是没有的
相当于把this指针交给父函数使用
摘自阮一峰:
ES5 的继承,实质是先创造子类的实例对象
this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到
this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
Object.getPrototypeOf方法:可以用来从子类上获取父类,可以用该方法判断,一个类是否继承了另一个类
Object.getPrototypeOf(ColorPoint) === Point // true
示例代码
// class
class Phone{
// 构造方法
constructor(brand,price){
this.brand = brand
this.price = price
}
// 父类的成员属性
call(){
console.log('call')
}
}
// class子类
// extends继承父类属性方法
class SmartPhone extends Phone{
// 构造方法
constructor(brand,price,color,size){
// super代表父类的constructor方法
super(brand,price)
this.color = color
this.size = size
}
photo(){
console.log('photo')
}
}
const apper = new SmartPhone('apper',9999,'black','6')
console.log(apper)
apper.photo()
// ES5构造函数继承
function Phone(brand,proce){
this.brand = brand
this.proce = proce
}
Phone.prototype.call = function(){
console.log('call')
}
// 子级构造函数
function SmartPhone(brand,proce,color,size){
// 调用Phone生成,call方法改变this指向
// 使用call方法,和class还是有点不一样的
// 比ES5的通过修改原型链实现继承,class要清晰和方便很多
// 子函数原型上也有两个父函数缔造属性,而class super是没有的
// 相当于把this指针交给父函数使用
Phone.call(this,brand,proce)
this.color = color
this.size = size
}
// 设置子级构造函数的原型,使子级包含父级原型方法
SmartPhone.prototype = new Phone
// 校正,constructor从原型指回构造函数
SmartPhone.prototype.constructor = SmartPhone
// 声明子类的方法
SmartPhone.prototype.photo = function(){
console.log('photo')
}
let chuizi = new SmartPhone('锤子',2499,'black','5.5')
console.log(chuizi) // 原型继承
console.log(chuizi.__proto__.photo()) // 没有返回值,photo + undefined子类重写父类方法
在js class语法中,子类是不可以直接去调用父类方法的,通过重写
// 代码其他位置同上,就子类多了个call()重写
class SmartPhone extends Phone{
...代码
call(){
console.log('子类call')
}
}
...
apper.call() // 子类callclass中的get和set
get封装动态属性:读取price就会执行get,返回值就是这个属性的值 set控制判断值是否合法:修改就会执行,必须传
newVal参数
// get 和 set
class Phone{
// 读取price就会执行get,返回值就是这个属性的值
get price(){
console.log('get读取price')
return 'price'
}
// 修改就会执行
set price(newVal){
console.log('set修改price' + '修改后:' + newVal)
}
}
// 实例化对象
let s = new Phone()
console.log(s.price) // 打印log/price
s.price = 'set' // logES6数值扩展
Number.EPSILON
Number.EPSILON是Js表示的最小精度值接近于
2.220446049250313e-16
差值小于最小精度,则认为他们是相等的用于
浮点数运算,设置精度,因为浮点数计算是有误差的
console.log(.1 + .2 === .3) // false
function equal(a,b){
if(Math.abs(a-b) < Number.EPSILON){
return true
}else return false
}
console.log(equal(.1 + .2 , .3)) // true 误差小于最小精度,相等进制数
let b = 0b1010 // 二进制 0b
let o = 0o777 // 八进制 0o
let d = 100 // 十进制
let x = 0xff // 十六进制 0xNumber.isFinite
检测一个数值是否为有限数
console.log(Number.isFinite(100)) // true
console.log(Number.isFinite(100/0)) // fasle
console.log(Number.isFinite(Infinity)) // falseNumber.isNaN
检测一个数是否为NaN
ES5独立,ES6置于Number内(方便其他语言习惯)
console.log(Number.isNaN(123*'w')) // trueNumber.parseInt Number.parseFloat
字符串转整数
console.log(Number.parseInt('321312js')) // 321312
console.log(Number.parseFloat('3.21312js')) // 3.21312Number.isInteger
判断一个数是否为整数
console.log(Number.isInteger(2.3)) // fasleMath.trunc
将数字的小数部分抹掉
console.log(Math.trunc(3.4343)) // 3Math.sign
判断一个数到底为正数 负数 还是零 正数返回1 负数返回-1 零返回0
console.log(Math.sign(100)) // 1
console.log(Math.sign(-100)) // -1
console.log(Math.sign(0)) // 0ES6对象方法扩展
Object.is
判断两个值是否完全相等
同值相等,和直接用全等号的区别:判断两个NaN时会相等,NaN和任何值都不相等
console.log(Object.is(120,120)) // true
console.log(Object.is(NaN,NaN)) // false
console.log(NaN === NaN) // falseObject.assign
对象的合并,不会影响原对象
参数:
第一个参数是目标对象,后面的参数都是源对象
后覆盖前,前作为模板,前具有后没有内容,保留Object.assign()方法实行的是浅拷贝,而不是深拷贝
可用于配置合并
const config1 = {
host:'8080',
name:'1',
test:'1'
}
const config2 = {
host:'5000',
name:'2'
}
console.log(Object.assign(config1,config2))Object.setPrototypeOf
设置对象原型
参数:对象,设置的原型
const school = {}
const cities = {'原型':['proto']}
Object.setPrototypeOf(school,cities) // 设school原型为cities
console.log(school)Object.getPrototypeOf
读取对象原型
参数:对象,读取的原型
const school = {}
const cities = {'原型':['proto']}
Object.setPrototypeOf(school,cities)
console.log(school)
console.log(Object.getPrototypeOf(school)) // cities拓展
Object.create
创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
const me = Object.create(person)模块化
模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来
模块化的好处
1.防止命名冲突
2.代码复用
3.高维护性
模块化规范产品
ES6之前的模块化规范
CommonJS ==> NodeJS、Browserify
AMD ==> requireJS
CMD ==> seaJS
浏览器使用ES6模块化方式一
export用于规定模块的对外接口
向外暴露
import 用于输入其他模块提供的功能
语法:
将module.js中暴露的数据存到
module变量中
<script>标签type="module"于标签中引入模块
<script type="module"> // type="module" // 引入module.js模块内容 import * as module from "./module.js" console.log(module) </script>模块化需在服务器环境运行
跨域的问题
浏览器使用ES6模块化方式二
引入模块的方法
1.制定入口文件
app.js,于入口文件引入模块,最后于页面引入app.js<script src="./app.js" type="module"></script>2.html内
script type="module"标签
ES6模块暴露数据语法
分别暴露
通过
export暴露
export let school = 'school'
export function teacher() {
console.log('teacher')
}统一暴露
通过
export{'暴露数据1','数据2'}方式对象简化写法包含【要暴露的数据名】
let school = 'school'
function findJob(){
console.log('teacher')
}
// 统一暴露
export {school,findJob}默认暴露
通过
export default{}方式【内含数据】数据写为对象形式,为一个整体
export default {
school : 'school',
teacher : function(){
console.log('teacher');
}
}区别
1.通过export方式导出,在import导入时可以选择{ }一个导入或多个导入,但不能自定义模块名
import { a,b } from ab // export {a,b}2.export default在import导入不需要加{ },可以自定义模块名,但导入后多个模块为一个整体对象
import a from ab // export default {a,b} console.log(a) // { a,b }
ES6模块引入模块语法
1.通用导入方式
import * as m2 from "./m2.js"2.解构赋值形式
从模块文件中取出对应模块
school,teach变量便可直接使用
import { school,teach } from "./module.js"解构赋值形式-应对分别暴露
school变量重名,
as决定别名
import { school as guigu, findJob } from "./m2.js"解构赋值形式-应对统一暴露
应对默认暴露,
为default定义别名,也可直接定义名称(简便形式),但不能直接使用default
import { default as m3 } from "./m3.js"3.简便形式
简便形式,只能应对默认暴露
import mo from "./m3.js"babel
是一个Js编译器,可以对ES6模块化代码转换,转化为ES5
安装工具
babel-cli babel命令行工具包
babel-preset-env 将语法转换为ES5
browserify browserify打包工具(项目中使用webpack)
安装
1.安装工具
babel-cli
babel命令行工具包
babel-preset-env
将语法转换为ES5
browserify
browserify打包工具(项目中使用webpack)
2.安装
初始化
npm init --yes
安装三
npm i babel-cli babel-preset-env browserify -D 安装 -D dev开发环境
局部安装(npx)
npx babel全局安装
babel
执行转换为Es5
npx babel src/js -d dist/js --presets=babel-preset-env 第一个路径是代转换文件位置,第二个路径是生成文件位置
--presets=babel-preset-env是配置
3.打包
直接引入转换的js会报错,因为模块化语法是CommonJS的,需要打包
npx browserify dist/js/app.js -o dist/bundle.js文件夹内需存在引入的依赖js,否则打包报错
import $ from 'jquery';
// 等同于
const $ = require("jquery");