ES6学习笔记

45 阅读6分钟

前言

视频链接

😀let和const命令

一、特点

  1. let、const声明变量,没有变量提升
  2. 块级作用域
  3. 不能重复声明
  4. let声明变量,const声明常量
  5. const声明的对象,对象中的属性值可以修改
const person = {
    name: '小马哥'
}
person.name = 'alex'

二、作用

  1. var 声明变量提升经典案例
var arr = [];
var i;
for (i = 0; i< 10; i++) {
    arr[i] = function() {
        return i;
    }
}
console.log(arr[5]()); // 结果10
const arr = [];
for (let i = 0; i < 10; i++) {
    arr[i] = function() {
        return i;
    }
}
console.log(arr[5]()); // 结果5
  1. let、const不污染全局变量
let RegExp = 10;
console.log(RegExp);
console.log(window.RegExp);

三、用法建议

在默认情况下用const,而只有在你知道变量值需要被修改的情况下使用let

😁 模板字符串

使用tab键上面的反引号``,插入变量时使用${变量名}

let id = 1,
    name = '小马哥';
let htmlStr = `
        <ul>
            <li>
                <p id = ${id}>${name}</p>
            </li>
        </ul>
    `;

😂 函数

一、带参数默认值的函数

function add(a = 10, b = 20) {
    return a + b;
}
console.log(add());

二、默认的表达式也可以是一个函数

function add(a, b = getVal(5)) {
    return a + b;
}
function getVal(val) {
    return val + 5;
}
console.log(add(10)); // 结果20

三、剩余参数:由三个点...和一个紧跟着的具名参数指定 ...keys(随便起)

// es5例子:
function pick(obj) {
    let result = Object.create(null);
    for(let i = 1; i < arguments.length; i++) {
        result[arguments[i]] = obj[arguments[i]];
    }
    return result;
}
let book = {
    title: 'es6的教程',
    author: '小马哥',
    year: 2019
}
let bookData = pick(book, 'year', 'author');
console.log(bookData);
// es6例子:
function pick(obj, ...keys) {
    // ...keys 解决了arguments(伪数组)的问题,对象参数个数的不确定性
    console.log(keys); // 数组结果:["year", "author"]
    let result = Object.create(null); // 创建空的对象
    for (let i = 0; i < keys.length; i++) {
        result[keys[i]] = obj[keys[i]];
    }
    return result;
}

let book = {
    title: 'es6的教程',
    author: '小马哥',
    year: 2019
}
let bookData = pick(book, 'year', 'author');
console.log(bookData);

// 另一个例子,结果是数组的话可以用es6的方法很方便的遍历出来
function checkArgs(...args) {
    console.log(args); // 数组
    console.log(arguments); // 伪数组
}
checkArgs('a','b','c')

四、扩展运算符...

剩余运算符:把多个独立的合并到一个数组中
扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数
案例,处理数组中最大值

const maxNum = Math.max(20, 30);
console.log(maxNum);
// es5处理方法
const arr = [10, 20, 50, 30, 100, 40];
console.log(Math.max.apply(null, arr));
// es6 扩展运算符用法
const arr = [10, 20, 50, 30, 100, 40];
console.log(Max.max(...arr));

五、箭头函数

使用=>来定义
function(){}等价于()=>{}

// es5写法
let add = function(a, b) {
    return a + b; 
}
console.log(add(10, 20));
// es6写法
let add = (a, b) => {
    return a + b;
}
console.log(add(10, 20));

1.省略写法

let add = val => { // val 表示形参
    return val;
}
// 省略如下
let add = val => val;

// 如果函数体内存在方法或者表达式
let add = val => {
    return val + 5;
}
// 省略如下
let add = val => (val + 5);

console.log(add(10));

// 如果有多个参数
let add = (val1, val2) => (val1 + val2)
console.log(add(10, 20));

// 如果返回的是个对象
let getObj = id => ({
    id: id,
    name: "小马哥"
});
let obj = getObj(1);
console.log(obj)

2.无参数写法

let fn = ()=> {
    
}

3. 闭包函数

闭包函数es5写法

