JS进阶

构造对象

new

new 运算符接受一个函数 F 及其参数: new F(arguments…).这个一过程分为:

  1. 创建类的实例.这步是把一个空的对象的 proto 属性设置为 F.prototype.
  2. 初始化实例.函数 F 被传入参数并调用.关键字 this 被设定为该实例.
  3. 返回实例.

对于实例对象来说,都是通过 new 产生的,无论是function Foo()还是let a = { b : 1 }

1
2
3
4
5
6
7
8
9
function person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}

p1 = new person("tom", 12);

instanceof

instanceof 是一个操作符,可以判断对象是否为某个类型的实例

1
2
3
p1 instanceof person; //true,说明p1是由person创造的
p1 instanceof Object; //true,
1 instanceof Number; //false,instanceof判断的是对象

原型与原型链

  1. new一个函数的时候会创建一个对象,函数.prototype等于被创建对象.__proto__
  2. 一切函数都是由 Function 这个函数创建的,所以Function.prototye === 被创建函数.__proto__
  3. 一切函数的原型对象都是由Object这个函数创建的,所以Object.prototype === 一切函数.prototype.__proto__

__proto__把对象和原型连接起来,形成原型链

this

在函数被直接调用时this绑定到全局对象.即 window

1
2
3
4
5
console.log(this); //指向window
function fn1() {
console.log(this); //指向window
}
fn1(); //相当于window.fn1()

内部函数

函数嵌套产生的内部函数的this不是他的父函数,仍然是全局,window

1
2
3
4
5
6
7
8
function fn0() {
function fn() {
console.log(this);
}
fn();
}

fn0();

setTimeOut()和 setInterval()

这两个函数执行的函数 this 也是全局

1
2
3
4
5
6
7
8
9
10
document.addEventListener(
"click",
function (e) {
console.log(this);
setTimeout(function () {
console.log(this);
}, 200);
},
false
);

DOM 对象绑定事件

在事件处理程序中 this 代表事件源 DOM 对象

1
2
3
4
5
6
7
8
9
10
11
12
document.addEventListener(
"click",
function (e) {
console.log(this); //指向dom对象
var _document = this;
setTimeout(function () {
console.log(this); //指向window
console.log(_document);
}, 200);
},
false
);

Function.prototype.bind()

bind 改变 this 的指向.返回一个新函数,并使函数内部的 this 为传入的第一个参数,并不执行

1
2
var fn3 = obj1.fn.bind(obj1);
fn3();

使用 call 和 apply 设置 this

call,apply 调用一个函数,传入函数执行上下文及其参数,并立即执行

1
2
fn.call(context,param1,param2...)//接受参数列表
fn.apply(context,paramArray)//接收参数数组

第一个参数都是希望设置的 this 对象

举例

1
2
3
4
5
6
7
8
//实现一个函数可以遍历传入的参数
function sum() {
//arguments.forEach无法执行,因为arguments不是数组,使用call将forEach的this指向arguments,使之可以遍历
Array.prototype.forEach.call(arguments, function (value) {
console.log(value);
});
}
sum(3, 4, 1, 6); //函数就可以遍历了
1
2
3
4
5
6
7
8
9
10
function sum() {
var result = 0;
//arguments.forEach无法执行,因为arguments不是数组,使用call将forEach的this指向arguments,使之可以遍历
Array.prototype.forEach.call(arguments, function (value) {
console.log(value);
return (result += value);
});
console.log(result);
}
sum(3, 4, 1, 6); //函数就可以将传入值相加了

arguments

  1. 在函数调用时,会自动在该函数内部生成一个名为 arguments 的隐藏对象
  2. 该对象类似于数组,可以使用[]运算符获取函数调用时传递的实参
  3. 只有函数被调用时,arguments 对象才会创建,未调用时其值为 null
1
2
3
4
5
6
7
8
function fn5(name, age) {
console.log(arguments);
name = "XXX";
console.log(arguments);
arguments[1] = 30;
console.log(arguments);
}
fn5("Byron", 20);

研究 this

