【ES6】学Vue前必须掌握的内容(下)

语言: CN / TW / HK

Symbol 数据类型

ES6引入了一种新的数据类型Symbol,表示独一无二的值。它是JS的第七种数据类型,前六种分别是number、string、undefined、null、boolean、object。

特点:

  • Symbol值是唯一的,解决命名冲突的问题;
  • Symbol值不能与其他数据进行计算,包括字符串的拼接;
  • for...in、for...of 不会遍历Symbol属性。

Symbol是函数,但并不是构造函数。创建一个Symbol数据类型:

const symbol=Symbol(); console.log(typeof(symbol));//symbol

Symbol的使用

``` 1.作为对象的属性值 const sym=Symbol(); const obj={ name:'hua' }; obj[sym]='aaa'; //注意!只能通过属性选择器[]来添加和读取 console.log(obj, obj.sym, obj[sym]); // {name:'hua',Symbol():'aaa'} undefined aaa for(let k in obj){ console.log(obj[k]); // hua }

2.定义常量 const m=Symbol('my'); ```

Set 数据结构

Set类似于数组,但值都是唯一不重复的。Set本身是一个构造函数,通过new Set()生成一个Set实例。

``` const set=new Set();

const set2=new Set([1,2,2,3,3,3]); console.log(set2);//Set(3) {1,2,3} //不是数组,是Set数据结构 console.log([...set2]);//[1,2,3] //数组 ```

Map 数据结构

Map类似于对象,也是键值对的集合,但它的键不限于字符串。也就是说Object结构提供了“字符串-值”的对应,Map结构提供了“值-值”的对应,是一种更为完善的Hash结构实现。

const map=new Map(); map.set(key,value);//添加 map.get(key);//通过键获取值 map.delete(key);//删除

Map的键实际上是和内存地址绑定的,只要内存地址不一样,就视为两个键。

const map=new Map(); map.set(['b'],1); console.log(map.get(['b'])); //undefined const a=['b']; map.set(a,1); console.log(map.get(a)); //1

promise ⭐

① 同步、异步

编辑

主线程执行同步任务完毕后,查询任务队列是否还有异步任务。

② 含义

Promise是ES6引入的异步编程的一种新解决方案。本身是一个构造函数。

简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。

注意:

  • 一个 promise对象只能改变一次状态,成功或者失败后都会返回结果数据。
  • 状态变化只有两种可能:从 pending 变为 fulfilled 和 从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
  • 后文中的 resolved 只指 fulfilled状态,不包括 rejected。

③ 基本使用

之前遇到的异步任务都是一次异步,如果需要多次调用异步函数,是通过“函数瀑布”来实现的,这种也称为“回调地狱”。

//1s后开始打印,共输出三次,每次间隔1s setTimeout(() => { console.log('first'); setTimeout(() => { console.log('second'); setTimeout(() => { console.log('third'); },1000) },1000) },1000)

在一个复杂的程序当中,用 "函数瀑布" 实现的程序无论是维护还是异常处理都是一件特别繁琐的事情,而且会让缩进格式变得非常冗赘。下面展示用Promise实现相同功能:

new Promise(function(resolve,reject){ setTimeout(() => { console.log('first'); resolve(); },1000) }).then(()=> new Promise(function(resolve,reject){ setTimeout(() => { console.log('second'); resolve(); }, 1000); }) ).then(function(){ setTimeout(() => { console.log('third'); },1000) })

解释一下上面的代码:

Promise 构造函数只有一个参数,这个参数是一个函数,该函数在构造之后会直接被异步运行,所以也被称为起始函数。起始函数有两个参数 resolvereject,它们都是函数,由 JavaScript 引擎提供,不用自己部署。

  • resolve函数的作用是,将Promise对象的状态从pending变为resolved(未完成->成功),在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
  • reject函数的作用是,将Promise对象的状态从pending变为rejected(未完成->失败),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。
  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列。
  • then方法可以接受两个回调函数作为参数,第一个回调函数是Promise对象的状态改变为 resoved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数可以省略。

new Promise(function(resolve,reject){ let a=1; let b=2; if(b!=2){ reject('error!'); } else{ resolve(a+b); } }).then(function(value){console.log('a+b='+value);},function(err){console.log(err)})

上面代码的then方法的第二个参数可以省略,然后使用catch方法,即:

new Promise(function(resolve,reject){ let a=1; let b=2; if(b!=2){ reject('error!'); } else{ resolve(a+b); } }).then(function(value){ console.log('a+b='+value); }).catch(function(err){ console.log(err); })

Promise.prototype.catch()方法是.then(null / undefined, rejuctoin)的别名,用于指定发生错误时的回调函数。

  • 一般来说,不要在then()方法里定义reject状态的回调函数(即then的第二个参数),而是使用catch()方法。
  • Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一catch个语句捕获。
  • then块默认会向下顺序执行,return是不能中断的,可以通过throw来跳转至catch实现中断。

finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

const promise=new Promise((resolve,reject)=>{}); promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});

  • finally方法的回调函数不接受任何参数

④ 综合案例

