ES6②

2022-01-18 17:21:57 浏览数 (1)

生成器

基本概念

Generator 函数是Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

代码语言:javascript复制
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(helloworld),即该函数有三个状态:hello,world 和 return 语句(结束执行)。

然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)

下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

ES6没有规定,function关键字与函数名之间的星号,写在哪个位置。这导致下面的写法都能通过。

代码语言:javascript复制
function * foo(x, y) { ··· }

function *foo(x, y) { ··· }

function* foo(x, y) { ··· }

function*foo(x, y) { ··· }

生成器函数解决回调地狱问题

要实现异步要不断的嵌套,如下。

代码语言:javascript复制
setTimeout(()=>{
    console.log("1s")
    	setTimeout(()=>{
   	 console.log("2s")
    		 setTimeout(()=>{
   		 console.log("3s")
		},3000)
 },2000)
},1000)

这就是所谓的callback hell

解决方法

代码语言:javascript复制
function one(){
    setTimeout(()=>{
        console.log("1s")
        iterator.next()
    },1000)
}
function two(){
    setTimeout(()=>{
        console.log("2s")
        iterator.next()
    },2000)
}
function three(){
    setTimeout(()=>{
        console.log("3s")
     
    },3000)
}
function *gen(){
    yield one();
    yield two();
    yield three();


}
var  iterator =gen()
  iterator.next()

实例2

代码语言:javascript复制
//模拟获取  用户数据  订单数据  商品数据 
       function getUsers(){
           setTimeout(()=>{
               let data = '用户数据';
               //调用 next 方法, 并且将数据传入
               iterator.next(data);
           }, 1000);
       }
       function getOrders(){
           setTimeout(()=>{
               let data = '订单数据';
               iterator.next(data);
           }, 1000)
       }
       function getGoods(){
           setTimeout(()=>{
               let data = '商品数据';
               iterator.next(data);
           }, 1000)
       }
       function * gen(){
           let users = yield getUsers();
           let orders = yield getOrders();
           let goods = yield getGoods();
       }
       //调用生成器函数
       let iterator = gen();
       iterator.next();

promise对象

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

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

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

如果某些事件不断地反复发生,一般来说,使用stream模式是比部署Promise更好的选择。

基本使用

代码语言:javascript复制
const p =new Promise(function(resolve,reject)
   {
      setTimeout(()=>{

       let data ='数据返回成功'
       resolve(data)
       // let err ='数据返回失败'
       // reject(err)

      },2000)
   }
   )
   p.then(function(value){
       console.log(value)

   },function(reason){
       console.log(reason)
   })

读取文件

代码语言:javascript复制
const fs = require('fs')
// fs.readFile('aa.txt',(err,data)=>{

//     if(err) throw err;
//     console.log(data.toString())
// })
const p =new Promise(function(resolve,reject){
  fs.readFile('aa.txt',(err,data)=>{
     if (err) reject(err)
    resolve(data)
  })
})
p.then(function(value){
    console.log(value.toString())
},function(reason){
console.log("读取失败!!")

})

封装ajax

原生ajax请求

就直接往本站发请求了,看看拿不拿的到响应结果。

代码语言:javascript复制
    const xhr = new XMLHttpRequest()
xhr.open("GET",'https://www.jnylife.com')
xhr.send()
xhr.onreadystatechange= function (){
    if(xhr.readyState ===4){
        if(xhr.status>=200 && xhr.status<= 300)
        //成功
        console.log(xhr.response)
        else{
        console.error(xhr.status)
}
    }
}

成功拿到响应结果,如图:

promise封装ajax

代码语言:javascript复制
  const p  =  new Promise((resolve,reject)=>{

    const xhr = new XMLHttpRequest()
    xhr.open("GET",'https://www.jnylife.com')
    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()

Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

代码语言:javascript复制
getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

代码语言:javascript复制
getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function funcA(comments) {
  console.log("Resolved: ", comments);
}, function funcB(err){
  console.log("Rejected: ", err);
});

上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为Resolved,就调用funcA,如果状态变为Rejected,就调用funcB

如果采用箭头函数,上面的代码可以写得更简洁。

代码语言:javascript复制
getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("Resolved: ", comments),
  err => console.log("Rejected: ", err)
);

