加载中...

ES6新特性(下)


ES6新特性(下)

  1. let和const命令
  2. es6的模板字符串
  3. 增强的函数
  4. 解构赋值
  5. 扩展的字符串、对象、数组功能
  6. Symbol
  7. Map和Set
  8. 迭代器和生成器
  9. Promise对象
  10. Proxy对象
  11. async的用法
  12. 类 class
  13. 模块化实现

参考链接:

1-8章节请看→ES6新特性(上):https://wuyea.top/posts/2174201661.html

9. Promise对象

什么是Promise

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

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

各种异步操作都可以用同样的方法进行处理,比如axios内部就是用Promise实现的。

Promise对象有以下两个特点。

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

基本用法

  1. 基础用法示例:

    // Promise接收一个函数作为参数, 默认的relove和reject分别是两个函数
    let pro = new Promise(function(resolve,reject) {
        // .....一坨代码 来模拟异步
        let res = {
            code:200,
            data:{
                name: 1,
            },
            error: "失败了"
        }
        setTimeout(() => {
            if(res.code === 200){
                resolve(res.data);
            }else{
                reject('失败',res.error);
            }
        }, 1000);
    }); 
    console.log(pro);
    // then能接收两个回调函数,一个成功的,一个失败的
    pro.then((val)=>{
        console.log(val);
    }, (err)=>{
        console.log(err);
    })
  2. 封装传参示例:

    // 对示例1中的代码进行改造。为什么改造? 因为上述代码 不能传参
    function timeout(ms) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello world')
            }, ms);
        })
    }
    timeout(1000).then((value) => {
        console.log(value);
    })
  3. Promise对象的实例:

    const getJSON = function (url) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.onreadystatechange = handler;
            xhr.responseType = 'json';
            xhr.setRequestHeader('Accept', 'application/json');
            xhr.send();  // 发送
            function handler() {
                console.log(this.readyState);
                if (this.readyState !== 4) {
                    return;
                }
                if (this.status === 200) {
                    resolve(this.response);
                } else {
                    reject(new Error(this.statusText));
                }
            }
        })
    }
    getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
        .then((json) => {
            console.log(json);
        }, function (error) {
            console.error(error);
        })

Promise对象中的相关方法

then() 方法

then() 方法的第一个参数是 resolve 的回调函数,第二个参数是可选的,是 reject 状态的回调函数。

then() 返回一个新的 Promise 实例,可以采用链式编程。

getJSON('https://www.oschina.net/action/ajax/get_tool_ad')
    .then(data => {
        console.log(data);
    }).catch(err=>{
    	console.log(err);
    })

// catch(err=>{}) 等价于 then(null, err=>{})

resolve() 方法

resolve() 方法能将现有的任何对象转换成 Promise 对象。

let p = Promise.resolve('foo');
// 等价于 new Promise(resolve=>resolve('foo'));

console.log(p);
p.then((val)=>{
    console.log(val);
})

reject() 方法

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s) // 出错了
});

all() 方法

Promiseall() 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

let Promise1 = new Promise(function (resolve, reject) {})
let Promise2 = new Promise(function (resolve, reject) {})
let Promise3 = new Promise(function (resolve, reject) {})

let p3 = Promise.all([Promise1, Promise2, Promise3])

p3.then(() => {
    // 三个都成功 则成功
}).catch(err => {
    // 只要有失败,都失败
})
// 应用场景:一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。
// 所有的都加载完后,我们再进行页面的初始化。

race() 方法

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

race() 的应用场景:比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。

//请求某个图片资源
function requestImg(imgSrc) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
            resolve(img);
        }
        img.src = imgSrc;
    });
}
//延时函数,用于给请求计时
function timeout() {
    var p = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('图片请求超时'));
        }, 3000);
    });
    return p;
}
// race([]) 中是一个列表,第一个是要请求的,第二个是计时的。
// 比如示例中请求未超过3秒请求图片的先返回成功了就进入then,否则进入catch
Promise.race([requestImg('https://img2.baidu.com/it/u=1310029438,409566289&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1735750800&t=038096a189bcdc2e2497bebb8aeda910'), timeout()]).then((data) => {
    console.log(data);
    document.body.appendChild(data);
}).catch((err) => {
    console.log(err);
});

