javaScript進階
一、作用域
JS的作用域簡單來說就是變數(變數作用于又稱背景關系)和函式生效(能被訪問)的區域
1.全域作用域
函式之外宣告的變數,會成為全域變數,
變數在程式的任何地方都能被訪問,表示它是全域變數,window 物件的內置屬性都擁有全域作用域,
自動全域
如果您為尚未宣告的變數賦值,此變數會自動成為全域變數
2.函式作用域
在固定的代碼片段(函式)才能被訪問
函式作用域就是一個獨立的地盤,讓變數不會外泄、暴露出去,也就是說作用域最大的用處就是隔離變數,不同作用域下同名變數不會有沖突,
變數取值:到創建 這個變數 的函式的作用域中取值

3.作用域鏈
變數取值到 創建 這個變數 的函式的作用域中取值,
但是如果在當前作用域中沒有查到值,就會向上級作用域去查,直到查到全域作用域,這么一個查找程序形成的鏈條就叫做作用域鏈,

JavaScript 屬于解釋型語言,JavaScript 的執行分為:解釋和執行兩個階段,這兩個階段所做的事并不一樣:
解釋階段:
- 詞法分析
- 語法分析
- 作用域規則確定
執行階段:
- 創建執行背景關系
- 執行函式代碼
- 垃圾回收
JavaScript 解釋階段便會確定作用域規則,因此作用域在函式定義時就已經確定了,而不是在函式呼叫時確定,但是執行背景關系是函式執行之前創建的,執行背景關系最明顯的就是 this 的指向是執行時確定的,而作用域訪問的變數是撰寫代碼的結構確定的,
作用域和執行背景關系之間最大的區別是:
執行背景關系在運行時確定,隨時可能改變;作用域在定義時就確定,并且不會改變,
一個作用域下可能包含若干個背景關系環境,有可能從來沒有過背景關系環境(函式從來就沒有被呼叫過);有可能有過,現在函式被呼叫完畢后,背景關系環境被銷毀了;有可能同時存在一個或多個(閉包),同一個作用域下,不同的呼叫會產生不同的執行背景關系環境,繼而產生不同的變數的值,
二、this指向
在函式內,this是非常特殊的關鍵詞識別符號,在每個函式的作用域中被自動創建
當函式被呼叫,一個背景關系就被創建,背景關系包括函式在哪呼叫,誰呼叫的,引數是哪些,等等,背景關系中的this,指的就是函式指行期間的this,this的背景關系基于函式呼叫的情況,和函式在哪定義無關,但是和函式怎么呼叫有關
this理解的關鍵:
1:this永遠指向一個物件;
2:this的指向完全取決于函式呼叫的位置;
1、全域
在全域背景關系(任何函式以外),this指向全域物件,
console.log(this === window); // true
2、函式
在函式內部時,this由函式怎么呼叫來確定
//簡單呼叫,即獨立函式呼叫
function f1(){
return this;
}
//當前呼叫者其實是window window.f1()
f1() === window;
當函式作為物件方法呼叫時,this指向該物件
var obj = {
type: 1,
name: 'Tina',
age: 18,
sayHi:function(){
console.log(this)
console.log('hi~~')
}
}
//this === obj
obj.sayHi()
3.建構式
建構式的this指向創建的實體物件
function Obj (name,age){
this.name = name
this.age = age
this.sayHai= function(){
console.log(this)
}
}
let o = new Obj('Tina',18) //{name: 'tina', age: 18}
建構式執行程序分為4步:
第一步: 創建一個Object物件實體,
第二步: 將建構式的執行物件賦給新生成的這個實體,
第三步: 執行建構式中的代碼
第四步: 回傳新生成的物件實體
function Obj (name){
this.name = name
return name
}
let o = new Obj('Tina') // ???
建構式在new的時候,會默認創建一個空的 { },并且把建構式的prototype賦值給空物件的__proto__,當建構式回傳值不是物件時,回傳值就會默認成建構式自己創建的物件
4.call和apply
call和apply可以指定函式運行時的this
call方法使用的語法規則
函式名稱.call(obj,arg1,arg2...argN);
引數說明:
obj:函式內this要指向的物件,
arg1,arg2...argN :引數串列,引數與引數之間使用一個逗號隔開
apply方法使用的語法規則
函式名稱.apply(obj,[arg1,arg2...,argN])
引數說明:
obj :this要指向的物件
[arg1,arg2...argN] : 引數串列,要求格式為陣列
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
練習題
var A = {
name: '張三',
f: function () {
console.log('姓名:' + this.name);
}
};
var B = {
name: '李四'
};
B.f = A.f;
B.f()
A.f()
function foo() {
console.log(this.a);
}
var obj2 = {
a: 2,
fn: foo
};
var obj1 = {
a: 1,
o1: obj2
};
obj1.o1.fn();
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a);
console.log(this);
}
}
}
var j = o.b.fn;
j();
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x =5;
return function() {
return this.x;
}();
},
getY: function() {
var y =7;
return this.y;
}
}
console.log(obj.getX())
console.log(obj.getY())
var name="the window";
var object={
name:"My Object",
getName:function(){
return this.name;
}
}
object.getName();
(object.getName)();
(object.getName=object.getName)();
三、原型
1.prototype
在JavaScript中,每個函式 都有一個prototype屬性,當一個函式被用作建構式來創建實體時,這個函式的prototype屬性值會被作為原型賦值給物件實體(也就是設定 實體的__proto__屬性),也就是說,所有實體的原型參考的是函式的prototype屬性,
建構式使用方式
function Person(name,age){
this.name = name
this.age = age
}
var p = new Person('張三',20);
每一個JavaScript物件(除了 null )都具有的一個屬性,叫__proto__,這個屬性會指向該物件的原型
console.log(p.__proto__ === Person.prototype); // true