let fn = (function(){
    return function() {
        console.log('hello es6');
    }
})(); // fn后边加(),表示运行,得到的是一个函数
fn(); // fn得到的是一个函数,再加()表示运行得到的这个函数

闭包函数es6箭头函数写法

let fn = (()=>{
    return ()=>{
        console.log('hello es6 2');
    }
})();
fn();

4. this绑定

es5中this指向:取决于调用该函数的上下文对象

let PageHandle = {
    id: 123,
    init: function() {
        document.addEventListener('click', function(event){
            // 这儿的this发生了改变,指向了定义这个函数的上下文document,所以会报错,这么做行不通
            this.doSomeThings(event.type);
        }, false)
    },
    doSomeThings:function(type){
        console.log(`事件类型${type},当前id:${this.id}`)
    }
}
PageHandle.init();

es5中指向绑定方法bind()解决上述问题方法

let PageHandle = {
    id: 123,
    init: function() {
        document.addEventListener('click', function(event){
            this.doSomeThings(event.type);
        }.bind(this), false) // 通过bind解决this指向问题
    },
    doSomeThings:function(type){
        console.log(`事件类型${type},当前id:${this.id}`)
    }
}
PageHandle.init();

es6解决方法

  • 箭头函数没有明确的this指向,箭头函数this指向他的父级,没有父级则默认指向window
  • 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
  • 对象的{}是不产生作用域
let PageHandle = {
    id: 123,
    init: function() {
        // 箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链来确定
        document.addEventListener('click', (event)=>{
            this.doSomeThings(event.type);
        }, false)
    },
    doSomeThings:function(type){
        console.log(`事件类型${type},当前id:${this.id}`)
    }
}
PageHandle.init();

5. 箭头函数使用注意事项

  • 使用箭头函数,函数内部没有arguments
let getVal = (a, b) => {
    console.log(arguments); // 报错
    return a + b;
}
console.log(getVal(1, 3));
  • 箭头函数不能使用new关键字来实例化对象
    function 函数也是一个对象,但是箭头函数不是一个对象,它其实是一个语法糖
let Person = ()=>{

};
let p = new Person(); // 报错

六、this指向总结

在JavaScript中,this的值是在运行时基于函数的调用方式动态绑定的。简单来说,this指向的是执行当前代码的对象。

1. 在对象方法中使用this

const obj = {
    prop: 10,
        getPropValue: function() {
        return this.prop;
    }
};
console.log(obj.getPropValue()); // 输出:10

在这个例子中,this指向的是obj对象

2. 在构造函数中使用this

function MyClass(prop) {
    this.prop = prop;
}
const myInstance = new MyClass(10);
console.log(myInstance.prop); // 输出:10

在构造函数中,this指向的是新创建的对象实例

3. 在事件监听函数中使用this

<button id="myButton">Click me</button>
<script>
    document.getElementById('myButton').addEventListener('click', function() {
        console.log(this === document.getElementById('myButton')); // 输出:true
    });
</script>

在事件监听函数中,this指向的是触发事件的DOM元素

4. 在箭头函数中使用this

const obj = {
    prop: 10,
    getPropValue: () => {
        return this.prop;
    }
};
console.log(obj.getPropValue()); // 输出:undefined 或者 window.prop 的值

在箭头函数中,this的值是继承的,它不会从对象中获取,所以this.prop没有指向obj对象,而是指向了外层上下文中的this

this的值在函数执行时被确定,不会在函数被调用的时候改变,但是在箭头函数中是例外,箭头函数中的this是根据其定义时所在的上下文确定的,而不是执行时

5. 使用callapplybind方法可以显式地改变this的值

const obj = {
    prop: 10
};
function logProp() {
    console.log(this.prop);
}
logProp.call(obj); // 输出:10

在这个例子中,使用call方法将logProp函数中的this绑定到了obj对象

6. 在回调函数中使用this时要格外小心,因为this的值可能不是你预期的

const obj = {
    prop: 10,
    doSomething: function() {
        setTimeout(function() {
              console.log(this.prop); // 输出:undefined,因为这里的this不是obj
        }, 100);
    }
};
obj.doSomething();

为了在回调函数中使用this指向正确的对象,通常的解决方案是在外部保存this的引用,或者使用箭头函数自动绑定外层上下文的this