1
2
3
4
5
6
7
8
9
var obj = {
foo: function () {
console.log(this);
},
};

var bar = obj.foo;
obj.foo(); // 打印出的 this 是 obj
bar(); // 打印出的 this 是 window

实际上的正常调用方式

func.call(context, p1, p2)

其他简化方式都可以转化

1
2
3
4
5
func(p1, p2) 等价于
func.call(undefined, p1, p2)

obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)

this,就是上面代码中的 context。就这么简单。

this 是你 call 一个函数时传的 context,由于你从来不用 call 形式的函数调用,所以你一直不知道。

浏览器规则:如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

上面的代码就解释的通了

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
foo: function () {
console.log(this);
},
};

var bar = obj.foo;
obj.foo(); // 转换为 obj.foo.call(obj),this 就是 obj
bar();
// 转换为 bar.call()
// 由于没有传 context
// 所以 this 就是 undefined
// 最后浏览器给你一个默认的 this —— window 对象

Event Handler 中的 this

1
2
3
btn.addEventListener("click", function handler() {
console.log(this); // 请问这里的 this 是什么
});

当使用 addEventListener() 为一个元素注册事件的时候,句柄里的 this 值是该元素的引用。其与传递给句柄的 event 参数的 currentTarget 属性的值一样。

jQuery Event Handler 中的 this

那么下面代码中的 this 是什么呢:

1
2
3
$ul.on("click", "li", function () {
console.log(this);
});

当 jQuery 的调用处理程序时,this 关键字指向的是当前正在执行事件的元素。对于直接事件而言,this 代表绑定事件的元素。对于代理事件而言,this 则代表了与 selector 相匹配的元素。(注意,如果事件是从后代元素冒泡上来的话,那么 this 就有可能不等于 event.target。)若要使用 jQuery 的相关方法,可以根据当前元素创建一个 jQuery 对象,即使用 $(this)。

[]语法

1
2
3
4
5
function fn() {
console.log(this);
}
var arr = [fn, fn2];
arr[0](); // 这里面的 this 又是什么呢?

我们可以把 arr0 想象为 arr.0( ),虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2) 对应上了,于是就可以愉快的转换了:

1
2
3
4
         arr[0]()
假想为 arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了

call、apply 、函数执行的本质

当我们执行一个函数,以下几种调用方式等价

1
2
3
4
5
6
7
8
"use strict";
function fn(a, b) {
console.log(this);
}
fn(1, 2);
//等价于
fn.call(undefined, 1, 2);
fn.apply(undefined, [1, 2]);

在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。

在非严格模式下(不加”use strict”), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {
fn: function (a, b) {
console.log(this);
},
child: {
fn2: function () {
console.log(this);
},
},
};
obj.fn(1, 2);
//等价于
obj.fn.call(obj, 1, 2); // 所以 this 是 obj
obj.fn.apply(obj, [1, 2]);

obj.child.fn2();
//等价于
obj.child.fn2.call(obj.chid); // 所以 this 是 obj.child

箭头函数中的 this

  1. 箭头函数会捕获其所在上下文的 this 值作为自己的 this 值,自己本身并没有 this 值.
  2. 箭头函数的 this 永远指向其上下文的 this,任何方法都改变不了其指向,如 call,bind,apply.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let app = {
fn1: function (a) {
console.log(this); //app
},
fn2(a) {
console.log(this); //app
},
fn3: (a) => {
console.log(this); //window
},
};

app.fn2.call(app);
app.fn3.call(它上下文的this);

箭头函数的复杂情况示例

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
44
45
46
47
48
49
50
51
52
53
54
var app = {
fn1() {
setTimeout(function () {
console.log(this);
}, 10);
},
fn2() {
setTimeout(() => {
console.log(this);
}, 20);
},
fn3() {
setTimeout(
function () {
console.log(this);
}.bind(this),
30
);
},
fn4: () => {
setTimeout(() => {
console.log(this);
}, 40);
},
};

app.fn1();
app.fn2();
app.fn3();
app.fn4();