2.constructor
每個原型都有一個 constructor 屬性指向關聯的建構式
console.log(Person === p.__proto__.constructor); //true

在 Javascript 語言中,constructor 屬性是專門為 function 而設計的,它存在于每一個 function 的prototype 屬性中,這個 constructor 保存了指向 function 的一個參考
通過建構式創建的物件,constructor 指向建構式,而建構式本身的constructor ,則指向Function本身,因為所有的函式都是通過new Function()構造的
function Person() {
}
var p = new Person()
console.log(Person.prototype); // Object{}
console.log(p.prototype); // undifined
console.log(p.constructor); //function Person(){}
此處的p是通過 Person函式構造出來的,所以p的constructor屬性指向Person
console.log(Person.constructor); //function Function(){}
之前提過,每個函式其實是通過new Function()構造的
console.log({}.constructor); // function Object(){}
每個物件都是通過new Object()構造的
console.log(Object.constructor); // function Function() {}
Object也是一個函式,它是Function()構造的
console.log([].constructor); //function Array(){}
函式是物件構造的 物件也是函式構造的,倆者即是函式也是物件,所以為什么建構式它是一個函式卻回傳一個物件,倆者是互相繼承的關系
var o1 = new f1();
typeof o1 //"object"
prototype的用法
最主要的方法就是將屬性暴露成公用的
代碼對比:
function Person(name,age){
this.name = name;
this.age = age;
this.sayHello = function(){
console.log(this.name + "say hello");
}
}
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name); //bella
console.log(boy.name); //alex
console.log(girl.sayHello === boy.sayHello); //false
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello=function(){
console.log(this.name + "say hello");
}
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name); //bella
console.log(boy.name); //alex
console.log(girl.sayHello === boy.sayHello); //true
總結:
function Person(){
}
var person1=new Person()
person1.__proto__==Person.prototype
person1.constructor==Person
Person.__proto__==Function.prototype
Person.prototype.constructor==Person
person1.__proto__.constructor==Person
四、原型鏈
在js中,大部分東西都是物件,陣列是物件,函式也是物件,物件更加是物件,不管我們給陣列和函式定義什么內容,它們總是有一些相同的方法和屬性,比如說valueOf(),toString()等
這說明一個物件所擁有的屬性不僅僅是它本身擁有的屬性,它還會從其他物件中繼承一些屬性,當js在一個物件中找不到需要的屬性時,它會到這個物件的父物件上去找,以此類推,這就構成了物件的原型鏈
function Foo(_name) {
this.name = _name;
}
Foo.prototype.show = function() {
console.log('I am ', this.name);
};
var f1 = new Foo('obj1');
var f2 = new Foo('obj2');
f1.show(); // I am obj1
f2.show(); // I am obj2
//我們定義的show函式在Foo.prototype中,當我們執行f1.show()時,js發現f1本身沒有show這個屬性,所以它就到f1的原型(也就是__proto__指向的物件)去找,找到了就可以呼叫