const obj = {
    prop: 10,
    doSomething: function() {
        const that = this; // 在外部保存this的引用
        setTimeout(function() {
            console.log(that.prop); // 输出:10
        }, 100);
    }
};
obj.doSomething();
 
// 或者使用箭头函数
const obj = {
    prop: 10,
    doSomething: function() {
        setTimeout(() => {
            console.log(this.prop); // 输出:10
        }, 100);
    }
};

🤣 解构赋值

  • 解构赋值是对赋值运算符的一种扩展,它是针对数组和对象来进行操作
  • 优点:代码书写上简洁易读

一、对象解构赋值

let node = {
    type: 'iden',
    name: 'foo'
}
// es5写法
let type = node.type;
let name = node.name;

// es6解构赋值写法
// 这种写法叫做完全解构
let {type, name} = node;
console.log(type, name);
let obj = {
    a: {
        name: "张三"
    },
    b: [],
    c: 'hello world'
}
// 这种写法叫做不完全解构
let {a} = obj;
console.log(a); //obj.a

// 别名写法
let {a:t} = obj;
console.log(t); //obj.a

// 剩余运算符写法
let {a, ...res} = obj;
console.log(res); // 返回的是包含b、c的对象,不是数组
// 默认值写法
let {a, b = 30} = {a:20}

二、数组解构赋值

let arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(a, b, c);

可嵌套解构

let [a, [b], c] = [1, [2], 3];

😃 对象的扩展功能

es6直接写入变量和函数,作为对象的属性和方法

  • es5写法
const name = '小马哥',
    age = 20;
const person = {
    name: name,
    age: age,
    sayName: function() {
        console.log(this.name)
    }
}
person.sayName();
  • es6写法,属性方法同名省略
const name = '小马哥',
    age = 20;
const person = {
    name,
    age,
    sayName() {
        console.log(this.name)
    }
}
person.sayName();
function fn(x,y) {
    return {x,y};
}
console.log(fn(10, 20)); // {x:10, y:20}
let cart = {
    wheel: 4,
    set(newVal) {
        if(newVal < this.wheel) {
            throw new Error('轮子数太少了');
        }
        this.wheel = newVal;
    },
    get() {
        return this.wheel;
    }
}
console.log(cart.get()); // 4
cart.set(6);
console.log(cart.get()); // 6
  • 属性和方法表达式组合
const obj = {};
obj.isShow = true;
const name = 'a';
obj[name + 'bc'] = 123;
console.log(obj); // {isShow: true, abc: 123}

obj['f' + 'bc'] = function() {
    console.log(this);
}
console.log(obj); // {isShow: true, abc: 123, fbc: f()}
const name = 'a';
const obj = {
    isShow: true,
    [name + 'bc']: 123,
    ['f' + name]() {
        console.log(this);
    }
}
  • 对象的方法is()assign()
    is()等同于===,既比较值也比较类型,但是===有一个缺陷,在比较NaN的时候返回false
console.log(NaN === Nan); // false
console.log(Object.is(NaN, NaN)); true

Object.assign(target, obj1, obj2...)用于对象的合并, 将所有的对象合并到target中 ,浅拷贝

let newObj = Object.assign({}, {a:1}, {b:2});
console.log(newObj); // {a:1, b:2}

😄 数据类型Symbol(了解)

Symbol表示独一无二的值
最大用途:用来定义对象的私有变量

let s1 = Symbol('s1');
console.log(s1); // Symbol('s1')
let obj = {
    [s1]: '小马哥'
};
// obj[s1] = '小马哥';

// 如果用Symbol定义的对象中的变量,取值时一定要用[变量名]取值
console.log(obj[s1]); // '小马哥'

// 获取Symbol声明的属性名(作为对象的key)
let s = Object.getOwnPropertySymbols(obj);
console.log(s); // [Symbol(s1)]
console.log(s[0]); // Symbol(s1)

// 另一种方法,反射
let m = Reflect.ownKeys(obj);
console.log(m); // [Symbol(s1)]

😅 Map和Set

一、Set

集合,表示无重复值的有序列表

let set = new Set();
set.add(2);
set.add('4');
set.add('4');
set.add(['hello','world',3]);

