JavaScript 部分基礎知識點

2021-12-29 前端技術優選

   戳藍字「前端技術優選」關注我們哦!

JavaScript (JS) 是一種程式語言,為通常用於客戶端(client-side)的網頁動態腳本,不過,也常通過像Node.js這樣的包,用於 伺服器端(server-side)。

今天,發一篇關於Js基礎知識點的文章,為更多的新人指路。總會有人在你的前方為你探路,前行之路,你不孤單~

先來個目錄結構

───1、變量聲明
│   └───JavaScript 的數據類型分類和判斷
│   └───引用類型和值類型
───2、原型與原型鏈(繼承)
│   └───原型和原型鏈
───3、作用域和閉包
│   └───作用域
│   └───什麼是閉包,如何形成?
───4、如何理解同步和異步
│   └───同步 vs 異步
│   └───異步和單線程
│   └───前端異步的場景描述
───5、簡單描述一下對 ES6/ES7 的了解
│   └───解構賦值
│   └───箭頭函數
│   └───Promise 對象
│   └───Set 和 Map 數據結構

1、變量聲明1-1、JavaScript 的數據類型分類和判斷

在 JavaScript 中,共有7種基本類型:

其中string、number、Boolean、undefined、Null、symbol是6種原始類型。

值得注意的是:原始類型中不包含 Object。

類型判斷用到哪些方法?

1、typeof

typeof xxx 得到的值有以下幾種類型: undefined boolean number string object function symbol。

例如:

console.log(typeof 42);
// expected output: "number"

console.log(typeof 'blubber');
// expected output: "string"

console.log(typeof true);
// expected output: "boolean"

console.log(typeof declaredButUndefinedVariable);
// expected output: "undefined";

typeof null 結果是 object ,JavaScript 誕生以來便如此,由於 null 代表的是空指針(大多數平臺下值為 0x00),因此,null 的類型標籤是 0,typeof null 也因此返回 "object"。

typeof [1, 2] 結果是 object ,結果中沒有array 這一項,引用類型除了function其他的全部都是 object

typeof Symbol() 用 typeof 獲取 symbol 類型的值得到的是 symbol ,Symbol實例是唯一且不可改變的這是 ES6 新增的知識點.

2、instanceof

用於實例和構造函數的對應。例如判斷一個變量是否是數組,使用 typeof 無法判斷,但可
以使用 [1, 2] instanceof Array 來判斷,返回true。因為, [1, 2] 是數組,它的構造函數就是
Array 。同理:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
var auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// expected output: true

console.log([1, 2] instanceof Array);
// expected output: true

1-2、引用類型和值類型

除了原始類型,JS 還有引用類型,上面提到的 typeof 識別出來的類型中,只有 object
和 function 是引用類型,其他都是值類型。

根據 JavaScript 中的變量類型傳遞方式,又分為值類型引用類型,值類型變量包括
Boolean、String、Number、Undefined、Null,引用類型包括了 Object 類的所有,如
Date、Array、Function 等。在參數傳遞方式上,值類型是按值傳遞,引用類型是按共享
傳遞。

// 值類型
var a = 1;
var b = a;
b = 3
console.log(a) // 1
console.log(b) // 3
// a b 都是值類型,兩者分別修改賦值,相互之間沒有任何影響。

// 引用類型
var a = {x: 10, y: 20}
var b = a
b.x = 100
b.y = 200
console.log(a) // {x: 100, y: 200}
console.log(b) // {x: 100, y: 200}

a 和 b 都是引用類型。在執行了 b = a 之後,修改 b 的屬性值, a 的也跟著
變化。因為 a 和 b 都是引用類型,指向了同一個內存地址,即兩者引用的是同一個值,因
此 b 修改屬性時, a 的值隨之改動。

2、原型與原型鏈(繼承)