以上代码相当于;
var app = {
fn1() {
function fn() {
console.log(this);
}
//过10ms后执行
//fn.call(undefined),所以输出window
},
fn2() {
//过20ms执行箭头函数
//箭头函数里没有资格有自己的this,借用setTimeout外面的this,也就是app
},
fn3() {
//创建了一个新函数,这个新函数绑定了外面的this,也就是app
//20ms后执行新函数,输出this,也就是刚刚绑定的app
},
fn4: () => {
//过40ms执行箭头函数
//箭头函数里没有资格有自己的this,借用setTimeout外面的this
//setTimeout所在的fn4也是箭头函数,没资格拥有自己的this,借用外面的this,也就是window
},
};

函数的执行环境

JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因

一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments 变量,其中包含调用函数时传入的参数

接下来创建作用域链,然后初始化变量。首先初始化函数的形参表,值为 arguments 变量中对应的值,如果 arguments 变量中没有对应值,则该形参初始化为 undefined。

如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,最后为 this 变量赋值,会根据函数调用方式的不同,赋给 this 全局对象,当前对象等

至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取.

每个执行上下文中都有三个重要的属性

  • 变量对象(VO),包含变量、函数声明和函数的形参,该属性只能在全局上下文中访问
  • 作用域链(JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了)
  • this

三种变量

实例变量:(this)类的实例才能访问到的变量

静态变量:(属性)直接类型对象能访问到的变量

私有变量:(局部变量)当前作用域内有效的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function ClassA() {
var a = 1; //私有变量,只有函数内部可以访问
this.b = 2; //实例变量,只有实例可以访问
}

ClassA.c = 3; // 静态变量,也就是属性,类型访问

console.log(a); // error
console.log(ClassA.b); // undefined,这里的this指向window
console.log(ClassA.c); //3

var classa = new ClassA();
console.log(classa.a); //undefined
console.log(classa.b); // 2,这里的this指向实例
console.log(classa.c); //undefined

继承

继承是指一个对象直接使用另一个对象的属性和方法.

如果实现以下两点就实现了继承

  1. 得到一个类的属性
  2. 得到一个类的方法

原型继承,核心在于在子类的构造函数中通过parent.call(this)继承父亲的属性,然后改变子类的原型为new parent()来继承父类的函数.

属性的获取

对象属性的获取是通过构造函数的执行.在一个类中执行另外一个类的构造函数,就可以把属性赋值到自己内部,

但是需要把环境改到自己的作用域内,用call修改 this 的指向即可.

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
function Person(name, sex) {
this.name = name;
this.sex = sex;
}

Person.prototype.printName = function () {
console.log(this.name);
};

function Male(age) {
this.age = age;
}

Male.prototype.printAge = function () {
console.log(this.age);
};

function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}
Male.prototype.printAge = function () {
console.log(this.age);
};

var m = new Male("Tom", "male", 10);
console.log(m.sex); //'male'

继承的范例

1
2
3
4
5
6
7
8
9
10
function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}

Male.prototype = Object.create(Person.prototype);

Male.prototype.printAge = function () {
console.log(this.age);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Person(name, sex) {
this.name = name;
this.age = age;
}

Person.prototype.printName = function () {
console.log(this.name);
};

function Male(name, sex, age) {
Person.call(this, name, age);
this.sex = sex;
}

Male.prototype = new Person();

Male.prototype.constuctor = Male;

Male.prototype.printAge = function () {
console.log(this.age);
};

var man = new Male("Tom", "Male", 10);
man.printName(); //'Tom'

hasOwnProperty

判断属性是自己的还是继承的

1
2
m.hasOwnProperty("name"); //true
m.hasOwnProperty("printName"); //false

Promise 对象

回调地狱

下列代码实现按顺序执行,1 秒后执行 fn1,再过 1 秒执行 fn2,再过 1 秒执行 fn3

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
function fn1(callback) {
setTimeout(() => {
console.log("fn1");
callback();
}, 1000);
}

function fn2(callback) {
setTimeout(() => {
console.log("fn2");
callback();
}, 1000);
}

function fn3() {
setTimeout(() => {
console.log("fn3");
}, 1000);
}

fn1(function () {
fn2(function () {
fn3();
});
});

由于层层嵌套,形成回调地狱.(如果套个十几二十个,真的要崩溃)

什么是 Promise

Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.(MDN 解释)

Promise 是一个对象,对象里存储着状态.分别是 pending(等待态),fulfilled(完成态),rejected(拒绝态)

Promise 启动之后,当满足成功的条件时我们让状态从 pending 变成 fullfilled (执行 resolve);当满足失败的条件,我们让状态从 pending 变成 rejected(执行 reject)

面试官想听的版本:

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理,让开发者不用再关注于时序和底层的结果。Promise 的状态具有不受外界影响和不可逆两个特点。

then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了.

then 的参数有两个,也是一个成功函数,一个失败函数.

1
const promise2 = doSomething().then(successCallback, failureCallback);

Promise+ajax 范例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//获取IP
function getIp(){
return new Promise(function(resolve,reject){
var xhr = XMLHttpRequest()
xhr.open('GET','https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp',true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson.ip)
}
xhr.onerror = function(){
reject('获取IP失败')
}
xhr.send()
})
}

