ES6②

简介: ES6②

生成器

基本概念

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

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

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

JAVASCRIPT

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

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

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

JS

1
2
3
4
5
6
7
8
9
setTimeout(()=>{
console.log("1s")
setTimeout(()=>{
console.log("2s")
setTimeout(()=>{
console.log("3s")
		},3000)
 },2000)
},1000)

这就是所谓的callback hell

解决方法

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
functionone(){
setTimeout(()=>{
console.log("1s")
        iterator.next()
    },1000)
}
functiontwo(){
setTimeout(()=>{
console.log("2s")
        iterator.next()
    },2000)
}
functionthree(){
setTimeout(()=>{
console.log("3s")
    },3000)
}
function *gen(){
yield one();
yield two();
yield three();
}
var  iterator =gen()
  iterator.next()

实例2

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//模拟获取  用户数据  订单数据  商品数据 
functiongetUsers(){
setTimeout(()=>{
let data = '用户数据';
//调用 next 方法, 并且将数据传入
               iterator.next(data);
           }, 1000);
       }
functiongetOrders(){
setTimeout(()=>{
let data = '订单数据';
               iterator.next(data);
           }, 1000)
       }
functiongetGoods(){
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更好的选择。

基本使用

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const p =newPromise(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)
   })

读取文件

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const fs = require('fs')
// fs.readFile('aa.txt',(err,data)=>{
//     if(err) throw err;
//     console.log(data.toString())
// })
const p =newPromise(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请求

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

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
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

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const p  =  newPromise((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

1
2
3
4
5
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});

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

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

JAVASCRIPT

1
2
3
4
5
6
7
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(functionfuncA(comments) {
console.log("Resolved: ", comments);
}, functionfuncB(err){
console.log("Rejected: ", err);
});

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

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

JAVASCRIPT

1
2
3
4
5
6
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments =>console.log("Resolved: ", comments),
err =>console.log("Rejected: ", err)
);

读取多个文件

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//引入 fs 模块
const fs = require("fs");
//使用 promise 实现
const p = newPromise((resolve, reject) => {
    fs.readFile("aa.txt", (err, data) => {
        resolve([data]);
    });
});
p.then(value => {
returnnewPromise((resolve, reject) => {
        fs.readFile("aa.txt", (err, data) => {
          value.push(data)
          resolve(value)
        });
    });
}).then(value => {
returnnewPromise((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():清除所有成员,没有返回值。

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 => !(newSet(arr2).has(item)));
console.log(diff);

MAP

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

JAVASCRIPT

1
2
3
4
5
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更合适。

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let m = newMap();
//添加元素
   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

1
2
3
4
5
6
7
8
9
10
functionPoint(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写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//手机
functionPhone(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
classShouji{
//构造方法 名字不能修改
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请求

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 发送 AJAX 请求, 返回的结果是 Promise 对象
functionsendAJAX(url) {
returnnewPromise((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
asyncfunctionmain(){
//发送 AJAX 请求
let result = await sendAJAX("https://api.apiopen.top/getJoke");
//再次测试
let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P')
console.log(tianqi);
       }
       main();
目录
相关文章
|
6月前
|
网络架构
ES6中数组新增了哪些扩展?
ES6中数组新增了哪些扩展?
75 1
|
6月前
|
编解码 JavaScript 前端开发
ES6 字符串的新增方法
ES6 字符串的新增方法
|
6月前
ES5、ES6类的定义
ES5和ES6都支持类的定义,但ES6引入了更简洁的语法。在ES5中,类是函数,方法绑定在原型上;而ES6使用`class`关键字,构造方法为`constructor`,方法直接定义在类内。ES6的类继承使用`extends`关键字,子类需调用`super`初始化父类属性。示例展示了Person类及其Student子类的定义和方法调用。
37 1
ES6新增操作字符串的七种方法
ES6新增操作字符串的七种方法
|
6月前
|
API 开发工具 网络架构
springtboot 操作es
springtboot 操作es
|
6月前
|
前端开发 JavaScript
ES6中 对象合并
ES6中 对象合并
35 0
|
11月前
|
JavaScript 前端开发
ES6学习(四)—字符串的新增方法
ES6学习(四)—字符串的新增方法
|
前端开发 JavaScript
ES6 对象合并
ES6 对象合并
175 0
|
前端开发