JavaScript 常被描述為一種基於原型的語言 (prototype-based language)——每個對象擁有一個原型對象,對象以其原型為模板、從原型繼承方法和屬性。原型對象也可能擁有原型,並從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱為原型鏈 (prototype chain),它解釋了為何一個對象會擁有定義在其他對象中的屬性和方法。

注意: 理解對象的原型(可以通過Object.getPrototypeOf(obj)或者已被棄用的__proto__屬性獲得)與構造函數的prototype屬性之間的區別是很重要的。前者是每個實例上都有的屬性,後者是構造函數的屬性。也就是說,Object.getPrototypeOf(new Foobar())和Foobar.prototype指向著同一個對象。

在javascript中,函數可以有屬性。每個函數都有一個特殊的屬性叫作原型(prototype) ,正如下面所展示的。請注意,下面的代碼是獨立的一段(在網頁中沒有其他代碼的情況下,這段代碼是安全的)。為了最好的學習體驗,你最好打開一個控制臺 (在Chrome和Firefox中,可以按Ctrl+Shift+I 來打開)切換到"Console"選項卡, 複製粘貼下面的JavaScript代碼,然後按回車來運行。

function doSomething(){}
console.log( doSomething.prototype );
// 不管您如何聲明函數,javascript中的函數總是有一個默認的原型屬性
var doSomething = function(){}; 
console.log( doSomething.prototype );

正如上面所看到的, doSomething 函數有一個默認的原型屬性,它在控制臺上面呈現了出來. 運行這段代碼之後,控制臺上面應該出現了像這樣的一個對象.

{
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ `hasOwnProperty`(),
        isPrototypeOf: ƒ `isPrototypeOf`(),
        propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
        toLocaleString: ƒ `toLocaleString`(),
        toString: ƒ `toString`(),
        valueOf: ƒ `valueOf`()
    }
}

現在,我們可以添加一些屬性到 doSomething 的原型上面,如下所示:

function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );

輸出:

{
    foo: "bar",
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ `hasOwnProperty`(),
        isPrototypeOf: ƒ `isPrototypeOf`(),
        propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
        toLocaleString: ƒ `toLocaleString`(),
        toString: ƒ `toString`(),
        valueOf: ƒ `valueOf`()
    }
}

然後,我們可以使用 new 運算符來在現在的這個原型基礎之上,創建一個 doSomething 的實例。

function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );

輸出:

{
    prop: "some value",
    __proto__: {
        foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ `hasOwnProperty`(),
            isPrototypeOf: ƒ `isPrototypeOf`(),
            propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
            toLocaleString: ƒ `toLocaleString`(),
            toString: ƒ `toString`(),
            valueOf: ƒ `valueOf`()
        }
    }
}

就像上面看到的, doSomeInstancing 的 __proto__ 屬性就是doSomething.prototype. 但是這又有什麼用呢? 好吧,當你訪問 doSomeInstancing 的一個屬性, 瀏覽器首先查找 doSomeInstancing 是否有這個屬性. 如果 doSomeInstancing 沒有這個屬性, 然後瀏覽器就會在 doSomeInstancing 的 __proto__中查找這個屬性(也就是 doSomething.prototype). 如果 doSomeInstancing 的 __proto__ 有這個屬性, 那麼 doSomeInstancing 的 __proto__ 上的這個屬性就會被使用. 否則, 如果 doSomeInstancing 的 __proto__ 沒有這個屬性, 瀏覽器就會去查找 doSomeInstancing 的 __proto__ 的 __proto__ ,看它是否有這個屬性. 默認情況下, 所有函數的原型屬性的 __proto__ 就是 window.Object.prototype. 所以 doSomeInstancing 的 __proto__ 的 __proto__ (也就是 doSomething.prototype 的 __proto__ (也就是 Object.prototype)) 會被查找是否有這個屬性. 如果沒有在它裡面找到這個屬性, 然後就會在 doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 裡面查找. 然而這有一個問題: doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 不存在. 最後, 原型鏈上面的所有的 __proto__ 都被找完了, 瀏覽器所有已經聲明了的 __proto__ 上都不存在這個屬性,然後就得出結論,這個屬性是 undefined.