圖片第一行告訴了我們4點:
-
所有函式都有一個prototype指標,指向原型物件,如圖中的Foo的prototype指標,prototype指標的意義是,當我們使用這個建構式new出新物件的時候,新物件的
__proto__指向prototype -
建構式的prototype所指向的原型物件有一個constructor指標,指回建構式,如圖中Foo.prototype的constructor指標指向Foo,constructor指標有助于我們找到一個物件的建構式是誰,
-
__proto__每個物件都有,js在new一個物件的時候,會將它的__proto__指向建構式的prototype指向的那個物件,在上圖中,f1、f2這些實體物件的__proto__都指向了Foo.prototype, -
如果一個物件的
__proto__指向了另一個物件,那么前者就繼承了后者的所有屬性function Foo(_name) { this.name = _name; } Foo.prototype.show = function() { console.log('I am ', this.name); }; var f1 = new Foo('obj1'); var f2 = new Foo('obj2'); var obj = { type:1 } f1.__proto__ = obj console.dir(f1) f1.show(); // I am obj1 f2.show(); // I am obj2
Foo是一個函式,它的建構式是js內部的function Function(),Function的prototype指向了一個物件Function.prototype,因此Foo的__proto__就指向了Function.prototype
所有的函式都以function Function()為建構式,因此,所有函式(包括function Function()和function Object())的__proto__都指向Function.prototype這個物件,這個物件中定義了所有函式都共有的方法,比如call()、apply()等,
我們繼續深入下去,Function.prototype這個物件,它就是一個普通的物件,它的建構式是js內置的function Object(),function Object()的prototype指向Object.prototype,因此Function.prototype.__proto__就指向Object.prototype,這個物件中定義了所有物件共有的屬性,比如我們之前說的hasOwnProperty()和toString()等,
同理,Foo.prototype和其他自定義的物件也是
__proto__指向Object.prototype物件
Object.prototype就是原型鏈的終點了,它的__proto__是null,js查找屬性時,如果到這里還沒有找到,那就是undefined了
五、閉包
函式和函式內部能訪問到的變數加在一起就是一個閉包
常規認為,一個函式嵌套另一個函式,兩個函式中間的環境,叫閉包,但其實這也是制造一個不會被污染沙箱環境,實質上,由于js函式作用域的存在,所有的函式,都可以是一個閉包
function foo(){
var num = 1
function add(){
num++
return num
}
return add
}
var func = foo()
func()
閉包常常用來間接訪問一個變數也可以理解為隱藏一個變數
function foo(){
var num = 18
var obj = {
text:'我是一個字串',
getNUm:function(){
return num
}
}
return obj
}
var obj = foo()
var age = obj.getNUm()
console.log(age)
由于 JS 的函式內部可以使用函式外部的變數,而函式外部無法訪問到函式內部的變數,所以正好符合了閉包的定義,所以只要懂了 JS 的作用域,自然而然就懂了閉包,
六、事件機制
JavaScript語言的一大特點就是單執行緒,也就是說,同一個時間只能做一件事
單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行后一個任務,如果前一個任務耗時很長,后一個任務就不得不一直等著,于是,所有任務可以分成兩種,一種是同步任務,另一種是異步任務
同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;
異步任務指的是,不進入主執行緒、而進入"任務佇列" 的任務,只有"任務佇列"通知主執行緒,某個異步任務可以執行了,該任務才會進入主執行緒執行,