//从IP获取城市
function getCityFromIp(ip){
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson.city)
}
xhr.onerror = function(){
reject('获取city失败')
}
xhr.send()
})
}

//通过城市获取天气
function getWeatherFromCity(city{
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city='+city, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson)
}
xhr.onerror = function(){
reject('获取天气失败')
}
xhr.send()
})
})

getIp().then(function(ip){
return getCityFromIp(ip)
}).then(function(city){
return getWeatherFromCity(city)
}).then(function(data){
console.log(data)
}).catch(function(e){
console.log('出现了错误',e)
})
//getIP()得到一个promise对象,传入ip,返回另一个promise对象,依次往下
//中途报错直接执行catch()

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getCityFromIp(ip){
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET','https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip',true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson)
}
xhr.onerror = function(){
reject('获取city失败')
}
xhr.send()
})
}

var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')

//Promise.all, 当所有的 Promise 对象都完成后再执行
Promise.all([p1,p2,p3]).then(data => {
console.log(data)
})

Promise.race

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
function getCityFromIp(ip) {
var promise = new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
"https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip=" +
ip,
true
);
xhr.onload = function () {
var retJson = JSON.parse(xhr.responseText); // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson);
};
xhr.onerror = function () {
reject("获取city失败");
};
setTimeout(() => {
xhr.send();
}, Math.random() * 1000);
});
return promise;
}

var p1 = getCityFromIp("10.10.10.1");
var p2 = getCityFromIp("10.10.10.2");
var p3 = getCityFromIp("10.10.10.3");

//Promise.race, 谁的先输出就先执行谁
Promise.race([p1, p2, p3]).then((data) => {
console.log(data);
});

callback&Promise&async/await

把一个需求不断简化

需求如下:

  1. 读取 a.md 文件,得到内容
  2. 把内容转换成 HTML 字符串
  3. 把 HTML 字符串写入 b.html

callback()处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require("fs");
var markdown = require("markdown").markdown;
fs.readFile("a.md", "UTF-8", function (err, str) {
if (err) {
return console.log(err);
}
var html = markown.toHTML(str);
fs.writeFile("b.html", html, function (err) {
if (err) {
return console.log(err);
}
console.log("write.success");
});
});

ES6 语法简化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let fs = require("fs");
let markdown = require("markdown").markdown;
fs.readFile("a.md", "UTF-8", (err, str) => {
if (err) {
return console.log(err);
}
let html = markdown.toHTML(str);
fs.writeFile("b.html", html, (err) => {
if (err) {
return console.log(err);
}
console.log("write.success");
});
});

Promise 处理

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
const fs = require("fs");
const markdown = require("markdown").markdown;

readFile("a.md")
.then((mdStr) => {
return markdown.toHTML(mdStr); //返回结果作为下个回调函数
})
.then((html) => {
writeFile("b.html", html);
})
.catch((e) => {
console.log(e);
});