function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop:      " + doSomeInstancing.prop);
console.log("doSomeInstancing.foo:       " + doSomeInstancing.foo);
console.log("doSomething.prop:           " + doSomething.prop);
console.log("doSomething.foo:            " + doSomething.foo);
console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
console.log("doSomething.prototype.foo:  " + doSomething.prototype.foo);

輸出:

doSomeInstancing.prop:      some value
doSomeInstancing.foo:       bar
doSomething.prop:           undefined
doSomething.foo:            undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo:  bar

是不是看的頭大了,別擔心。看看這個:

所有的引用類型(數組、對象、函數),都具有對象特性,即可自由擴展屬性( null除外)

所有的引用類型(數組、對象、函數),都有一個 proto 屬性,屬性值是一個普通的對象

所有的函數,都有一個 prototype 屬性,屬性值也是一個普通的對象

所有的引用類型(數組、對象、函數), proto 屬性值指向它的構造函數的prototype 屬性值

// 要點一:自由擴展屬性
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
// 要點二:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
// 要點三:函數有 prototype
console.log(fn.prototype)
// 要點四:引用類型的 __proto__ 屬性值指向它的構造函數的 prototype 屬性值
console.log(obj.__proto__ === Object.prototype)

2-1、原型和原型鏈

原型

// 構造函數
function Foo(name, age) {
 this.name = name
}
Foo.prototype.alertName = function () {
 alert(this.name)
}
// 創建示例
var f = new Foo('zhangsan')
f.printName = function () {
 console.log(this.name)
}
// 測試
f.printName()
f.alertName()

執行 printName 時很好理解,但是執行 alertName 時發生了什麼?這裡再記住一個重點 當
試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那麼會去它的
__proto__ (即它的構造函數的 prototype )中尋找,因此 f.alertName 就會找到
Foo.prototype.alertName 。

那麼如何判斷這個屬性是不是對象本身的屬性呢?使用 hasOwnProperty ,常用的地方是遍歷一個對象的時候。

var item
for (item in f) {

 /* 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性,    但是這裡建議大家還是加上這個判斷,保證程序正常輸出*/

 if (f.hasOwnProperty(item)) {
 console.log(item)
 }
}

原型鏈

還是接著上面的示例,如果執行 f.toString() 時,又發生了什麼?

f.printName()

因為 f 本身沒有 toString() ,並且 f.__proto__ (即 Foo.prototype )中也沒有
toString 。這個問題還是得拿出剛才那句話——當試圖得到一個對象的某個屬性時,如果
這個對象本身沒有這個屬性,那麼會去它的 __proto__ (即它的構造函數的 prototype )
中尋找。

如果在 f.proto__ 中沒有找到 toString ,那麼就繼續去 f._proto_._proto_ 中尋 找,因為 f.__proto 就是一個普通的對象而已嘛!

f.proto 即 Foo.prototype ,沒有找到 toString ,繼續往上找

f.proto__._proto_` 即 `Foo.prototype._proto_` 。`Foo.prototype` 就是一個普通 的對象,因此 `Foo.prototype.__proto 就是 Object.prototype ,在這裡可以找到toString

因此 f.toString 最終對應到了 Object.prototype.toString

這樣一直往上找,你會發現是一個鏈式的結構,所以叫做「原型鏈」。如果一直找到最上
層都沒有找到,那麼就宣告失敗,返回 undefined 。最上層是什麼 ——Object.prototype.__proto__ === null.原型鏈並不是無限的,原型鏈最終指向null。

參考文章:簡單粗暴地理解js原型鏈--js面向對象編程

微信請點擊閱讀原文更好的查看。

3、作用域和閉包

作用域和閉包是前端面試中,最可能考查的知識點

3-1、作用域

作用域就是一個獨立的地盤,讓變量不會外洩、暴露出去。