// 删除元素
set.delete(2);

// 校验某个值是否在set中
console.log(set.has('4'));

// set的长度
console.log(set.size);

// 遍历,set中值即是键也是值,键值相等
set.forEach((val, key)=>{
    console.log(val);
    console.log(key);
})

// 将set转换成数组
let set2 = new Set([1,2,3,3,3,4]);
// 使用扩展运算符
let arr = [...set2];
console.log(arr);


// set中对象的引用无法被释放
let set3 = new Set(), obj = {};
set3.add(obj);
// 释放当前资源
obj = null;
console.log(set3); // 依然存在对象obj,无变化

//使用WeakSet
let set4 = new WeakSet(), obj = {};
set4.add(obj);
obj = null;
console.log(set4);

WeakSet

  1. 不能传入非对象类型的参数
  2. 不可迭代
  3. 没有forEach()方法
  4. 没有size属性

二、Map

Map类型是键值对有序的列表,键和值是任意类型

let map = new Map();
map.set('name', '张三');
map.set('age', 20);
console.log(map.get('name')); // 张三
map.has('name');
map.delete('name');
map.clear(); // 清空
console.log(map);

map.set(['a', [1,2,3]], 'hello');
console.log(map); // {Array(2) => "hello"}

// 直接初始化
let m = new Map([
    ['a', 1], 
    ['c', 2]
]);
console.log(m); // {"a"=>1, "c"=>2}

// Map和Set差不多,也有WeakMap方法

😆 数组的扩展方法

一、数组的方法

1. from()方法

from()将伪数组转换成真正的数组,比如arguments对象就是伪数组

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
<script>
    function add() {
        console.log(arguments); // 伪数组,无数组方法
        // es5转换
        let arr = [].slice.call(arguments);
        console.log(arr);

        // es6写法
        let arr1 = Array.from(arguments);
        console.log(arr1);
    }
    add(1,2,3);
    
    // 主要作用实例
    let lis = document.querySelectorArr('li');
    console.log(Array.from(lis)); // 转换成真数组
    // 之前学过的扩展运算符...也可以将伪数组转为数组
    console.log([...lis]);
    
    // from() 还可以接受第二个参数,用来对每个元素进行处理
    let liContents = Array.from(lis, ele => ele.textContent);
    console.log(liContents); // ["1", "2", "3", "4"]
</script>

2. of()方法

of()将任意的数据类型,转换成数组

console.log(Array.of(3, 11, 20, [1, 2, 3], {id:1})); // [3, 11, 20, Array(3), {...}]

3. copywithin()方法

copywithin(开始替换位, 开始读取复制位, 结束读取复制位) 数组内部将指定位置的元素复制到其他位置

// 将索引3以及之后的值,从索引0的位置开始替换掉原来的值
let arr = [1,2,3,8,9,10].copywithin(0,3);
console.log(arr); // [8,9,10,8,9,10]

4. find()findIndex()方法

find()找出第一个符合条件的数组成员 find()找出第一个符合条件的数组成员的索引

let num = [1, 2, -10, -20, 9, 2].find((n)=>{
    return n < 0;
})

// 简写
// [1, 2, -10, -20, 9, 2].find(n=>n<0);
console.log(num); // -10

5. entries()keys()values()方法

entries()keys()values()返回一个遍历器,可以使用for...of循环进行遍历

entries()对键值对遍历
keys()对键名遍历
values()对键值遍历

for(let index of ['a', 'b'].keys()){
    console.log(index); // 0 | 1
}

for(let ele of ['a', 'b'].values()){
    console.log(ele); // a | b
}

for(let [index, value] of ['a', 'b'].entries()){
    console.log(index, value); // 0 a | 1 b
}

6. includes()方法

返回一个布尔值,表示某个数组是否包含给定的值

console.log([1,2,3].includes(2)); // true

😉 迭代器Interator的用法

是一种新的遍历机制,两个核心

  • 第一,迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器,通过迭代器的next()方法获取迭代之后的结果
  • 第二、迭代器是用于遍历数据结构的指针(数据库的游标)