//对读取文件进行包装
function readFile(url) {
return new Promise((resolve, reject) => {
fs.readFlie(url, "UTF-8", (err, str) => {
if (err) {
reject(new Error("readFlie error"));
} else {
resolve(str);
}
});
});
}

//对写入文件进行包装
function writeFile(url, data) {
return new Promise((resolve, reject) => {
fs.writeFile(url, data, (err, str) => {
if (err) {
reject(new Error("write error"));
} else {
resolve();
}
});
});
}

使用模块改装上面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const markdown = require("markdown").markdown;
const fsp = require("fs-promise"); //用于把fs变promise化
let onerror = (err) => {
console.error("something wrong");
};

fsp
.readFile("a.md", "UTF-8")
.then((mdStr) => {
return markdown.toHTML(mdStr);
})
.then((html) => {
fsp.writeFile("b.html", html);
})
.catch(onerror);

async/await 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
const markdown = require("markdown").markdown;
const fsp = require("fs-promise");
let onerror = (err) => {
console.error("sonething wrong");
};

async function start() {
let mdStr = await fsp.readFile("a.md", "UTF-8");
let html = markdown.toHTML(mdStr);
await fsp.writeFile("b.html", html);
}

start().catch(onerror);

方法

Promise.prototype.then(onFulfilled, onRejected)

接收成功或失败的结果回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来 resolve.

Promise.prototype.catch(onRejected)

接受一个失败的结果回调到当前 promise, 返回一个新的 promise。当这个回调函数被调用,新 promise 将以它的返回值来 resolve,否则如果当前 promise 进入 fulfilled 状态,则以当前 promise 的完成结果作为新 promise 的完成结果.

Promise.prototype.finally(onFinally)

添加一个事件处理回调于当前 promise 对象,并且在原 promise 对象解析完毕后,返回一个新的 promise 对象。回调会在当前 promise 运行完毕后被调用,无论当前 promise 的状态是完成(fulfilled)还是失败(rejected)

async/await

async 函数的创建时通过在函数声明语句之前加上 async 关键字,

这是异步函数的特征之一 ––它将任何函数转换为 promise。

示例如下:

1
2
3
4
5
6
7
const asyncFunction = async () => {
// Code
};
//或者
async function asyncFunction() {
//code
}

await

async 异步函数可通过await来暂停,该关键字只能用在 async 函数内部。

每当函数执行完毕,await返回的是任何 async 函数会返回的东西。

阮一峰老师的解释: async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句

只能放在 async 函数内部使用,不能放在普通函数里面,否则会报错。

后面放 Promise 对象,在Pending状态时,相应的协程会交出控制权,进入等待状态。等等党永不为奴!这个是本质。

await是 async wait 的意思,wait 的是resolve(data)消息,并把数据data返回。

await后面也可以跟同步代码,不过系统会自动转化成一个 Promise 对象。

await只关心异步过程成功的消息resolve(data),拿到相应的数据data。至于失败消息reject(error),不关心,不处理。

await 的特点

1.建立在 promise 之上。所以,不能把它和回调函数搭配使用。但它会声明一个异步函数,并隐式地返回一个 Promise。因此可以直接 return 变量,无需使用 Promise.resolve 进行转换。

2.和 promise 一样,是非阻塞的。但不用写 then 及其回调函数,这减少代码行数,也避免了代码嵌套。而且,所有异步调用,可以写在同一个代码块中,无需定义多余的中间变量。

3.它的最大价值在于,可以使异步代码,在形式上,更接近于同步代码。

4.它总是与 await 一起使用的。并且,await 只能在 async 函数体内。

5.await 是个运算符,用于组成表达式,它会阻塞后面的代码。如果等到的是 Promise 对象,则得到其 resolve 值。否则,会得到一个表达式的运算结果。

MDN 例子:

1
2
3
4
5
6
async function hello() {
return (greeting = await Promise.resolve("Hello"));
}

hello().then(alert);
//先等返回了hello再打印

简单案例:

1
2
3
4
5
6
7
8
9
10
11
12
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint("hello world", 50);
// 上面代码指定50毫秒以后,输出"hello world"。