變量的作用域無非就是兩種:全局變量和局部變量。

全局作用域:

最外層函數定義的變量擁有全局作用域,即對任何內部函數來說,都是可以訪問的

var outerVar = "outer";
function fn(){
    console.log(outerVar);
}
fn(); // result:outer

局部作用域:

和全局作用域相反,局部作用域一般只在固定的代碼片段內可訪問到,而對於函數外部是無法訪問的,最常見的例如函數內部

function fn(){
    var innerVar = "inner";
}
fn();
console.log(innerVar);  // ReferenceError: innerVar is not defined

這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會放在 (function(){….})()
中。因為放在裡面的所有變量,都不會被外洩和暴露,不會汙染到外面,不會對其他的庫
或者 JS 腳本造成影響。這是函數作用域的一個體現。

注意: ES6 中開始加入了塊級作用域,使用 let 定義變量即可,如下:

if (true) {
 let name = 'Tom'
}
console.log(name) // 報錯,因為let定義的name是在if這個塊級作用域

作用域鏈 
如下代碼中, console.log(a) 要得到 a 變量,但是在當前的作用域中沒有定義 a,一層一層向上尋找,直到找到全局作用域還是沒找到,就宣布放棄。這種一層一層的關係,就是 作用域鏈

var a = 5
function fn() {
 var b = 10
 console.log(a)
 console.log(b)
}
fn()

3-2、 什麼是閉包,如何形成

那麼什麼叫閉包?觀點很多,出現頻率最高的有以下兩個觀點:

function F1() {
 var a = 100
 return function () {
 console.log(a)
 }
}
var f1 = F1()
var a = 200
f1()

閉包主要有兩個應用場景:

函數作為返回值,上面的例子就是

函數作為參數傳遞,看以下例子

function F1() {
 var a = 100
 return function () {
 console.log(a)
 }
}
function F2(f1) {
 var a = 200
 console.log(f1())
}
var f1 = F1()
F2(f1)

關於this對象

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); // result:The Window

this對象是在運行時基於函數的執行環境綁定的:在全局函數中,this等於window,而當函數被作為某個對象調用時,this等於那個對象。不過,匿名函數具有全局性,因此this對象通常指向window。

4、如何理解同步和異步4-1、同步 vs 異步

先看下面的慄子,根據程序閱讀起來表達的意思,應該是先列印 100 ,1秒鐘之後列印
200 ,最後列印 300 。但是實際運行根本不是那麼回事。

console.log(100)
setTimeout(function () {
 console.log(200)
}, 1000)
console.log(300)

再對比以下程序。先列印 100 ,再彈出 200 (等待用戶確認),最後列印 300 。這個運行
效果就符合預期要求。

console.log(100)
alert(200) // 1秒鐘之後點擊確認
console.log(300)

這倆到底有何區別?—— 第一個示例中間的步驟根本沒有阻塞接下來程序的運行,而第二
個示例卻阻塞了後面程序的運行。前面這種表現就叫做 異步(後面這個叫做 同步 ),即
不會阻塞後面程序的運行

4-2、異步和單線程

setTimeout(function(){
 a = false;
}, 100)
while(a){
 console.log('while執行了')
}

因為JS是單線程的,一次只能做一件事情,所以進入while循環之後,沒有「時間」(線程)去跑定時器了,所以這個代碼跑起來是個死循環!

4-3、前端異步的場景描述5、簡單描述一下對 ES6/ES7 的了解5-1、解構賦值

ES6允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構(Destructuring)。

以前,為變量賦值,只能直接指定值。

let a = 1;
let b = 2;
let c = 3;

ES6 允許寫成下面這樣。

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

賦值的代碼大大減少了,不需要分別把變量a,b,c分別聲明定義和賦值,只需要將變量a,b,c作為一個數組的元素,然後將數組[1,2,3]賦值給數組[a,b,c]即可,變量a,b,c即可分別得到對應的值。