// 使用迭代
const items = ['one', 'two', 'three'];
// 创建新的迭代器
const ite = items[Symbol.iterator]();
console.log(ite.next()); //{value: "one", done: false} done如果为false表示遍历继续,如果为true表示遍历完成
console.log(ite.next());
console.log(ite.next());
console.log(ite.next());

😊 生成器Generator的用法

generator函数,可以通yield关键字,将函数挂起,为了改变执行流提供了可能,同时为了做异步编程提供了方案

一、它和普通函数的区别

  • function后面 函数名之前有个*
  • 只能在函数内部使用yield表达式,让函数挂起
// function *func(params) {}// *也可以放在这个位置
function* func() {
    yield 2;
    yield 3;
}
// 返回一个遍历器对象(和迭代器还不一样),可以调用next()方法
let fn = func();
console.log(fn.next()); // {value: 2, done: false}
console.log(fn.next()); // {value: 3, done: false}
console.log(fn.next()); // {value: undefined, done: true}

总结:generator函数是分段执行的,yield语句是暂停执行,而next()是恢复执行

二、用next()进行传值(抽象得很)

next的传参每次只传给上一个yield值

function* add() {
    console.log('start');
    // x 不是yield '2'的返回值,它是恢复当前yield继续执行的next()调用时传入的实参
    let x = yield '2';
    console.log('one:' + x);
    let y = yield '3';
    console.log('two:' + y);
    return x + y;
}
const fn = add();
console.log(fn.next()); // {value: '2', done: false}
console.log(fn.next(20)); // {value: '3', done: false}
console.log(fn.next(30)); // {value: 50, done: true}

// 如果函数没有返回值,则可以手动调用return,一般不会这么用
// console.log(fn.return(100));

三、使用场景:为不具备Interator接口(数组有)的对象提供了遍历操作

function* objectEntries(obj) {
    // 获取对象的所有的key,保存到数组中[name, age]
    const propKeys = Object.keys(obj);
    for(const propkey of propKeys) {
        yield [propkey, obj[propkey]]
    }
}
const obj = {
    name: '小马哥',
    age: 18
}
obj[Symbol.iterator] = objectEntries;

// [key, value] 相当于调用了next
for(let [key, value] of objectEntries(obj)) {
    console.log(`${key}: ${value}`);
}

四、Generator的用法

1. 实现异步请求同步执行,或者解决回调地狱问题,例如ajax请求,

Generator 部署ajax操作,让异步代码同步化

function* main() {
    let res = yield request('https://fress-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976');
    console.log(res);
    console.log('数据请求完成,可以继续操作');
}

const ite = main();
ite.next();

function request(url) {
    $.ajax({
        url,
        method: 'get',
        success(res) {
            ite.next(res);
        } 
    })
}

2. 加载页面

加载页面的业务逻辑:
① 加载loading...页面
② 请求数据并加载完成...(需要异步操作)
③ loading关闭掉

function* load() {
    loadUI();
    yield showData();
    hideUI();
}

let itLoad = load();
itLoad.next();

function loadUI() {
    console.log('加载loading...页面');
}

function showData() {
    // 模拟异步请求
    setTimeout(()=>{
        console.log('数据加载完成');
        itLoad.next();
    }, 1000);
}

function hideUI() {
    console.log('隐藏loading...页面');
}

// 如果依次执行三个方法,则会showData最后执行,业务逻辑行不通

😋 Promise对象

一、基本用法

相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
各种异步操作都可以用同样的方法进行处理

特点:
① 对象的状态不受外界影响,处理异步操作三个状态(pending-进行中、Resolved/Fulfilled-成功、Rejected-失败)
② 一旦状态改变,就不会再变,任何时候都可以得到这个结果

let pro = new Promise(function(resolved, rejected) {
    // 执行一步操作
    
    let res = {
        code: 200,
        data: {
            name: '小马哥'
        }
    }
    // 模拟异步
    setTimeout(()=>{
        if(res.code === 200) {
            // 返回正常结果
            resolved(res.data);
        } else {
            res.error = "失败";
            rejected(res.error);
        }
    }, 1000);
})
console.log(pro)
// 接受成功回调的结果,接受一个回调函数
pro.then((val)=>{
    console.log(val); // res.data
},(err)=>{
    console.log(val); // res.error
});

这种方式无法向Promise对象中传递参数,需要封装一下

