ES6开发必备基本用法

目录

1. ES6 简介

2. let 和 const

3. 模板字符串

4. 函数默认值和剩余参数

5. 扩展运算符和箭头函数

6. 箭头函数的 this 指向问题

7. 解构赋值

8. 对象的扩展功能

9. Symbol类型

10. Map 和 Set 方法

11. 数组的扩展方法

12. Iterator(遍历器) 

13. Generator 函数(生成器)

 14. Promise 对象

15. async 异步操作

16. class 类的用法

17. ES6 module(模块化)


参考文献:1、ES6常用API详讲 - 掘金

                  2、ES6 入门教程  

1. ES6 简介

  • ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言
  • ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”
  • 90%的浏览器都支持ES6,也可以通过 babel 编译器(一个 js 编译器)将 ES5 代码 转为 ES6 代码

2. let 和 const

  • let 声明变量没有变量提升,var 有变量提升
  • let 是一个块作用域,let声明的变量只在它所在的代码块有效。
  • let 不可以重复声明 ,var 可以重复
  • const 声明常量,一旦声明就不能被修改,除此之外也有 let 的三个特性。

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

// 例 1
// 底层看到 var ,会自动在前面声明变量,即变量提升,而 let 不会
// 如下代码,会在前面 自动添加 var a; 而不会自动添加 let b;
let a = 10;
var b = 1;
console.log(a)  // ReferenceError: a is not defined.   报错
console.log(b)  // 1



// 例 2
var a = [];
// 由于变量提升,会在这里自动声明 var = i;
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6]();         //  10

// let 是一个块作用域,let声明的变量只在它所在的代码块有效。
for (let i = 0; i < 10; i++) {
  // ...
}
console.log(i);  // ReferenceError: i is not defined

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6]();          // 6


// 例 3
// var 的重复声明
var c = 1;
var c = 3;
console.log(c);    //4 

//let 不可以重复声明
let d = 1;
let d = 3;
console.log(d);    //报错



// 例 4
//const 声明常量,一旦声明就不能被修改
const max = 30;
max = 40;
console.log(max);   //会报错