1、結構賦值可以嵌套的

let [ a,b,[ c1,c2 ] ] = [ 1,2,[ 3.1,3.2 ] ];
console.log(c1);// c1的值為3.1
console.log(c2);// c2的值為3.2

2、不完全解構

let [a, b, c] = [1, 2];
console.log(a);// a的值為1
console.log(b);// b的值為2

3.解構不成功,變量的值就等於undefined。

let [a,b,c] = [1,2];
console.log(a);// a的值為1
console.log(b);// b的值為2
console.log(c);// 結果:c的值為undefined

4.解構賦值允許指定默認值

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。所以,只有當一個數組成員嚴格等於undefined,默認值才會生效。

對象的解構賦值

var { a,b,c } = {"a":1,"c":3,"b":2};
    console.log(a);//結果:a的值為1
    console.log(b);//結果:b的值為2
    console.log(c);//結果:c的值為3

字符串的解構賦值

var [a,b,c,d,e,f] = "我是一隻小鳥";
    console.log(a);//我
    console.log(b);//是
    console.log(c);//一
    console.log(d);//只
    console.log(e);//小
    console.log(f);//鳥

解構賦值的用途

一、交換變量的值

    var x = 1;
    var y = 2;
    [x,y] = [y,x];

二、提取函數返回的多個值

function demo(){
    return {"name": "張三","age": 21}
}
var {name,age} = demo();
console.log(name);// 結果:張三
console.log(age);// 結果:21

三、定義函數參數

function demo({a,b,c}){
    console.log("姓名:"+ a);
    console.log("身高:"+ b);
    console.log("體重:"+ c);
}
demo({a:"唐三",b:"1.72m",c:"50kg",d:"8000"});
/* 通過這種寫法, 很方便就能提取JSON對象中想要的參數,
例如案例中,我們只需要獲取實參中的:a,b,c,
而不需要關其他的參數,比如:d或者其他更多的參數。*/

四、提取 JSON 數據

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

五、輸入模塊的指定方法

加載模塊時,往往需要指定輸入哪些方法。解構賦值使得輸入語句非常清晰。

const { SourceMapConsumer, SourceNode } = require("source-map");

5-2、Module 的語法

歷史上,JavaScript 一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來。其他語言都有這項功能,比如 Ruby 的require、Python 的import,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支持都沒有,這對開發大型的、複雜的項目形成了巨大障礙。

// CommonJS模塊
let { stat, exists, readFile } = require('fs');

// 等同於
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

上面代碼的實質是整體加載fs模塊(即加載fs的所有方法),生成一個對象(_fs),然後再從這個對象上面讀取 3 個方法。這種加載稱為「運行時加載」,因為只有運行時才能得到這個對象,導致完全沒辦法在編譯時做「靜態優化」。

導出Export:作為一個模塊,它可以選擇性地給其他模塊暴露(提供)自己的屬性和方法,供其他模塊使用。

導入Import:作為一個模塊,可以根據需要,引入其他模塊的提供的屬性或者方法,供自己模塊使用。

模塊化實現

//---module-B.js文件---

//導出變量:name
export var name = "模塊化"; 

模塊B我們使用關鍵字export關鍵字,對外暴露了一個屬性:name的值為:字符串 「模塊化」。一個關鍵字,一句代碼就實現了,是不是很簡單。

//---module-A.js文件---

//導入 模塊B的屬性 name
import { name } from "./module-B.js";
console.log(name)
//列印結果:模塊化

模塊A我們使用關鍵字import導入了模塊B的name屬性,並且賦值給變量name。關鍵字from的作用是指定你想要引入的模塊,我們這裡指定的是module-B.js文件,也就是上面的模塊B。列印結果:「模塊化」正是模塊B的對外暴露的屬性。

5-3、箭頭函數

箭頭函數中的this指向的是定義時的this,而不是執行時的this。