function timeOut(ms) {
    return new Promise((resolved, rejected)=>{
        setTimeout(()=>{
            resolved('hello promise success!!');
        }, ms);
    })
}
timeOut(2000).then((val)=>{
    console.log(val);
})

二、使用Promise封装ajax

原生js封装ajax请求

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 hanlder() {
            console.log(this.readyState); // 这个this是xhr,readyState三个值2,3,4
            if(this.readyState === 4) {
                if(this.status === 200) {
                    resolve(this.response);
                } else {
                    reject(new Error(this.statusText));
                }
            }
        }
    })
}
getJSON('https://baidu.com')
    .then((data) => {
    
    }, (error) => {
        conosole.log(error);
    })
let axios = {
    get(url, data) {
        return new Promise(function (resolve, reject) {
            let xhr = new XMLHttpRequest();
            let query = '';
            let flag = typeof data === 'object' && !Array.isArray(data)
            let keys = Object.keys(data);
            if (flag && keys.length > 0) {
                query = '?'
                keys.forEach((key) => {
                    query += `${key}=${data[key]}&`;
                })
                query = query.slice(0, query.length - 1);
            }
            xhr.open('get', url + query, true);
            xhr.send(null)
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) { // 成功完成
                    if (xhr.status === 200) {
                        resolve(xhr.responseText)
                    } else {
                        reject(xhr.status)
                    }
                } else {
                    // HTTP请求还在继续...
                }
            }
        })
    },
    post(url, data) {
        return new Promise(function (resolve, reject) {
            let xhr = new XMLHttpRequest();
            let query = null;
            let flag = typeof data === 'object' && !Array.isArray(data)
            let keys = Object.keys(data);
            if (flag && keys.length > 0) {
                query = '';
                keys.forEach((key) => {
                    query += `${key}=${data[key]}&`;
                })
                query = query.slice(0, query.length - 1);
            }
            xhr.open('post', url, true);
            xhr.setRequestHeader('content-Type', 'application/x-www-form-urlencoded')
            xhr.send(query)
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) { // 成功完成
                    if (xhr.status === 200) {
                        resolve(xhr.responseText)
                    } else {
                        reject(xhr.status)
                    }
                } else {
                    // HTTP请求还在继续...
                }
            }
        })
    }
}
axios.get('http://localhost:3000/get', { name: 1, age: 2 }).then(res => {
    console.log(res);
})
axios.post('http://localhost:3000/post', { name: 1, age: 2 }).then(res => {
    console.log(res);
})

三、Promise中的方法

1. then() 方法

  • then() 第一个参数resolve回调函数,第二个参数是可选的,是reject状态回调函数
  • then()返回的是一个新的promise实例,可以采用链式编程
getJSON(url)
    .then(data => {
    
    }).then(null, err => { // 这个then相当于第一个then返回的promise对象,null相当于resolve
    
    });
    
// 等价于如下写法(常规写法)
getJSON(url)
    .then(data => {
    
    }).catch(err => {
    
    });

2. resolve() 方法

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

let p = Promise.resolve('foo');
// 等价于
// let p = new Promise(resolve=>resolve('foo'));
p.then(data => {
    console.log(data); // foo
})

3. reject()方法

4. all()方法

应用场景:一些游戏类的素材比较多,等待图片、flash、静态资源文件,都加载完成才进行页面的初始化

let promise1 = new Promise((resolve, reject)=>{});
let promise2 = new Promise((resolve, reject)=>{});
let promise3 = new Promise((resolve, reject)=>{});

let p4 = Promise.all([promise1, promise2, promise3]);

p4.then(()=>{
    // 三个都成功 才成功
}).catch(err=>{
    // 如果有一个失败 则失败,返回第一个失败状态的值
})

5. race方法

给某个异步请求设置超时时间,并且在超时后执行相应的操作

function requestImg(imgSrc) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function() {
            resolve(img);
        }
        img.src = imgSrc;
    })
}

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            reject(new Error('图片请求超时'));
        }, 3000)
    })
}

Promise.race([requestImg('images/2.png'), timeout()]).then(data => {
    console.log(data);
    document.body.appendChild(data);
}).catch(err => {
    console.log(err);
});