finally()

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

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代码中,不管 promise 最后的状态,在执行完 thencatch 指定的回调函数以后,都会执行 finally 方法指定的回调函数。

10. Proxy对象

概述

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

11. async的用法

含义

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

重点概要:async函数返回一个Promise对象,可以使用then方法添加回调函数。

但函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

详细说明:async函数对 Generator 函数的改进,体现在以下四点:

  1. 内置执行器。

    Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

  2. 更好的语义。

    asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

  3. 更广的使用性。

    co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

  4. 返回值是 Promise。

    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

    进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

使用示例

/*
Promise 对象的状态变化
async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise
对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。
也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
*/
function requesetData(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onreadystatechange = handler;
        xhr.responseType = 'json';
        xhr.setRequestHeader('Accept', 'application/json');
        // 发送
        xhr.send();
        function handler() {
            if (this.readyState === 4) {
                if (this.status === 200) {
                    resolve(this.response);
                } else {
                    reject(new Error(this.statusText));
                }
            }
        }
    });
}
async function getNowWeather(url) {
    // 这里是请求一天的天气
    let response = await requesetData(url);
    // 假设这个data是请求3-7天的天气
    let data = await response.HeWeather6;
    // return是把前两次请求的数据进行了合并,返回后才会执行then函数
    return data[0].now;
}
getNowWeather(
    'https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
    .then((nowData) => {
    console.log(nowData);

})

12. 类 class

类的基本使用

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

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

class Person {
    // 实例化的时候自动调用该方法。
    // 一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {  // 类的方法,方法之间不需要写,号隔开
        return this.name;
    }
}
// 也可以通过Object.assign()方法一次性向类中添加多个方法
Object.assign(Person.prototype, {
    sayAge() {
        return this.age;
    }
    sayName() {
    	return this.name;
	}
})
// 错误
// let p1 = Person('wuye', 18)

// 正确
// 类在实例化的时候必须加上new
let p1 = new Person('wuye', 18);
console.log(p1);

类的继承

Class 可以通过 extends 关键字实现继承,让子类继承父类的属性和方法。

class Animal {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        return this.name;
    }
    sayAge() {
        return this.age;
    }
}
class Dog extends Animal{
    constructor(name, age, color) {
        super(name,age); // Animal.call(this, name, age)
        this.color = color;
    }
    // 子类自己的方法
    sayColor() {
        return `${this.name}的颜色是:${this.color}`;
    }
    // 重写父类的方法
    sayName() {
        return this.name + super.sayAge() + this.color;
    }
}
let d1 = new Dog('小黄', 8, 'yellow');
console.log(d1.sayColor());
console.log(d1.sayName());

13. 模块化实现

ES6模块功能主要由两个命令构成:exportimport

export命令用于规定模块的对外接口,import命令用于输入其它模块提供的功能。

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

下面是示例,先来看一下文件结构:

demo
  ¦-- modules
  ¦    Ⅼ- index.js
  Ⅼ- hello.html

export命令:

index.js 文件内容如下:

export const name = 'wuye';
export const age = 18;
export function sayName() {
    return 'my name is wuye';
}
// 上面的写法也可以改为如下写法:
const name = 'wuye';
const age = 18;
function sayName() {
    return 'my name is wuye';
}
export {
	name, age, sayName
}

// export default在一个js文件中只能使用一次,普通的export可以使用多次
const obj = {foo:'foo'}
export default obj;

import命令

hello.html 文件内容如下:

<html>
    <head></head>
    <body>
        <script type="module">
            // 这里的type类型一定要改为 module!!!
            // 运行的时候需要在服务器上运行才有效果。vscode可以使用插件"Live Server"
            // import {name,age,sayName} from './modules/index.js'
            // console.log(name);
            
            // 如果要导入default的,使用如下:
            // import obj,{name,age,sayName} from './modules/index.js'
            
            // 也可以全部导入
            import * as f from './modules/index.js'
            console.log(f.default) // 输出obj对象
        </script>
    </body>
</html>

文章作者: 无夜
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 无夜 !
评论
  目录