let a=+prompt('请输入一个数字'); var promise =new Promise(function(resolve,reject){ //To Do 要异步执行的事情,这个异步执行的事情有可能成功执行完毕,那么Promise将是fulfilled状态,如果执行失败则是rejected; //下面测试代码,人为设置为rejected状态; if(a!=0){resolve(a/2);} else{reject("不能为0"); } }) promise.then(//调用第一个then() success=>{ console.log("异步执行成功,状态为fulfilled,成功后返回的结果是:"+success); return(" 当前 success "); }, error=>{ console.log("异步执行失败,状态为rejected,失败后返回的结果是:"+error); return(" 当前 error "); } ).then( //调用第二个then() 因为调用第一个then()方法返回的是一个新的promise对象,此对象的状态由上面的success或者error两个回调函数的执行情况决定的: //如果回调函数能正常执行完毕,则新的promise对象的状态为fulfilled,下面执行success2,如果回调函数无法正常执行,则promise状态为rejected;下面执行error2 success2=>{ console.log("第一个then的回调函数执行成功 成功返回结果:"+success2); throw(" 当前 success2 ");//自定义异常抛出 }, error2=>{ console.log("第一个then的回调函数执行失败 失败返回结果:"+error2); return(" 当前 error2 "); } ).catch(err=>{ //当success2或者error2执行报错时,catch会捕获异常; console.log("捕获异常:"+err); }).finally(()=>{ console.log('end'); });

输出结果如下:​编辑

for...of

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。遍历器对象本质上,就是一个指针对象。

ES6引入了 for...of 循环,作为遍历所有数据结构的统一的方法。

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

const arr=['a','b','c']; for(let v of arr){ console.log(v); //'a' 'b' 'c' console.log(arr[v]); //undefined undefined undefined } for(let k in arr){ console.log(k); //'0' '1' '2' console.log(arr[k]); //'a' 'b' 'c' }

数组的键名是数字,但是for...in中是以字符串的形式返回的。for...in主要为遍历对象而设计,不适用于遍历数组。对于普通的对象,for...of 结构不能直接使用,不然会报错,必须部署了 Iterator 接口后才能使用。

Generator函数

Generator 函数是一个普通函数,但是有两个特征。

  • function 关键字与函数名之间有一个星号;
  • 函数体内部使用 yield 表达式,定义不同的内部状态;
  • 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,即上面提到的Iterator对象。

function* myGenerator(){ yield 1; yield 'a'; return 'end'; } let mg=myGenerator(); console.log(mg.next()); //{value:1,done:false} console.log(mg.next()); //{value:'a',done:false} console.log(mg.next()); //{value:'end',done:true}

必须调用遍历器对象的 next 方法,使得指针移向下一个状态,即下一个yield表达式(或return语句)。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

next方法:一般情况下,next方法不传入参数的时候,yield表达式的返回值是undefined,当next传入参数的时候,该参数会作为上一步yield的返回值。

function* myGenerator(){ let x=yield 1; console.log(x); //next无参时undefined,有参时输出参数first return ; //无参时undefined } let mg=myGenerator(); //console.log(mg.next()); console.log(mg.next('first')); console.log(mg.next());

async函数⭐

async函数就是 Generator 函数的语法糖

``` async function test1(){ return 1; } console.log(test1()); //Promise对象

async function test2(){ return Promise.resolve(2); } console.log(test2()); //Promise对象

async function test3(){ const p=Promise.resolve(3); //下面两部分等价 p.then(data=>{ console.log(data); }) // const data=await p; console.log(data); } console.log(test3()); //Promise对象 ```

  • return返回的不是一个Promise类型的对象,返回的结果就是resolved(fulfilled)状态的Promise。
  • return返回的是一个Promise类型的对象,返回值成功就成功。
  • await表达式相当于Promise.then成功的情况。

``` async function test3(){ //const data=await 3; const data=await Promise.resolve(3); console.log(data); } test3();

async function test4(){ const p=Promise.reject(4); try{ const data=await p; console.log('success '+data); } catch(err){ console.log('failure '+err); } } test4(); ```

  • await 3相当于await Promise.resolve(3);
  • Promise.catch异常的情况对应 try...catch。

类class ⭐

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

class lei{ constructor(age){ this.age=age; } fangfa(){ console.log('方法不需要写function'); } } let l=new lei(18); console.log(l.age); //18 l.fangfa(); //方法不需要写function

  • 通过class关键字创建类,类名首字母大写;
  • 类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象;只要new生成实例化对象,就会自动调用该函数,即使不写也会自动生成;
  • 生成实例化对象new不能省略;
  • 方法不需要写function。

class Father { constructor(x, y) { this.x = x; this.y = y; } sum(){ console.log(this.x+this.y); } father(){ return ('我是父亲'); } } class Son extends Father { constructor(x,y){ super(x,y); this.x=x; this.y=y; } son() { console.log(super.father()+'的儿子' ); } sub(x,y){ console.log(this.y-this.x) } } let son = new Son(1, 2); console.log(son); //Son {x: 1, y: 2} son.sum(); // 3 son.son(); //我是父亲的儿子 son.sub(); //1

  • 继承extends,super()。
  • 必须先调用父类的构造函数才能写子类构造函数的this。
  • constructor的this指向实例对象,方法的this指向调用者。

模块⭐

① 概念

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

优点:

  • 防止命名冲突;
  • 代码复用;
  • 高维护性。

② 基本使用

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

//分别暴露 export let name='hua'; export function fn(){ console.log('module'); } //统一暴露 let name='hua'; function fn(){ console.log('module'); } export{name,fn};

```

```

// 默认暴露(常用) 

``` //默认暴露 export default{ age:18, fun:function(){ console.log('默认暴露') } }

```

  • 重名时可用 as 取别名

③ 应用

1) 按需加载:import() 可以在需要的时候,再加载某个模块

button.addEventListener('click',event=>{ import("../ES6/module初学") .then(load=>{ load.open(); }) .catch(err=>{ console.log(err); }) })

上面代码中,import() 方法放在 click 事件的监听函数之中,只有用户点击了按钮,才会加载这个模块。

2) 条件加载:根据不同的情况,加载不同的模块

if(xxx){ import('moduleA').then(...); }else{ import('moduleB').then(...); }

上面代码中,如果满足条件,就加载模块 A,不满足则加载模块 B。 ​