//定義一個對象
var obj = {
    x:100,//屬性x
    show(){
        //延遲500毫秒,輸出x的值
        setTimeout(
        //不同處:箭頭函數
        () => { console.log(this.x)},
        500
        );
    }
};
obj.show();//列印結果:100

當定義obj的show( )方法的時候,我們在箭頭函數編寫this.x,此時的this是指的obj,所以this.x指的是obj.x。而在show()被調用的時候,this依然指向的是被定義時候所指向的對象,也就是obj對象,故列印出:100。

5-4、Promise 對象

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。

ES6 規定,Promise對象是一個構造函數,用來生成Promise實例。

Promise對象有三種狀態:

pending:剛剛創建一個Promise實例的時候,表示初始狀態;

fulfilled:resolve方法調用的時候,表示操作成功;

rejected:reject方法調用的時候,表示操作失敗;

狀態只能從 初始化 -> 成功 或者 初始化 -> 失敗,不能逆向轉換,也不能在成功fulfilled 和失敗rejected之間轉換。

const pro = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

了解了Promise的創建和狀態,我們來學習一個最重要的實例方法:then( )方法。

pro.then(function (res) {
    //操作成功的處理程序
},function (error) {
    //操作失敗的處理程序
});
// 參數是兩個函數,第一個用於處理操作成功後的業務,第二個用於處理操作異常後的業務。

catch( )方法

pro.catch(function (error) {
    //操作失敗的處理程序
});

之所以能夠使用鏈式調用,是因為then方法和catch方法調用後,都會返回promise對象。

如果你之前一點都沒接觸過Promise的話,現在一定很懵逼,沒關係,下面我們用一個案例來串聯前面的知識點,演示一下,認真閱讀注釋:

//用new關鍵字創建一個Promise實例
    let pro = new Promise(function(resolve,reject){
        //假設condition的值為true
        let condition = true;

        if(condition){
            //調用操作成功方法
            resolve('操作成功');
            //狀態:pending->fulfilled
        }else{
            //調用操作異常方法
            reject('操作異常');
            //狀態:pending->rejected
        }
    });

    //用then處理操作成功,catch處理操作異常
    pro.then(function (res) {

        //操作成功的處理程序
        console.log(res)

    }).catch(function (error) {

        //操作失敗的處理程序
        console.log(error)

    });
    //控制臺輸出:操作成功

上面案例的注釋十分詳細,串聯起了上面介紹的所有知識點:創建實例,狀態轉換,then方法和catch方法的使用。

由於我們設置了變量condition的值為true,所以執行後控制臺輸出的結果是:「操作成功」。

上面就是Promise用於處理操作異常的這個過程;但是,正如文章開頭講到的,如果多個操作之間層層依賴,我們用Promise又是怎麼處理的呢?

    let pro = new Promise(function(resolve,reject){

        if(true){
            //調用操作成功方法
            resolve('操作成功');
        }else{
            //調用操作異常方法
            reject('操作異常');
        }
    });

    //用then處理操作成功,catch處理操作異常
    pro.then(requestA)
        .then(requestB)
        .then(requestC)
        .catch(requestError);

    function requestA(){
        console.log('請求A成功');
        return '請求B,下一個就是你了';
    }
    function requestB(res){
        console.log('上一步的結果:'+res);
        console.log('請求B成功');
        return '請求C,下一個就是你了';
    }
    function requestC(res){
        console.log('上一步的結果:'+res);
        console.log('請求C成功');
    }
    function requestError(){
        console.log('請求失敗');
    }

    //列印結果:
    //請求A成功
    //上一步的結果:請求B,下一個就是你了
    //請求B成功
    //上一步的結果:請求C,下一個就是你了
    //請求C成功

案例中,先是創建一個實例,還聲明了4個函數,其中三個是分別代表著請求A,請求B,請求C;有了then方法,三個請求操作再也不用層層嵌套了。我們使用then方法,按照調用順序,很直觀地完成了三個操作的綁定,並且,如果請求B依賴於請求A的結果,那麼,可以在請求A的程序用使用return語句把需要的數據作為參數,傳遞給下一個請求,案例中我們就是使用return實現傳遞參數給下一步操作的。