-
所有同步任務都在主執行緒上執行,形成一個執行堆疊,
-
主執行緒之外,還存在一個"任務佇列",只要異步任務有了運行結果,就在"任務佇列"之中放置一個事件
-
一旦"執行堆疊"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看里面有哪些事件,那些對應的異步任務,于是結束等待狀態,進入執行堆疊,開始執行
-
主執行緒不斷重復上面的第三步
console.log(1); setTimeout(function() { console.log(2); },1000) setTimeout(function() { console.log(3); },0) console.log(4); /* 分析: 同步任務,按照順序一步一步執行 異步任務,當讀取到異步任務的時候,將異步任務放置到任務佇列 中,當滿足某種條件或者說指定事情完成了(這里的是時間分別是達到了0ms和1000ms)當指定 事件完成了才從任務佇列中注冊到主執行緒的事件佇列,當同步事件完成了,便從 事件佇列中讀取事件執行,(因為3的事情先完成了,所以先從任務佇列中注冊到 事件佇列中,所以先執行的是3而不是在前面的2) */
宏任務與微任務
宏任務:script代碼,setTimeout,setInterval
微任務:Promise,process.nextTick
不同型別的任務會進入對應的任務佇列,
事件回圈的順序,決定js代碼的執行順序,

進入整體代碼(宏任務)后,開始第一次回圈,
接著執行所有的微任務,然后再次從宏任務開始,找到其中一個任務佇列執行完畢,再執行所有的微任務
console.log(1);
setTimeout(function() {
console.log(2)
},1000);
new Promise(function(resolve) {
console.log(3);
resolve();
}
).then(function() {
console.log(4)
});
console.log(5);
/*
為什么是這樣呢?因為以同步異步的方式來解釋執行機制是不準確的,更加準確的方式是宏任務和微任務:
因此執行機制便為:執行宏任務 ===> 執行微任務 ===> 執行另一個宏任務 ===> 不斷回圈
即:在一個事件回圈中,執行第一個宏任務,宏任務執行結束,執行當前事件回圈中的微任務,
執行完畢之后進入下一個事件回圈中,或者說執行下一個宏任務
*/
七、ES6
1.let
let 宣告的變數只在 let 命令所在的代碼塊內有效
{
let a = 0;
a // 0
}
a // 報錯 ReferenceError: a is not defined
let 只能宣告一次 (var 可以宣告多次)
let a = 1;
let a = 2;
var b = 3;
var b = 4;
a // Identifier 'a' has already been declared 識別符號“a”已宣告
b // 4
let 不存在變數提升,var 會變數提升
console.log(a); //ReferenceError: a is not defined 參考錯誤:未定義a
let a = "apple";
console.log(b); //undefined
var b = "banana";
2.const
const 宣告一個只讀的常量,一旦宣告,常量的值就不能改變
const PI = "3.1415926";
PI = '123' //Assignment to constant variable 常量變數的賦值
暫時性死區:
var PI = "a";
if(true){
console.log(PI); // ReferenceError: PI is not defined
const PI = "3.1415926";
}
ES6 明確規定,代碼塊內如果存在 let 或者 const,代碼塊會對這些命令宣告的變數從塊的開始就形成一個封閉作用域,代碼塊內,在宣告變數 PI 之前使用它會報錯,
3.解構賦值
解構賦值是對賦值運算子的擴展,是一種針對陣列或者物件進行模式匹配,然后對其中的變數進行賦值處理
基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
可忽略
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
不完全解構
let [a = 1, b] = [];
// a = 1, b = undefined
let obj = {p: [{y: 'world'}] };
let {p: [y, x] } = obj;
// x = undefined
// y = 'world'
剩余運算子
let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
解構默認值
let [a = 2] = [undefined]; // a = 2
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
字串等
在陣列的解構中,解構的目標若為可遍歷物件,皆可進行解構賦值
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
4.Proxy
Proxy 可以對目標物件的讀取、函式呼叫等操作進行攔截,然后進行操作處理,它不直接操作物件,而是像代理模式,通過物件的代理物件進行操作,在進行這些操作時,可以添加一些需要的額外操作
let target = {
name: 'Tom',
age: 24
}
let handler = {
get: function(target, key) {
console.log('getting '+key);
return target[key]; // 不是target.key
},
set: function(target, key, value) {
console.log('setting '+key);
target[key] = value;
}
}
let proxy = new Proxy(target, handler)
proxy.name // 實際執行 handler.get
proxy.age = 25 // 實際執行 handler.set
// getting name
// setting age
// 25
5.箭頭函式
箭頭函式提供了一種更加簡潔的函式書寫方式,基本語法是:
引數 => 函式體
var f = v => v;
//等價于
var f = function(a){
return a;
}
f(1); //1
當箭頭函式沒有引數或者有多個引數,要用 () 括起來
var f = (a,b) => a+b;
f(6,2); //8
當箭頭函式函式體有多行陳述句,用 {} 包裹起來,表示代碼塊,當只有一行陳述句,并且需要回傳結果時,可以省略 {} , 結果會自動回傳
var f = (a,b) => {
let result = a+b;
return {};
}
f(6,2); // 8
當箭頭函式要回傳物件的時候,為了區分于代碼塊,要用 () 將物件包裹起來
// 報錯
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不報錯
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
箭頭函式沒有 this、arguments系結
var func = () => {
// 箭頭函式里面沒有 this 物件,
// 此時的 this 是外層的 this 物件,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭頭函式體中的 this 物件,是定義函式時的物件,而不是使用函式時的物件
function fn(){
setTimeout(()=>{
// 定義時,this 系結的是 fn 中的 this 物件
console.log(this.a);
},0)
}
var a = 20;
fn()
// fn 的 this 物件為 window 所以列印20
6.class類
在ES6中,class (類)作為物件的模板被引入,可以通過 class 關鍵字定義類,class 的本質是 function,
它可以看作一個語法糖,讓物件原型的寫法更加清晰、更像面向物件編程的語法
注意要點:不可重復宣告
class Example {
constructor(a) {
this.a = a;
}
}
let obj = new Example(1)
console.log(obj)
通過 extends 實作類的繼承
class Child extends Example {
constructor(a,b) {
super(b);
this.b = a;
this.hi=()=>alert('hi')
}
}
let b = new Child(1,2)
console.log(b)//{a:2,b:1,hi:()=>alert('hi')}
7.模塊
export和import
ES6 的模塊化分為匯出(export) @與匯入(import)兩個模塊
模塊匯入匯出各種型別的變數,如字串,數值,函式,類,
- 匯出的函式宣告與類宣告必須要有名稱(export default 命令另外考慮),
- 不僅能匯出宣告還能匯出參考(例如函式),
- export 命令可以出現在模塊的任何位置
- import 命令會提升到整個模塊的頭部,首先執行,
/*-----export [test.js]-----*/
let myName = "Tom";
let myAge = 20;
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is Tom! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// Tom
console.log(myClass.a );// yeah!
as 的用法
export 命令匯出的介面名稱,須和模塊內部的變數有一一對應關系
匯入的變數名,須和匯出的介面名稱相同,順序可以不一致
不同模塊匯出介面名稱命名重復, 使用 as 重新定義變數名
/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
//使用 as 重新定義匯出的介面名稱,隱藏模塊內部的變數
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry
export default
- 在一個檔案或模塊中,export、import 可以有多個,export default 僅有一個,
- export default 中的 default 是對應的匯出介面變數,
- 通過 export 方式匯出,在匯入時要加{ },export default 則不需要,
- export default 向外暴露的成員,可以使用任意變數來接收,
var a = "My name is Tina!";
export default a; // 僅有一個
import b from "./xxx.js"; // 不需要加{}, 使用任意變數接收
8.Promise
Promise 異步操作有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗),除了異步操作的結果,任何其他操作都無法改變這個狀態,
Promise 物件只有:從 pending 變為 fulfilled 和從 pending 變為 rejected 的狀態改變,只要處于 fulfilled 和 rejected ,狀態就不會再變了即 resolved(已定型)
const p1 = new Promise(function(resolve,reject){
resolve('success1');
// resolve('success2');
});
const p2 = new Promise(function(resolve,reject){
resolve('success3');
// reject('reject');
});
p1.then(function(value){
console.log(value); // success1
});
p2.then(function(value){
console.log(value); // success3
});
無法取消 Promise ,一旦新建它就會立即執行,無法中途取消,
如果不設定回呼函式,Promise 內部拋出的錯誤,不會反應到外部,
當處于 pending 狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)
then方法
then 方法接收兩個函式作為引數,第一個引數是 Promise 執行成功時的回呼,第二個引數是 Promise 執行失敗時的回呼,兩個函式只會有一個被呼叫,
在 JavaScript 事件佇列的當前運行完成之前,回呼函式永遠不會被呼叫
const p = new Promise(function(resolve,reject){
resolve('success');
});
p.then(function(value){
console.log(value);
});
console.log('first');
// first
// success
通過 .then 形式添加的回呼函式,不論什么時候,都會被呼叫,
then 方法將回傳一個 resolved 或 rejected 狀態的 Promise 物件用于鏈式呼叫,且 Promise 物件的值就是這個回傳值
多次呼叫
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一個then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二個then // 2
console.log(value);
}).then(function(value){ // 第三個then // undefined
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ // 第四個then // resolve
console.log(value);
return Promise.reject('reject');
}).then(function(value){ // 第五個then //reject:reject
console.log('resolve:' + value);
}, function(err) {
console.log('reject:' + err);
});
9.async
async 函式回傳一個 Promise 物件,可以使用 then 方法添加回呼函式
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
async 函式中可能會有 await 運算式,async 函式執行時,如果遇到 await ,那么await標記的運算式會先執行一遍,將await后面的代碼加入到微任務中,然后就會跳出整個async函式來執行后面的代碼,
await 關鍵字僅在 async function 中有效
function testAwait(){
return new Promise((resolve) => {
setTimeout(function(){
console.log("testAwait");
resolve();
}, 1000);
});
}
async function helloAsync(){
await testAwait();
console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync
練習題
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
reject('error');
})
promise.then(() => {
console.log(3);
}).catch(e => console.log(e))
console.log(4);
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once')
resolve('success')
}, 1000)
})
promise.then((res) => {
console.log(res)
})
promise.then((res) => {
console.log(res)
})
const p1 = () => (new Promise((resolve, reject) => {
console.log(1);
let p2 = new Promise((resolve, reject) => {
console.log(2);
const timeOut1 = setTimeout(() => {
console.log(3);
resolve(4);
}, 0)
resolve(5);
});
resolve(6);
p2.then((arg) => {
console.log(arg);
});
}));
const timeOut2 = setTimeout(() => {
console.log(8);
const p3 = new Promise(reject => {
reject(9);
}).then(res => {
console.log(res)
})
}, 0)
p1().then((arg) => {
console.log(arg);
});
console.log(10);
async function async1() {
console.log(1)
await async2()
console.log(2)
}
async function async2() {
console.log(3)
}
console.log(4)
setTimeout(() => {
console.log(5)
}, 0);
async1()
new Promise(resolve => {
console.log(6)
resolve()
})
.then(() => {
console.log(7)
})
console.log(8)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/543319.html
標籤:JavaScript
上一篇:Axios
下一篇:JavaScript 高階函式