3. 模板字符串

  • 模板字符串:使用 tab 键的反引号 `
  • 插入变量时:使用 ${ 变量名 }
const oBox = document.querySelector('.box')
let id = 1, name = '牛肉粉';
// ES6 写法
let htmlStr = `<ul>
			        <li>
				        <p id = ${id} > ${name} </p>
			        </li>
		       </ul>`
oBox.innerHTML = htmlStr;

// ES6 以前的写法
// oBox.innerHTML = "<ul><li><p id =" + id + ">" + name + "</p></li></ul>"

4. 函数默认值和剩余参数

  • 函数默认值
// 例 1
// ES5 的写法
function add(a, b){
    a = a || 10;   //设置默认值
    b = b || 20;
    return a + b;
}
console.log(add());    // 30

// ES6 的写法
function add(a = 10, b = 20){    //设置默认值
    return a + b;
}
console.log(add());    // 30


// 例 2.默认的值也可以是函数
function add(a, b = getVal(5)) {
    return a + b;
}

function getVal(val) {
    return val + 5;
}

console.log(add(10));   // 20
  • 剩余参数
  • 由...和一个紧跟着的具名参数组成 : ...keys
  • ...keys 解决了 arguments 的问题 
function checkArgs(...args){
    console.log(args);         // 返回一个真实的实参数组["a", "b", "c"]
    console.log(arguments);    // 返回一个实参伪数组
}

checkArgs('a','b','c');

5. 扩展运算符和箭头函数

  • 扩展运算符

剩余参数与扩展运算符的区别:

  • 剩余参数:把多个独立的合并到一个数组中
  • 扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数
// Math.max 获取最大值 
const arr = [10, 20, 50, 30, 90, 100, 40];
// 扩展运算符更方便
console.log(Math.max(...arr))               //  100
  • ES6 的箭头函数
  • 使用 =>  来定义 , function(){}  等价于  () => {}   ,代码变得更加简洁
  • 箭头函数内部没有 arguments
  • 箭头函数不能使用 new 关键字来实例化对象,function 函数也是一个对象,但是箭头函数不是
let add = function(a, b){
    return a + b;
}

// 上下两者写法等价

let add = (a, b) => {
    return a + b;
}

var getVal = (a,b) => {
    console.log(arguments);
    return a + b;
}
console.log(getVal(1,2));       //arguments is not defined

// 箭头函数不能使用 new 关键字来实例化对象,function 函数也是一个对象,但是箭头函数不是
let Person = () => {

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

6. 箭头函数的 this 指向问题

  • 箭头函数没有this的指向,箭头函数内部的this值只能通过查找作用域链来确定
  • 如果箭头函数被一个非箭头函数所包括,那么this的值与该函数的所属对象相等,否则 则是全局的window对象
let PageHandler = {
    id:123,
    init:function(){
        document.addEventListener('click',function(event) {
            this.doSomeThings(event.type);         // 这里的 this 指向 document
        },false);
    },
    doSomeThings:function(type){
        console.log(`事件类型:${type},当前id:${this.id}`);
    }
}
PageHandler.init();

//解决this指向问题
let PageHandler = {
    id: 123,
    init: function () {
        // 使用bind来改变内部函数this的指向
        document.addEventListener('click', function (event) {
            this.doSomeThings(event.type);         // 这里的 this 指向 PageHandler 
        }.bind(this), false);
    },
    doSomeThings: function (type) {
        console.log(`事件类型:${type},当前id:${this.id}`);
    }
}
PageHandler.init();

let PageHandler = {
    id: 123,
    init: function () {
        // 箭头函数没有this的指向,箭头函数内部的this值只能通过查找作用域链来确定

        // 如果箭头函数被一个非箭头函数所包括,那么this的值与该函数的所属对象相等,否则 则是全局的window对象
        document.addEventListener('click', (event) => {
            console.log(this);
            this.doSomeThings(event.type);
        }, false);
    },
    doSomeThings: function (type) {
        console.log(`事件类型:${type},当前id:${this.id}`);
    }
}
PageHandler.init();

7. 解构赋值

  • 解构赋值是对赋值运算符的一种扩展。它通常针对数组和对象进行操作。
  • 优点:代码书写简洁且易读性高
// 例 1 对对象的解构
// 完全解构
let node = {
    type:'iden',
    name:'foo'
}

let {type,name} = node;
console.log(type,name)            //iden foo


// 不完全解构
let obj = {
    a:{
        name:'张三'
    },
    b:[],
    c:'hello world'
}
//可忽略 忽略b,c属性
let {a} = obj;   
//剩余运算符 使用此法将其它属性展开到一个对象中存储
let {a,...res} = obj;
console.log(a,res);

// 默认值 a 的值 20 ,b 的值 30
let {a,b = 30} = {a:20};  



// 例 2 对数组的解构
let [a, b] = [1, 2, 3];
console.log(a,b) 
//可嵌套
let [a, [b], c] = [1, [2], 3];

8. 对象的扩展功能

 对象的方法:

1.   Object.is()

  • is()  和 ===
  • 比较两个值是否严格相等 

ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

2.   Object.assign()

Object.assign(target, obj1, obj2...)方法用于对象的合并,将原对象(obj)的所有属性,复制到目标对象(target)

// 例 1
const name = '张三';
const age = 19;
const person = {
    name, //等同于name:name
    age,
    // 方法也可以简写
    sayName() {
        console.log(this.name);
    }
}
person.sayName();


// 例 2
function getPoint() {
  const x = 1;
  const y = 10;
  return {x, y};
}

getPoint()
// {x:1, y:10}


// 对象的方法:
// is()  和 ===
// 比较两个值是否严格相等 
console.log(NaN === NaN);               // false  ,===有缺陷
console.log(Obejct.is(NaN, NaN));       // true 

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

9. Symbol类型

  • 它表示的是独一无二的值
  • 最大的用途:用来定义对象的私有变量
  • 如果用 Symbol 定义的是对象中的变量,取值时一定要用 [变量名] 
  • 如果用 Symbol 定义的是对象中的变量,该变量不能作为key,无法用 for 循环遍历

注意: 这个数据类型实际开发中用的不多

const name = Symbol('name');
const name2 = Symbol('name');
console.log(name === name2);          // false

// 用来定义对象的私有变量
let s1 = Symbol('s1');
console.log(s1);
let obj = {};
obj[s1] = '牛肉粉';       // 等价于 obj = {  [s1]: '牛肉粉'   }    
//如果用 Symbol 定义的是对象中的变量,取值时一定要用 [变量名] 
console.log(obj[s1]);
console.log(obj.s1);     // 会报错

如果用 Symbol 定义的是对象中的变量,该变量不能作为key,无法用 for 循环遍历
for(let key in obj){
    console.log(key);    // 没有输出
} 
console.log(Object.keys(obj));    // 输出一个空数组 []

10. Map 和 Set 方法

Set 集合:表示无重回复值的有序列表

let set = new Set();
// 添加元素 add()
set.add(2);
set.add('4');
set.add('4');         // 这个4会被忽略,因为集合表示无重回复值的有序列表
console.log(set);     // set(2) {2, "4"}
// 也可以添加数组
set.add([1, 2, 3]);  


// 删除元素 delete()
set.delete(2);
console.log(set);
 
// 校验某个值是否在 set 中  has()
console.log(set.has('4'));    // 返回 true

// 访问集合的长度
console.log(set.size);

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

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

let map = new Map();
// set() 设置值
map.set('name', '张三');
map.set('age', '20');
console.log(map);           // 输出 { 'name' => '张三', 'age' => '20'}
// 键和值可以是任意类型
map.set(['a', [1, 2, 3]], 'hello');

// get() 获取值
console.log(map.get('name'));     //张三

// has()  校验某个值是否在 map 中
console.log(map.has('name'));     //true

// delete()  删除值
map.delete('name');

// clear()  清除所有值
map.clear();

11. 数组的扩展方法

Array.from()方法用于将伪数组转为真正的数组

// Array.from()方法用于将伪数组转为真正的数组
function add() {
// ES5 的写法,不宜阅读
var arr1 = [].slice.call(argumets); // [1, 2, 3]
console.log(arr1);
// ES6 的写法
let arr2 = Array.from(argumets); // [1, 2, 3]
console.log(arr2);
}
add(1, 2, 3);


// 方法二:使用扩展运算符, 将伪数组转为真正的数组
// querySelectorAll()方法返回的是一个类似数组的对象
let lis = document.querySelectorAll('li');   
console.log([...lis]);

// Array.from()方法还可以接受第二个参数,对每个元素进行处理
let lis = document.querySelectorAll('li'); 
let names2 = Array.from(lis, s => s.textContent);
console.log(names2);

Array.of()方法用于将一组值,转换为数组

// Array.of()方法用于将一组值,转换为数组
console.log(Array.of(3, 11, 20, [1, 2, 3], {id: 1}));

copyWithin(target,  start = 0,  end = this.length);   在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

这三个参数都应该是数值,如果不是,会自动转为数值。

// 表示将从 3 号位直到数组结束的成员(8,9,10),复制到从 0 号位开始的位置,结果覆盖了原来的 1, 2, 3。
[1, 2, 3, 8, 9, 10].copyWithin(0, 3);     // [8, 9, 10, 8, 9, 10];

find() 方法:找出第一个符合条件的数组成员,它的参数是一个回调函数。

findIndex() 方法:找出第一个符合条件的数组成员的索引

let num = [1, 4, -5, 10, -4].find((n) => n < 0);    
console.log(num);            // -5

let numIdex = [1, 4, -5, 10, -4].findIndex((n) => n < 0);    
console.log(numIdex);        // 2

ES6 提供三个新的方法——entries()keys()values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是:

keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

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

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

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

// 如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

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

注意:以前的 indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,返回值是1或-1,所以要去比较是否不等于-1,表达起来不够直观

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

12. Iterator(遍历器) 

  • 遍历器(Iterator)它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员),快捷的访问数据。
  • Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
  • Iterator 的遍历过程是这样的。
  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
  5. 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
  • 原生具备 Iterator 接口的数据结构如下。
  1.  Array
  2. Map
  3. Set
  4. String
  5. TypedArray
  6. 函数的 arguments 对象
  7. NodeList 对象 
let items = ['one', 'two', 'three'];
//创建一个遍历器
let ite = items[Symbol.iterator]();
console.log(ite.next()) // { value: 'one', done: false }
console.log(ite.next()) // { value: 'two', done: false }
console.log(ite.next()) // { value: 'three', done: false }
console.log(ite.next()) // { value: undefined, done: true }

13. Generator 函数(生成器)

  • 上一章说过,Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回一个遍历器对象。而 Generator 函数也是遍历器生成函数,可以把 Generator 函数赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口,给不具备 Iterator 接口的数据结构(不自带Symbol.iterator方法)提供遍历操作。
  • 形式上,Generator 函数是一个普通函数,但是有两个特征(区别)。一是,function关键字与函数名之间有一个星号 ;二是,函数体内部使用yield表达式
  • Generator 函数是分段执行的,只有调用next方法才会遍历下一个内部状态。yield是暂停执行标志,next()是恢复执行。当 next() 传入参数时,该参数就会被当作上一个 yield 表达式的返回值
// 生成器函数:function关键字与函数名之间有一个星号 * ;函数体内部使用yield表达式
function* add() {
    console.log('start');
    // 这里的 x 不是 yield '2' 的返回值,它是下一个 next()调用恢复执行时传入的参数值
    let x = yield '2';                 // yield 暂停执行标志
    console.log('one:' + x);
    let y = yield '3';                 // yield 暂停执行标志
    console.log('two:' + y);
    return x + y;
}
// 返回一个遍历器对象
let fn = add()
// next() 恢复执行标志
console.log(fn.next());      // start      {value: '2', done: false}
// 当 next() 传入参数时,该参数就会被当作上一个 yield 表达式的返回值 ,即 x = 20, y = 30
console.log(fn.next(20));    // one:20     {value: '3', done: false}
console.log(fn.next(30));    // two:30     {value: '50', done: true}
  • 使用场景:为不具备 Iterator 接口的对象提供了遍历操作
  • 利用for...of循环,可以写出遍历任意对象(object)的方法。原生的 JavaScript 对象没有遍历接口(不具备 Symbol.iterator方法),无法使用for...of循环,通过 Generator 函数为它加上这个接口,即将 Generator 函数加到对象的Symbol.iterator属性上面,就可以用了。
//  Generator 生成器函数
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
}
// 把 Generator 生成器函数赋值给对象的Symbol.iterator属性, 为该对象加上遍历器接口
obj[Symbol.iterator] = objectEntries;
console.log(obj);
// objectEntries(obj) 等价于 obj[Symbol.iterator](obj) 
for(let [key, value] of objectEntries(obj)){ 
    console.log(`${key}: $value`);
}
  • Generator 函数的异步应用

Generator 函数在ajax请求的异步应用,让异步代码同步化

function* main(){
    let res = yield request('https://free-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);
        }
    })
}

 Generator 函数在加载页面的异步应用

// 1.加载 Loading...页面
// 2.数据加载完成...(异步操作)
// 3.Loading 关闭掉

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

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

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

loadUI();
showData();
hideUI();

分析上图:我们的实际需求是加载数据完成后才关闭页面,但由于三个函数函数是同步执行的,数据加载需要时间,在数据未加载完成时,就已经隐藏loading页面。通过 Generator 函数可以解决这个问题

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

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

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

function showData(){
    // 模拟数据加载异步操作
    setTimeout(() => {
        console.log('数据加载完成');
        // 当数据返回后才执行下一步操作
        itLoad.next();
    },1000);
}

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

 14. Promise 对象

  • 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果
  • Promise 是一个对象,Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。axios 的内部实现原理就是通过 Promise 实现的
  • Promise对象有以下两个特点:
  1. 对象的状态不受外界影响。有三种状态:pending(进行中)、fulfilled(成功)和rejected(失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为resolved和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果
  • Promise对象是一个构造函数,用来生成Promise实例,带有一个回调函数,回调函数的两个参数是 resolve(成功) 和 reject(失败),这两个参数他们也是函数。
  • then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
  • 基本使用 
function timeOut(ms){
  // Promise对象是一个构造函数,用来生成Promise实例
  return  new Promise((resolve, reject) => {     // 闭包函数:一个函数里面返回一个函数
  // ... 模拟执行异步操作,后端返回的数据
      let res = {
        // code: 200,
        code: 201,
        data:{
            name: '牛肉粉'
        },    
        error: '失败了';
      }
      setTimeout(() => {
         // 异步执行成功
         if (res.code === 200){
            resolve(res.data);
         // 异步执行失败
         } else {
            reject(res.error);
         }
      }, ms);
 
    });

}

// then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
timeOut(2000).then((val) => {
  // success
  console.log(val);     //这里的值接收的是 resolved() 的值,输出 {name: "牛肉粉"}
}, (err) => {
  // failure
  console.log(err);     //这里的值接收的是 rejected() 的值,输出 失败了
});
  • 使用 Promise 对象封装Ajax
const getJson = function(url){
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.resposeType = 'json';
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send();
        xhr.onreadystatechange = () => {
            if(this.readyState === 4){
                if(this.status === 200){
                    resolve(this.respose);
                }else{
                    reject(new Error(this.statusText))
                }
            }
        }

    })

}

getJson('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((data) => {
    console.log(data);
},(error) => {
    console.log(error);
})
  • then() 方法 
  • 如果一个promise执行完后 返回的还是一个promise 实例(注意,不是原来那个Promise实例),会把这个promise 的执行结果,传递给下一次then中。因此可以采用链式写法,即then方法后面再调用另一个then方法。
// then 方法的链式调用
getJson('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((data) => {
    return data.HeWeather6;
},then(HeWeather6) => {
    console.log(HeWeather6);
})
  •  cath() 方法

catch(err=>{})方法等价于then(null,err=>{}),用于指定发生错误时的回调函数

getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
    .then((json) => {
    console.log(json);
}).then(null,err=>{
    console.log(err);   
})
//等价于
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
    .then((json) => {
    console.log(json);
}).catch(err=>{
    console.log(err);   
})
  •  resolve()

resolve()方法将现有对象转换成Promise对象,该实例的状态为 fulfilled 成功

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

reject()方法和resolve()方法一样返回一个新的Promise实例

该实例的状态为 rejected(失败

let p2 = Promise.reject(new Error('出错了'));
//等价于 let p2 = new Promise((resolve,reject)=>reject(new Error('出错了)));
p2.catch(err => {
    console.log(err);
})
  • all()

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

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

案例:试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all实现如下

let pro1 = new Promise((resolve, reject) => {});
let pro2 = new Promise((resolve, reject) => {});
let pro3 = new Promise((resolve, reject) => {});

let pAll = Promise.all([pro1, pro2, pro3]);
pAll.then(() => {
    // 三个都成功 才成功
}).cath(err => {
    // 如果一个失败 则失败
})



// 案例:试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的的个人信息和好友列表,
// 这两个任务是可以并行执行的,用Promise.all实现如下
let meInfoPro = new Promise( (resolve, reject)=> {
    // 模拟资源加载需要花费时间
    setTimeout(resolve, 500, 'P1');
});
let youInfoPro = new Promise( (resolve, reject)=> {
    // 模拟资源加载需要花费时间
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([meInfoPro, youInfoPro]).then( (results)=> {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});

注意:

Promise.all()接受一个promise对象的数组,待全部完成之后,统一执行success;

Promise.race()接受一个包含多个promise对象的数组,只要有一个完成,就执行success

  • race()

接受一个包含多个promise对象的数组,只要有一个完成,就执行success

举个更具体的例子,加深对race()方法的理解

当我们请求某个图片资源,会导致时间过长,给用户反馈

用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()接受一个包含多个promise对象的数组,只要有一个完成,就执行success
// 如果3s 之内加载完成就执行 then 方法, 3s 之后还没加载完成就执行 catch 方法
Promise.race([requestImg('images/2.png'), timeout()]).then((data) => {
    console.log(data);
    document.body.appendChild(data);
}).catch((err) => {
    console.log(err);
}); 

15. async 异步操作

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

作用:使得异步操作更加方便

异步操作是JavaScript编程的麻烦事,很多人认为async函数是异步编程的解决方案

  • 凡是在前面添加了async的函数在执行后都会自动返回一个Promise对象
async function test() {
    
}

let result = test()
console.log(result)  //即便代码里test函数什么都没返回,我们依然打出了Promise对象
  • await必须在async函数里使用,不能单独使用
  • await的作用之一就是获取后面Promise对象成功状态传递出来的参数传给 then 函数。
async function f() {
    /*
    ** 如果 await 命令后面的不是一个Promise实例对象,
    ** await 命令会自动把await后面转为一个Promise实例对象
    */
    return await 'hello async';
}
// 因为返回的是一个Promise实例对象,所以可以用链式编程 
f().then(v => {console.log(v)}).catch(e => {console.log(e)});      //输出 hello async
  • 如果 async 函数中有多个 await , 那么 then 函数 会等待所有的 await 指令运行完才去执行
async function f(){
    let s = await 'hello world'
    let data = await s.split('');
    return data;
}
f().then(v => {console.log(v)}).catch(e => {console.log(e)});   
  • 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
async function f(){
    await Promise.reject('出错了');
    await Promise.resolve('hello');
}
f().then(v => {console.log(v)}).catch(e => {console.log(e)});      //输出 出错了


/*
** async函数f执行后,await后面的 Promise 对象会抛出一个错误对象,
** 变成了 reject 状态,catch方法的回调函数被调用
*/ 
async function f2() {
  await new Promise(function (resolve, reject) {
    // 抛出一个错误对象
    throw new Error('出错了');
  });
}

f2().then(v => console.log(v)).catch(e => console.log(e))
// Error:出错了
  • async 的错误处理 try...catch 

针对上诉遇到 reject状态,就中断执行的问题,可以通过 try...catch 代码块解决

async function f() {
    try {
        await Promise.reject('出错了');
    } catch (error){

    }
    return await Promise.resolve('hello');
  });
}

f2().then(v => console.log(v)).catch(e => console.log(e))     // 输出 hello

案例演示:获取和风天气,现在 now 的数据

const getJson = function(url){
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.resposeType = 'json';
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send();
        xhr.onreadystatechange = () => {
            if(this.readyState === 4){
                if(this.status === 200){
                    resolve(this.respose);
                }else{
                    reject(new Error(this.statusText))
                }
            }
        }

    })

}
async function getNowWeather(url) {
    // 发送 ajax 获取实况天气
    let await = getJson(url);
    // 获取 HeWeather6 的数据 获取未来 3——7 天的数据
    let arr = await res.HeWeather6;
    return arr[0].now;
}

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

16. class 类的用法

// es5 
/*
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName  = function() {
    return this.sayName;
}
let p = new Person('牛肉粉',18);
console.log(p);
*/

// es6
class Person {
    // 实例化的时候会立即被调用
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    //等同于Person.prototype = function sayName(){}
    sayName(){
        return this.name;
    }
    sayAge(){
        return this.age;
    }
}
// 小技巧:通过 Objec.assign() 方法(详见对象的扩展功能)一次性向类中添加多个方法
/*
Object.assign(Person.prototype, {
    sayName(){
        return this.name;
    }
    sayAge(){
        return this.age;
    }
})
*/

let p = new Person('牛肉粉',18);
console.log(p);
p.sayName();
p.sayAge();
  • 类的继承
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 必须要调用
        super(name, age);  // 等同于 Animal.call(this,name,age); 继承父类的属性
        this.color = color;
    }
    // 子类自己的方法
    sayColor(){
        return `${this.name}是${this.age}岁了,它的颜色是${this.color}`;
    }
    // 重写父类的方法
    sayName(){
        // super 相同于 Animal
        return this.name + super.sayAge + this.color;
    }
}

let d1 = new Dog('小黄', 28, 'red');
console.log(d1.sayAge());     // 调用继承父类的方法
console.log(d1.sayColor());   // 调用子类自己的方法
console.log(d1.sayName());    // 调用重写父类的方法

17. ES6 module(模块化)

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

  • ES6 模块功能主要有两个命令构成:export import
  • export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
  • 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量
//module/index.js
export const name = '张三';
export const age = 18;
export const sayName = function() {
    console.log(fristName);
}

//也可以这样
const name = '张三';
const age = 18;
const sayName = function() {
    console.log(fristName);
}
export {name, age, sayName}
//main.js
import {name, age, sayName} from './modules/index.js';//解构赋值 {name, age, sayName}

使用export default命令为模块指定默认输出,在其它模块加载该模块时,import命令可以为该匿名函数指定任意名字

//export-default.js
export default function(){
    console.log('foo');
}

//或者写成
function foo() {
  console.log('foo');
}

export default foo;
//import-default.js
import customName from './export-default.js'
customNmae();//foo

如果想在一条import语句中,同时输入默认方法(default)和其他接口(非default),可以写成下面这样

//export-default.js
export default function(){
    console.log('foo');
}

export function add(){
    console.log('add')
}
import customName,{add} from 'export-default.js'

// 方式二 * 代表所有属性和方法 as 代表重命名
import * as f from 'export-default.js'   
console.log(f);   // 输出一个 模块, 里面保存了 export 传过来的所有属性和方法
console.log(f.default);  

export default也可以用来输出类。

// MyClass.js
export default class Person{ ... }

// main.js
import Person from 'MyClass';
let o = new Person();
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值