更直觀的圖解

Promise.all( )方法

Promise.all( )方法:接受一個數組作為參數,數組的元素是Promise實例對象,當參數中的實例對象的狀態都為fulfilled時,Promise.all( )才會有返回。

//創建實例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例1操作成功');
        },5000);
    });

    //創建實例pro2
    let pro2 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例2操作成功');
        },1000);
    });


    Promise.all([pro1,pro2]).then(function(result){
        console.log(result);
    });
    //列印結果:["實例1操作成功", "實例2操作成功"]

Promise.race( )方法

另一個類似的方法是Promise.race()方法:它的參數要求跟Promise.all( )方法一樣,不同的是,它參數中的promise實例,只要有一個狀態發生變化(不管是成功fulfilled還是異常rejected),它就會有返回,其他實例中再發生變化,它也不管了。

//初始化實例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例1操作成功');
        },4000);
    });

    //初始化實例pro2
    let pro2 = new Promise(function(resolve,reject){
        setTimeout(function () {
            reject('實例2操作失敗');
        },2000);
    });

    Promise.race([pro2,pro1]).then(function(result){
        console.log(result);
    }).catch(function(error){
        console.log(error);
    });
    //列印結果:實例2操作失敗

同樣是兩個實例,實例pro1不變,不同的是實例pro2,這次我們調用的是失敗函數reject。

由於pro2實例中2000毫秒之後就執行reject方法,早於實例pro1的4000毫秒,所以最後輸出的是:實例2操作失敗。

以上就是對Promise對象的內容講解,上面提到了一個概念:回調地獄;指的是過多地使用回調函數嵌套,使得調試和維護起來極其的不便。

參考:mdn

參考:教你如何使用ES6的Promise對象

