主頁 > 企業開發 > JavaScript進階

JavaScript進階

2023-02-09 07:43:01 企業開發

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

callapply可以指定函式運行時的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

img

2.constructor

每個原型都有一個 constructor 屬性指向關聯的建構式

console.log(Person === p.__proto__.constructor); //true

img

在 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__指向的物件)去找,找到了就可以呼叫

img

圖片第一行告訴了我們4點:

  1. 所有函式都有一個prototype指標,指向原型物件,如圖中的Foo的prototype指標,prototype指標的意義是,當我們使用這個建構式new出新物件的時候,新物件的__proto__指向prototype

  2. 建構式的prototype所指向的原型物件有一個constructor指標,指回建構式,如圖中Foo.prototype的constructor指標指向Foo,constructor指標有助于我們找到一個物件的建構式是誰,

  3. __proto__每個物件都有,js在new一個物件的時候,會將它的__proto__指向建構式的prototype指向的那個物件,在上圖中,f1、f2這些實體物件的__proto__都指向了Foo.prototype,

  4. 如果一個物件的__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語言的一大特點就是單執行緒,也就是說,同一個時間只能做一件事
單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行后一個任務,如果前一個任務耗時很長,后一個任務就不得不一直等著,于是,所有任務可以分成兩種,一種是同步任務,另一種是異步任務

同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;

異步任務指的是,不進入主執行緒、而進入"任務佇列" 的任務,只有"任務佇列"通知主執行緒,某個異步任務可以執行了,該任務才會進入主執行緒執行,

img

  1. 所有同步任務都在主執行緒上執行,形成一個執行堆疊,

  2. 主執行緒之外,還存在一個"任務佇列",只要異步任務有了運行結果,就在"任務佇列"之中放置一個事件

  3. 一旦"執行堆疊"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看里面有哪些事件,那些對應的異步任務,于是結束等待狀態,進入執行堆疊,開始執行

  4. 主執行緒不斷重復上面的第三步

    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代碼的執行順序,

img

進入整體代碼(宏任務)后,開始第一次回圈,

接著執行所有的微任務,然后再次從宏任務開始,找到其中一個任務佇列執行完畢,再執行所有的微任務

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/543324.html

標籤:其他

上一篇:Vue3 企業級優雅實戰 - 組件庫框架 - 9 實作組件庫 cli - 上

下一篇:react腳手架配置代理總結

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more