读取多个文件

代码语言:javascript复制
//引入 fs 模块
const fs = require("fs");


//使用 promise 实现
const p = new Promise((resolve, reject) => {
    fs.readFile("aa.txt", (err, data) => {
        resolve([data]);
    });
});

p.then(value => {

    return new Promise((resolve, reject) => {
    
        fs.readFile("aa.txt", (err, data) => {
          value.push(data)
          resolve(value)
        });
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile("aa.txt", (err, data) => {
            //压入
            value.push(data);
            resolve(value);
        });
    })
}).then(value => {
    console.log(value.toString());
});

Set

Set本质就是集合,元素唯一。

Set结构的实例有以下属性。

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。

Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。
代码语言:javascript复制
let arr = [1,2,3,4,5,4,3,2,1];
//1. 数组去重
// let result = [...new Set(arr)];
// console.log(result);
//2. 交集
let arr2 = [4,5,6,5,6];
// let result = [...new Set(arr)].filter(item => {
//     let s2 = new Set(arr2);// 4 5 6
//     if(s2.has(item)){
//         return true;
//     }else{
//         return false;
//     }
// });
// let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
// console.log(result);

//3. 并集
// let union = [...new Set([...arr, ...arr2])];
// console.log(union);

//4. 差集
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(diff);

MAP

JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

代码语言:javascript复制
var data = {};
var element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

上面代码原意是将一个DOM节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]

为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

代码语言:javascript复制
let m = new Map();

   //添加元素
   m.set('name','chenhao');
   m.set('change', function(){
       console.log("chenhao is cool");
   });
   console.log(m)
   let key = {
       school : 'ynnubs'
   };
   m.set(key, ['北京','上海','深圳']);

   //size
   // console.log(m.size);

   //删除
   // m.delete('name');

   //获取
   // console.log(m.get('change'));
   // console.log(m.get(key));

   //清空
   // m.clear();

   //遍历
   for(let v of m){
       console.log(v);
   }

   // console.log(m);

class

JavaScript语言的传统方法是通过构造函数,定义并生成新对象。下面是一个例子。

代码语言:javascript复制
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '('   this.x   ', '   this.y   ')';
};

var p = new Point(1, 2);

上面这种写法跟传统的面向对象语言(比如C 和Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

代码语言:javascript复制
   //手机
   function Phone(brand, price){
    this.brand = brand;
    this.price = price;
}

//添加方法
Phone.prototype.call = function(){
    console.log("我可以打电话!!");
}

//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call();
console.log(Huawei);

//class
class Shouji{
    //构造方法 名字不能修改
    constructor(brand, price){
        this.brand = brand;
        this.price = price;
    }

    //方法必须使用该语法, 不能使用 ES5 的对象完整形式
    call(){
        console.log("我可以打电话!!");
    }
}

let onePlus = new Shouji("1 ", 1999);

console.log(onePlus);

利用async和await结合发送ajax请求

代码语言:javascript复制
// 发送 AJAX 请求, 返回的结果是 Promise 对象
       function sendAJAX(url) {
           return new Promise((resolve, reject) => {
               //1. 创建对象
               const x = new XMLHttpRequest();

               //2. 初始化
               x.open('GET', url);

               //3. 发送
               x.send();

               //4. 事件绑定
               x.onreadystatechange = function () {
                   if (x.readyState === 4) {
                       if (x.status >= 200 && x.status < 300) {
                           //成功啦
                           resolve(x.response);
                       }else{
                           //如果失败
                           reject(x.status);
                       }
                   }
               }
           })
       }
   
       //promise then 方法测试
       // sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
       //     console.log(value);
       // }, reason=>{})
 
       // async 与 await 测试  axios
       async function main(){
           //发送 AJAX 请求
           let result = await sendAJAX("https://api.apiopen.top/getJoke");
           //再次测试
           let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=北京&appid=23941491&appsecret=TXoD5e8P')

           console.log(tianqi);
       }

       main();

0 人点赞