相關焦點

  • 部分常用JavaScript代碼(7)
    /[^ -}]|\s/.test(txt.value))">//76.用javascript判斷文件是否存在function Exists(filespec){if (filespec){ var fso; fso = new ActiveXObject("Scripting.FileSystemObject");
  • 前端-JavaScript基礎知識
    數值範圍:64位浮點數的指數部分的長度是11個二進位位,意味著指數部分的最大值是2047(2的11次方減1)超出的範圍不能表示。NaN是 JavaScript 的特殊值,表示「非數字」(Not a Number),主要出現在將字符串解析成數字出錯的場合。單引號字符串的內部,可以使用雙引號。雙引號字符串的內部,可以使用單引號。
  • 消火栓系統知識點總結第二部分!室內消火栓!
    3月30日的公眾號文章我們複習了消火栓系統第一部分的知識點總結,包括系統的選擇、市政消火栓、室外消火栓。
  • C++基礎知識點整理(一)
    > 一直想嘗試自己動手構建一個簡單的深度學習訓練框架,包括數據讀取與處理、PS、NN前後向傳播、模型save和load、不同訓練方式(offline/online .etc)、指標監控、模型部署等部分
  • JavaScript 模塊化簡析
    JavaScript 基礎  做客戶端的同學對 OC 的 #import "classname"、Swift 的 Module 以及文件修飾符 和 Java 的 import package+class 模式應該都不陌生。我們習慣了引用一個文件就是引用一個類的模式。
  • Javascript碰上Streamlit會擦出怎樣的火花?
    第一個案例實現代碼這裡要用到一個自定義的組件,暫定為mycomponent新建一個文件夾,文件夾內結構如下:(1)文件夾名可以自定義,比如example(2)example文件夾內再新建一個文件夾,命名為:mycomponent這個文件夾內放置兩個文件,init__.py與index.html(3)example文件夾內,mycomponent文件夾外,新建一個文件,命名為javascript_test.py
  • 一次搞懂-JavaScript 模塊化詳解
    這個基本的思想是所有的 JavaScript 模塊系統的基礎。文中代碼案例地址:https://github.com/AnsonZnl/JS-Modules-Sample模塊化的好處JS 中常見的模塊IIFE 模式:匿名函數自調用(閉包)利用閉包的原理創造一個獨有的函數作用域來保存私有變量,達到模塊化的效果。
  • 從零開始手把手教你使用javascript+canvas開發一個塔防遊戲01地圖創建
    </p> </div><script type="text/javascript" src="js/tools.js"></script><script type="text/javascript" src="js/MapData.js"></script>
  • JavaScript 模塊導入的一個小麻煩
    通過使用 IDE 的可能性,例如  ES6 code snippet 插件,你可以部分解決 JavaScript 中命名導入自動完成的問題。總比沒有好。原文連結https://dmitripavlutin.com/javascript-import-module-drawback
  • [JavaScript]部分常用JavaScript代碼(1)
  • JavaScript 數據類型與類型判斷詳解
    基本數據類型值不可變var name = 'javascript';name.toUpperCase(); // 'JAVASCRIPT'console.log(name); // 'javascript'let a = 1console.log(+
  • 眾籌無人機:原理圖、知識點、教學大綱大曝光!
    ■整套無人機視頻教程主要講了哪些知識點?■電路原理圖部分主要涉及到哪些知識要點?■視頻教程硬體部分有沒有課程大綱?■視頻教程軟體部分有沒有課程大綱?■關於軟體方面的視頻課程,聽說張飛老師的講課方式有創新?■學生或新手能不能學會?■老手有沒有必要入手能不能學到自己想要的東西?
  • 【基礎篇】——空中三角測量(一)
    知識點一:空三基本概念 空中三角測量( aerotriangulation
  • 使用JavaScript全局變量繞過XSS
    https://www.javatpoint.com/javascript-global-variable假設目標Web應用程式受到反射到腳本字符串或JavaScript函數中的XSS的攻擊(可以在PortSwigger Web Security Accademy上找到很棒的XSS實驗室,我將使用該實驗室進行一些測試)。
  • 2018年最佳JavaScript數據可視化和圖表庫
    (給前端大全加星標,提升前端技能)英文:Dan Englishby  譯文:眾成翻譯/小生 zcfy.cc/article/the-best-javascript-data-visualization-charting-libraries
  • 9 個出色的 JavaScript 庫推薦
    https://medium.com/better-programming/top-javascript-libraries-you-should-know-about-3b6e22555089
  • 【知識點】高中地理地球自轉知識點匯總!
    一、知識點1、方向:自西向東(如圖A)。(1)從北極上空俯視:呈逆時針方向旋轉(如圖B)。(2)從南極上空俯視:呈順時針方向旋轉(如圖C)。(3)東經度增大的方向,西經度減小的方向即為地球自轉方向。(1)圖中甲為夜半球,乙為晝半球,︵AOB為晨線。
  • JavaScript中判斷對象是否屬於Array類型的4種方法及其背後的原理與局限性
    /iframe.html"></iframe>        <script type="text/javascript">            window.onload = function(){                console.log('document.domain : ' + document.domain);
  • JavaScript初學者應注意的七個細節!
    1 : -1;(2)使用JSON作為數據格式    偉大的Douglas Crockford發明了JSON數據格式來存儲數據,你可以使用原生的javascript方法來存儲複雜的數據而不需要進行任何額外的轉換,例如:1234
  • JavaScript定時器:setTimeout與setInterval 定時器與異步循環數組
    新聞列表滾動、jQuery的.animate()方法就是依靠定時器模擬動畫效果;此博客實現了這一代碼,貼在下面與大家一起討論: http://www.cnblogs.com/slowsoul/archive/2013/02/21/2921354.html   JavaScript——創建運動框架提到定時器,就不得不先介紹一個JavaScript運行機制--》瀏覽器UI線程用於執行javascript