6. done()finally()方法

😎 async的用法

作用:使得异步操作更加方便
基本操作: async它会返回一个Promise对象
asyncGenerator的一个语法糖

async function f() {
    // return await 'hello async'; // 会自动将返回结果转为Promise对象
    let s = await 'hello world'; // 这儿模拟相当于一个异步请求,等待返回结果
    let data = await s.split(''); // 这儿模拟的另一个异步请求,等待返回结果
    return data;
}
// 如果async函数中有多个await,那么then函数会等待所有的await指令,运
// 行完的结果才去执行
f().then(v=>console.log(v)).catch(e=>console.log(e));

async function f2() {
    // throw new Error('出错啦');
    try {
    
    } catch(error) {
    
    }
    return await Promise.resolve('hello');
}
f2().then(v=>console.log(v)).catch(e=>console.log(e));
//  需求: 想获取天气now的数据
async function getNowWeather(url) {
    // 发送 ajax 获取实况天气
    let res = await getJSON(url);
    console.log(res);
    // 获取HeWeather6的数据 获取未来3~7天的天气状况
    let arr = await res.HeWeather6; // 这儿的await是为了让then得到所有执行后的返回的结果
    return arr[0].now;
}

getNowWeather('https://fress-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
    .then(now => {
        console.log(now);
    })

😍 class

一、创建方式

  • es5 创建类的方式
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    return this.name;
}

let p1 = new Person('小马哥', 28);
console.log(p1);
  • es6 创建类的方式
class Person {
    // 原型方法,实例化的时候会立即被调用
    constuctor(name, age) {
        this.name = name;
        this.age = age;
    }
    // 方法
    sayName() {
        return this.name;
    }
    sayAge() {
        return this.age;
    }
    // 另一种添加方法的方式
    /**
    Object.assign(Person.prototype, {
        sayName() {
            return this.name;
        },
        sayAge() {
            return this.age;
        }
    })
    **/
}

let p1 = Person('小马哥', 28);
console.log(p1);

二、类的继承

使用关键字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.age}岁了,它的颜色是${this.color}`;
    }
    // 重写父类方法
    sayName() {
        return this.name + super.sayAge() + this.color;
    }
}
let d1 = new Dog('小黄', 28, 'red');
console.log(d1.sayColor);
console.log(d1.sayName);

😘 module模块的使用

es6模块功能主要由两个命令构成:exportimport
export用于规定模块的对外接口
import用于引入其他模块提供的功能
一个模块就是一个独立的文件

注意:一定要用服务器才可以运行导入的模块,静态文件访问不到模块

一、 基本使用方法

modules/index.js

export const name = '张三';
export const age = 18;
// 抛出函数的时候只能这种形式,不能定义函数后另起一行export sayName;
// 要抛出一个函数作为一个整体,一定是在函数前面加export关键字
// 不过可以用对象把函数包裹起来抛出,export {sayName};属性也可以这样
export function sayName() {
    return 'my name is ' + name;
}

index.html引入模块,script标签类型必须是module

<script type='module'>
    // 变量用 {} 是相当于解构赋值,还不算是解构 
    import {name, age, sayName} from './modules/index.js';
    console.log(name, age, sayName());
</script>

二、命名default

default 一个文件中只能用一次,在外部文件引入的时候可以随便取别名,对应的就是这个命名

index.html

<script type='module'>
    // cname 是 default对象
    // 一般使用这种方式
    /**
    import cname, {name, age, sayName} from './modules/index.js';
    console.log(cname, name, age, sayName());
    **/
    
    // * 全部导入
    import * as f from './modules/index.js';
    console.log(f);
    // default对象使用方法
    console.log(f.default)
    
    // 导出类可以直接
    // import cname, {name, age, sayName} from './modules/index.js';
    const p = new Person();
    p.sayAge;
</script>

modules/index.js

const name = '张三';
const age = 18;
function sayName() {
    return 'my name is ' + name;
}
export {
    name, age, sayName
}

// 对象
/*
const obj = {
    foo: 'foo'
}
export default obj;
*/

// 类
class Person{
    constructor() {
        
    }
    sayAge() {
        console.log('16');
    }

}

export default Person;