溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript 基礎(chǔ)知識(shí)點(diǎn)

發(fā)布時(shí)間:2020-06-24 09:01:04 來源:網(wǎng)絡(luò) 閱讀:224 作者:可樂程序員 欄目:web開發(fā)

JavaScript (JS) 是一種編程語言,為通常用于客戶端(client-side)的網(wǎng)頁動(dòng)態(tài)腳本,不過,也常通過像Node.js這樣的包,用于 服務(wù)器端(server-side)。

今天,發(fā)一篇關(guān)于Js基礎(chǔ)知識(shí)點(diǎn)的文章,為更多的新人指路??倳?huì)有人在你的前方為你探路,前行之路,你不孤單~

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


先來個(gè)目錄結(jié)構(gòu)

───1、變量聲明
│?└───JavaScript?的數(shù)據(jù)類型分類和判斷
│?└───引用類型和值類型
───2、原型與原型鏈(繼承)
│?└───原型和原型鏈
───3、作用域和閉包
│?└───作用域
│?└───什么是閉包,如何形成?
───4、如何理解同步和異步
│?└───同步?vs?異步
│?└───異步和單線程
│?└───前端異步的場(chǎng)景描述
───5、簡(jiǎn)單描述一下對(duì)?ES6/ES7?的了解
│?└───解構(gòu)賦值
│?└───箭頭函數(shù)
│?└───Promise?對(duì)象
│?└───Set?和?Map?數(shù)據(jù)結(jié)構(gòu)
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


1、變量聲明

1-1、JavaScript 的數(shù)據(jù)類型分類和判斷

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

  • string,

  • number,

  • bigint,

  • boolean,

  • null,

  • undefined,

  • symbol (ECMAScript 2016新增)。

其中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";
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


  • typeof null 結(jié)果是 object ,JavaScript 誕生以來便如此,由于 null 代表的是空指針(大多數(shù)平臺(tái)下值為 0x00),因此,null 的類型標(biāo)簽是 0,typeof null 也因此返回 "object"。

  • typeof [1, 2] 結(jié)果是 object ,結(jié)果中沒有array 這一項(xiàng),引用類型除了function其他的全部都是 object

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

2、instanceof

用于實(shí)例和構(gòu)造函數(shù)的對(duì)應(yīng)。例如判斷一個(gè)變量是否是數(shù)組,使用 typeof 無法判斷,但可 以使用 [1, 2] instanceof Array 來判斷,返回true。因?yàn)椋?[1, 2] 是數(shù)組,它的構(gòu)造函數(shù)就是 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
復(fù)制代碼

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

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

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

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


//?值類型
var?a?=?1;
var?b?=?a;
b?=?3
console.log(a)?//?1
console.log(b)?//?3
//?a?b?都是值類型,兩者分別修改賦值,相互之間沒有任何影響。
復(fù)制代碼
//?引用類型
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}
復(fù)制代碼

a 和 b 都是引用類型。在執(zhí)行了 b = a 之后,修改 b 的屬性值, a 的也跟著 變化。因?yàn)?a 和 b 都是引用類型,指向了同一個(gè)內(nèi)存地址,即兩者引用的是同一個(gè)值,因 此 b 修改屬性時(shí), a 的值隨之改動(dòng)。

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

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

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

在javascript中,函數(shù)可以有屬性。 每個(gè)函數(shù)都有一個(gè)特殊的屬性叫作原型(prototype),正如下面所展示的。請(qǐng)注意,下面的代碼是獨(dú)立的一段(在網(wǎng)頁中沒有其他代碼的情況下,這段代碼是安全的)。為了最好的學(xué)習(xí)體驗(yàn),你最好打開一個(gè)控制臺(tái) (在Chrome和Firefox中,可以按Ctrl+Shift+I 來打開)切換到"Console"選項(xiàng)卡, 復(fù)制粘貼下面的JavaScript代碼,然后按回車來運(yùn)行。

function?doSomething(){}
console.log(?doSomething.prototype?);
//?不管您如何聲明函數(shù),javascript中的函數(shù)總是有一個(gè)默認(rèn)的原型屬性
var?doSomething?=?function(){};?
console.log(?doSomething.prototype?);
復(fù)制代碼

正如上面所看到的, doSomething 函數(shù)有一個(gè)默認(rèn)的原型屬性,它在控制臺(tái)上面呈現(xiàn)了出來. 運(yùn)行這段代碼之后,控制臺(tái)上面應(yīng)該出現(xiàn)了像這樣的一個(gè)對(duì)象.

{
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
}
復(fù)制代碼

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

function?doSomething(){}
doSomething.prototype.foo?=?"bar";
console.log(?doSomething.prototype?);
復(fù)制代碼

輸出:

{
?foo:?"bar",
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
}
復(fù)制代碼

然后,我們可以使用 new 運(yùn)算符來在現(xiàn)在的這個(gè)原型基礎(chǔ)之上,創(chuàng)建一個(gè) doSomething 的實(shí)例。

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?);
復(fù)制代碼

輸出:

{
?prop:?"some?value",
?__proto__:?{
?foo:?"bar",
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
?}
}
復(fù)制代碼

就像上面看到的, doSomeInstancing 的 __proto__ 屬性就是doSomething.prototype. 但是這又有什么用呢? 好吧,當(dāng)你訪問 doSomeInstancing 的一個(gè)屬性, 瀏覽器首先查找 doSomeInstancing 是否有這個(gè)屬性. 如果 doSomeInstancing 沒有這個(gè)屬性, 然后瀏覽器就會(huì)在 doSomeInstancing 的 __proto__ 中查找這個(gè)屬性(也就是 doSomething.prototype). 如果 doSomeInstancing 的 __proto__ 有這個(gè)屬性, 那么 doSomeInstancing 的 __proto__ 上的這個(gè)屬性就會(huì)被使用. 否則, 如果 doSomeInstancing 的 __proto__ 沒有這個(gè)屬性, 瀏覽器就會(huì)去查找 doSomeInstancing 的 __proto__ 的 __proto__ ,看它是否有這個(gè)屬性. 默認(rèn)情況下, 所有函數(shù)的原型屬性的 __proto__ 就是 window.Object.prototype. 所以 doSomeInstancing 的 __proto__ 的 __proto__ (也就是 doSomething.prototype 的 __proto__ (也就是 Object.prototype)) 會(huì)被查找是否有這個(gè)屬性. 如果沒有在它里面找到這個(gè)屬性, 然后就會(huì)在 doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 里面查找. 然而這有一個(gè)問題: doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 不存在. 最后, 原型鏈上面的所有的 __proto__ 都被找完了, 瀏覽器所有已經(jīng)聲明了的 __proto__ 上都不存在這個(gè)屬性,然后就得出結(jié)論,這個(gè)屬性是 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);
復(fù)制代碼

輸出:

doSomeInstancing.prop:?some?value
doSomeInstancing.foo:?bar
doSomething.prop:?undefined
doSomething.foo:?undefined
doSomething.prototype.prop:?undefined
doSomething.prototype.foo:?bar
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


是不是看的頭大了,別擔(dān)心??纯催@個(gè):

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

  • 所有的引用類型(數(shù)組、對(duì)象、函數(shù)),都有一個(gè) __proto__ 屬性,屬性值是一個(gè)普通的對(duì)象

  • 所有的函數(shù),都有一個(gè) prototype 屬性,屬性值也是一個(gè)普通的對(duì)象

  • 所有的引用類型(數(shù)組、對(duì)象、函數(shù)), __proto__ 屬性值指向它的構(gòu)造函數(shù)的prototype 屬性值

//?要點(diǎn)一:自由擴(kuò)展屬性
var?obj?=?{};?obj.a?=?100;
var?arr?=?[];?arr.a?=?100;
function?fn?()?{}
fn.a?=?100;
//?要點(diǎn)二:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
//?要點(diǎn)三:函數(shù)有?prototype
console.log(fn.prototype)
//?要點(diǎn)四:引用類型的?__proto__?屬性值指向它的構(gòu)造函數(shù)的?prototype?屬性值
console.log(obj.__proto__?===?Object.prototype)
復(fù)制代碼

2-1、原型和原型鏈

原型

//?構(gòu)造函數(shù)
function?Foo(name,?age)?{
?this.name?=?name
}
Foo.prototype.alertName?=?function?()?{
?alert(this.name)
}
//?創(chuàng)建示例
var?f?=?new?Foo('zhangsan')
f.printName?=?function?()?{
?console.log(this.name)
}
//?測(cè)試
f.printName()
f.alertName()
復(fù)制代碼

執(zhí)行 printName 時(shí)很好理解,但是執(zhí)行 alertName 時(shí)發(fā)生了什么?這里再記住一個(gè)重點(diǎn) 當(dāng) 試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的 __proto__ (即它的構(gòu)造函數(shù)的 prototype )中尋找,因此 f.alertName 就會(huì)找到 Foo.prototype.alertName 。

那么如何判斷這個(gè)屬性是不是對(duì)象本身的屬性呢?使用 hasOwnProperty ,常用的地方是遍 歷一個(gè)對(duì)象的時(shí)候。

var?item
for?(item?in?f)?{
?//?高級(jí)瀏覽器已經(jīng)在?for?in?中屏蔽了來自原型的屬性,但是這里建議大家還是加上這個(gè)判斷,保證程序正常輸出
?if?(f.hasOwnProperty(item))?{
?console.log(item)
?}
}
復(fù)制代碼

原型鏈

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

f.printName()
復(fù)制代碼

因?yàn)?f 本身沒有 toString() ,并且 f.__proto__ (即 Foo.prototype )中也沒有 toString 。這個(gè)問題還是得拿出剛才那句話——當(dāng)試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果 這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的 __proto__ (即它的構(gòu)造函數(shù)的 prototype ) 中尋找。

如果在 f.proto?中沒有找到 toString ,那么就繼續(xù)去 f.proto.proto?中尋 找,因?yàn)?f.proto就是一個(gè)普通的對(duì)象而已嘛!

  • f.__proto__ 即 Foo.prototype ,沒有找到 toString ,繼續(xù)往上找

  • f.__proto__.__proto__ 即 Foo.prototype.__proto__ 。 Foo.prototype 就是一個(gè)普通 的對(duì)象,因此 Foo.prototype.__proto__ 就是 Object.prototype ,在這里可以找到toString

  • 因此 f.toString 最終對(duì)應(yīng)到了 Object.prototype.toString

這樣一直往上找,你會(huì)發(fā)現(xiàn)是一個(gè)鏈?zhǔn)降慕Y(jié)構(gòu),所以叫做“原型鏈”。如果一直找到最上 層都沒有找到,那么就宣告失敗,返回 undefined 。最上層是什么 ——Object.prototype.__proto__ === null.原型鏈并不是無限的,原型鏈最終指向null。

參考文章:簡(jiǎn)單粗暴地理解js原型鏈--js面向?qū)ο缶幊?/p>

微信請(qǐng)點(diǎn)擊閱讀原文更好的查看。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


3、作用域和閉包

作用域和閉包是前端面試中,最可能考查的知識(shí)點(diǎn)

3-1、作用域

作用域就是一個(gè)獨(dú)立的地盤,讓變量不會(huì)外泄、暴露出去。

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

全局作用域:

最外層函數(shù)定義的變量擁有全局作用域,即對(duì)任何內(nèi)部函數(shù)來說,都是可以訪問的

var?outerVar?=?"outer";
function?fn(){
?console.log(outerVar);
}
fn();?//?result:outer
復(fù)制代碼

局部作用域:

和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,而對(duì)于函數(shù)外部是無法訪問的,最常見的例如函數(shù)內(nèi)部

function?fn(){
?var?innerVar?=?"inner";
}
fn();
console.log(innerVar);?//?ReferenceError:?innerVar?is?not?defined
復(fù)制代碼

這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會(huì)放在 (function(){....})() 中。因?yàn)榉旁诶锩娴乃凶兞?,都不?huì)被外泄和暴露,不會(huì)污染到外面,不會(huì)對(duì)其他的庫 或者 JS 腳本造成影響。這是函數(shù)作用域的一個(gè)體現(xiàn)。

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

if?(true)?{
?let?name?=?'Tom'
}
console.log(name)?//?報(bào)錯(cuò),因?yàn)閘et定義的name是在if這個(gè)塊級(jí)作用域
復(fù)制代碼

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

var?a?=?5
function?fn()?{
?var?b?=?10
?console.log(a)
?console.log(b)
}
fn()
復(fù)制代碼

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

那么什么叫閉包?觀點(diǎn)很多,出現(xiàn)頻率最高的有以下兩個(gè)觀點(diǎn):

  • 函數(shù)套函數(shù)。

  • 在函數(shù)外獲取函數(shù)內(nèi)變量的技術(shù)。

function?F1()?{
?var?a?=?100
?return?function?()?{
?console.log(a)
?}
}
var?f1?=?F1()
var?a?=?200
f1()
復(fù)制代碼

閉包主要有兩個(gè)應(yīng)用場(chǎng)景:

  • 函數(shù)作為返回值,上面的例子就是

  • 函數(shù)作為參數(shù)傳遞,看以下例子

function?F1()?{
?var?a?=?100
?return?function?()?{
?console.log(a)
?}
}
function?F2(f1)?{
?var?a?=?200
?console.log(f1())
}
var?f1?=?F1()
F2(f1)
復(fù)制代碼

關(guān)于this對(duì)象

var?name?=?"The?Window";
var?object?=?{
?name?:?"My?Object",
?getNameFunc?:?function(){
?return?function(){
?return?this.name;
?};
?}
};
alert(object.getNameFunc()());?//?result:The?Window
復(fù)制代碼

this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),this等于那個(gè)對(duì)象。不過,匿名函數(shù)具有全局性,因此this對(duì)象同常指向window。

4、如何理解同步和異步

4-1、同步 vs 異步

先看下面的栗子,根據(jù)程序閱讀起來表達(dá)的意思,應(yīng)該是先打印 100 ,1秒鐘之后打印 200 ,最后打印 300 。但是實(shí)際運(yùn)行根本不是那么回事。

console.log(100)
setTimeout(function?()?{
?console.log(200)
},?1000)
console.log(300)
復(fù)制代碼

再對(duì)比以下程序。先打印 100 ,再彈出 200 (等待用戶確認(rèn)),最后打印 300 。這個(gè)運(yùn)行 效果就符合預(yù)期要求。

console.log(100)
alert(200)?//?1秒鐘之后點(diǎn)擊確認(rèn)
console.log(300)
復(fù)制代碼

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

4-2、異步和單線程

setTimeout(function(){
?a?=?false;
},?100)
while(a){
?console.log('while執(zhí)行了')
}
復(fù)制代碼

因?yàn)镴S是單線程的,一次只能做一件事情,所以進(jìn)入while循環(huán)之后,沒有「時(shí)間」(線程)去跑定時(shí)器了,所以這個(gè)代碼跑起來是個(gè)死循環(huán)!

4-3、前端異步的場(chǎng)景描述

  • 定時(shí)任務(wù):setTimeout, setInterval

  • 綁定事件:addEventListener(click等等)

  • 網(wǎng)絡(luò)請(qǐng)求:ajax和 img 動(dòng)態(tài)加載

5、簡(jiǎn)單描述一下對(duì) ES6/ES7 的了解

5-1、解構(gòu)賦值

ES6允許按照一定模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值,這被稱為解構(gòu)(Destructuring)。

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

let?a?=?1;
let?b?=?2;
let?c?=?3;
復(fù)制代碼

ES6 允許寫成下面這樣。

let?[a,?b,?c]?=?[1,?2,?3];
復(fù)制代碼

賦值的代碼大大減少了,不需要分別把變量a,b,c分別聲明定義和賦值,只需要將變量a,b,c作為一個(gè)數(shù)組的元素,然后將數(shù)組[1,2,3]賦值給數(shù)組[a,b,c]即可,變量a,b,c即可分別得到對(duì)應(yīng)的值。

1、結(jié)構(gòu)賦值可以嵌套的

let?[?a,b,[?c1,c2?]?]?=?[?1,2,[?3.1,3.2?]?];
console.log(c1);//?c1的值為3.1
console.log(c2);//?c2的值為3.2
復(fù)制代碼

2、不完全解構(gòu)

let?[a,?b,?c]?=?[1,?2];
console.log(a);//?a的值為1
console.log(b);//?b的值為2
復(fù)制代碼

3.解構(gòu)不成功,變量的值就等于undefined。

let?[a,b,c]?=?[1,2];
console.log(a);//?a的值為1
console.log(b);//?b的值為2
console.log(c);//?結(jié)果:c的值為undefined
復(fù)制代碼

4.解構(gòu)賦值允許指定默認(rèn)值

let?[foo?=?true]?=?[];
foo?//?true
let?[x,?y?=?'b']?=?['a'];?//?x='a',?y='b'
let?[x,?y?=?'b']?=?['a',?undefined];?//?x='a',?y='b'
復(fù)制代碼

注意,ES6 內(nèi)部使用嚴(yán)格相等運(yùn)算符(===),判斷一個(gè)位置是否有值。所以,只有當(dāng)一個(gè)數(shù)組成員嚴(yán)格等于undefined,默認(rèn)值才會(huì)生效。

對(duì)象的解構(gòu)賦值

var?{?a,b,c?}?=?{"a":1,"c":3,"b":2};
?console.log(a);//結(jié)果:a的值為1
?console.log(b);//結(jié)果:b的值為2
?console.log(c);//結(jié)果:c的值為3
復(fù)制代碼

字符串的解構(gòu)賦值

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);//鳥
復(fù)制代碼

解構(gòu)賦值的用途

一、交換變量的值

?var?x?=?1;
?var?y?=?2;
?[x,y]?=?[y,x];
復(fù)制代碼

二、提取函數(shù)返回的多個(gè)值

function?demo(){
?return?{"name":?"張三","age":?21}
}
var?{name,age}?=?demo();
console.log(name);//?結(jié)果:張三
console.log(age);//?結(jié)果:21
復(fù)制代碼

三、定義函數(shù)參數(shù)

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

四、提取 JSON 數(shù)據(jù)

let?jsonData?=?{
?id:?42,
?status:?"OK",
?data:?[867,?5309]
};
let?{?id,?status,?data:?number?}?=?jsonData;
console.log(id,?status,?number);
//?42,?"OK",?[867,?5309]
復(fù)制代碼

五、輸入模塊的指定方法

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

const?{?SourceMapConsumer,?SourceNode?}?=?require("source-map");
復(fù)制代碼

5-2、Module 的語法

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

//?CommonJS模塊
let?{?stat,?exists,?readFile?}?=?require('fs');
//?等同于
let?_fs?=?require('fs');
let?stat?=?_fs.stat;
let?exists?=?_fs.exists;
let?readfile?=?_fs.readfile;
復(fù)制代碼

上面代碼的實(shí)質(zhì)是整體加載fs模塊(即加載fs的所有方法),生成一個(gè)對(duì)象(_fs),然后再從這個(gè)對(duì)象上面讀取 3 個(gè)方法。這種加載稱為“運(yùn)行時(shí)加載”,因?yàn)橹挥羞\(yùn)行時(shí)才能得到這個(gè)對(duì)象,導(dǎo)致完全沒辦法在編譯時(shí)做“靜態(tài)優(yōu)化”。

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

導(dǎo)入Import:作為一個(gè)模塊,可以根據(jù)需要,引入其他模塊的提供的屬性或者方法,供自己模塊使用。

模塊化實(shí)現(xiàn)

//---module-B.js文件---
//導(dǎo)出變量:name
export?var?name?=?"模塊化";?
復(fù)制代碼

模塊B我們使用關(guān)鍵字export關(guān)鍵字,對(duì)外暴露了一個(gè)屬性:name的值為:字符串 “模塊化”。一個(gè)關(guān)鍵字,一句代碼就實(shí)現(xiàn)了,是不是很簡(jiǎn)單。

//---module-A.js文件---
//導(dǎo)入?模塊B的屬性?name
import?{?name?}?from?"./module-B.js";
console.log(name)
//打印結(jié)果:模塊化
復(fù)制代碼

模塊A我們使用關(guān)鍵字import導(dǎo)入了模塊B的name屬性,并且賦值給變量name。關(guān)鍵字from的作用是指定你想要引入的模塊,我們這里指定的是module-B.js文件,也就是上面的模塊B。打印結(jié)果:“模塊化”正是模塊B的對(duì)外暴露的屬性。

5-3、箭頭函數(shù)

箭頭函數(shù)中的this指向的是定義時(shí)的this,而不是執(zhí)行時(shí)的this。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


//定義一個(gè)對(duì)象
var?obj?=?{
?x:100,//屬性x
?show(){
?//延遲500毫秒,輸出x的值
?setTimeout(
?//不同處:箭頭函數(shù)
?()?=>?{?console.log(this.x)},
?500
?);
?}
};
obj.show();//打印結(jié)果:100
復(fù)制代碼

當(dāng)定義obj的show( )方法的時(shí)候,我們?cè)诩^函數(shù)編寫this.x,此時(shí)的this是指的obj,所以this.x指的是obj.x。而在show()被調(diào)用的時(shí)候,this依然指向的是被定義時(shí)候所指向的對(duì)象,也就是obj對(duì)象,故打印出:100。

5-4、Promise 對(duì)象

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對(duì)象。

ES6 規(guī)定,Promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成Promise實(shí)例。

Promise對(duì)象有三種狀態(tài):

  • pending:剛剛創(chuàng)建一個(gè)Promise實(shí)例的時(shí)候,表示初始狀態(tài);

  • fulfilled:resolve方法調(diào)用的時(shí)候,表示操作成功;

  • rejected:reject方法調(diào)用的時(shí)候,表示操作失??;

狀態(tài)只能從 初始化 -> 成功 或者 初始化 -> 失敗,不能逆向轉(zhuǎn)換,也不能在成功fulfilled 和失敗rejected之間轉(zhuǎn)換。

const?pro?=?new?Promise(function(resolve,?reject)?{
?//?...?some?code
?if?(/*?異步操作成功?*/){
?resolve(value);
?}?else?{
?reject(error);
?}
});
復(fù)制代碼

了解了Promise的創(chuàng)建和狀態(tài),我們來學(xué)習(xí)一個(gè)最重要的實(shí)例方法:then( )方法。

pro.then(function?(res)?{
?//操作成功的處理程序
},function?(error)?{
?//操作失敗的處理程序
});
//?參數(shù)是兩個(gè)函數(shù),第一個(gè)用于處理操作成功后的業(yè)務(wù),第二個(gè)用于處理操作異常后的業(yè)務(wù)。
復(fù)制代碼

catch( )方法

pro.catch(function?(error)?{
?//操作失敗的處理程序
});
復(fù)制代碼

之所以能夠使用鏈?zhǔn)秸{(diào)用,是因?yàn)閠hen方法和catch方法調(diào)用后,都會(huì)返回promise對(duì)象。

如果你之前一點(diǎn)都沒接觸過Promise的話,現(xiàn)在一定很懵逼,沒關(guān)系,下面我們用一個(gè)案例來串聯(lián)前面的知識(shí)點(diǎn),演示一下,認(rèn)真閱讀注釋:

//用new關(guān)鍵字創(chuàng)建一個(gè)Promise實(shí)例
?let?pro?=?new?Promise(function(resolve,reject){
?//假設(shè)condition的值為true
?let?condition?=?true;
?if(condition){
?//調(diào)用操作成功方法
?resolve('操作成功');
?//狀態(tài):pending->fulfilled
?}else{
?//調(diào)用操作異常方法
?reject('操作異常');
?//狀態(tài):pending->rejected
?}
?});
?//用then處理操作成功,catch處理操作異常
?pro.then(function?(res)?{
?//操作成功的處理程序
?console.log(res)
?}).catch(function?(error)?{
?//操作失敗的處理程序
?console.log(error)
?});
?//控制臺(tái)輸出:操作成功
復(fù)制代碼

上面案例的注釋十分詳細(xì),串聯(lián)起了上面介紹的所有知識(shí)點(diǎn):創(chuàng)建實(shí)例,狀態(tài)轉(zhuǎn)換,then方法和catch方法的使用。

由于我們?cè)O(shè)置了變量condition的值為true,所以執(zhí)行后控制臺(tái)輸出的結(jié)果是:“操作成功”。

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

?let?pro?=?new?Promise(function(resolve,reject){
?if(true){
?//調(diào)用操作成功方法
?resolve('操作成功');
?}else{
?//調(diào)用操作異常方法
?reject('操作異常');
?}
?});
?//用then處理操作成功,catch處理操作異常
?pro.then(requestA)
?.then(requestB)
?.then(requestC)
?.catch(requestError);
?function?requestA(){
?console.log('請(qǐng)求A成功');
?return?'請(qǐng)求B,下一個(gè)就是你了';
?}
?function?requestB(res){
?console.log('上一步的結(jié)果:'+res);
?console.log('請(qǐng)求B成功');
?return?'請(qǐng)求C,下一個(gè)就是你了';
?}
?function?requestC(res){
?console.log('上一步的結(jié)果:'+res);
?console.log('請(qǐng)求C成功');
?}
?function?requestError(){
?console.log('請(qǐng)求失敗');
?}
?//打印結(jié)果:
?//請(qǐng)求A成功
?//上一步的結(jié)果:請(qǐng)求B,下一個(gè)就是你了
?//請(qǐng)求B成功
?//上一步的結(jié)果:請(qǐng)求C,下一個(gè)就是你了
?//請(qǐng)求C成功
復(fù)制代碼

案例中,先是創(chuàng)建一個(gè)實(shí)例,還聲明了4個(gè)函數(shù),其中三個(gè)是分別代表著請(qǐng)求A,請(qǐng)求B,請(qǐng)求C;有了then方法,三個(gè)請(qǐng)求操作再也不用層層嵌套了。我們使用then方法,按照調(diào)用順序,很直觀地完成了三個(gè)操作的綁定,并且,如果請(qǐng)求B依賴于請(qǐng)求A的結(jié)果,那么,可以在請(qǐng)求A的程序用使用return語句把需要的數(shù)據(jù)作為參數(shù),傳遞給下一個(gè)請(qǐng)求,案例中我們就是使用return實(shí)現(xiàn)傳遞參數(shù)給下一步操作的。

更直觀的圖解

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


Promise.all( )方法

Promise.all( )方法:接受一個(gè)數(shù)組作為參數(shù),數(shù)組的元素是Promise實(shí)例對(duì)象,當(dāng)參數(shù)中的實(shí)例對(duì)象的狀態(tài)都為fulfilled時(shí),Promise.all( )才會(huì)有返回。

//創(chuàng)建實(shí)例pro1
?let?pro1?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例1操作成功');
?},5000);
?});
?
?//創(chuàng)建實(shí)例pro2
?let?pro2?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例2操作成功');
?},1000);
?});
?
?Promise.all([pro1,pro2]).then(function(result){
?console.log(result);
?});
?//打印結(jié)果:["實(shí)例1操作成功",?"實(shí)例2操作成功"]
復(fù)制代碼

Promise.race( )方法

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

//初始化實(shí)例pro1
?let?pro1?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例1操作成功');
?},4000);
?});
?//初始化實(shí)例pro2
?let?pro2?=?new?Promise(function(resolve,reject){
?setTimeout(function?()?{
?reject('實(shí)例2操作失敗');
?},2000);
?});
?Promise.race([pro2,pro1]).then(function(result){
?console.log(result);
?}).catch(function(error){
?console.log(error);
?});
?//打印結(jié)果:實(shí)例2操作失敗
復(fù)制代碼

同樣是兩個(gè)實(shí)例,實(shí)例pro1不變,不同的是實(shí)例pro2,這次我們調(diào)用的是失敗函數(shù)reject。

由于pro2實(shí)例中2000毫秒之后就執(zhí)行reject方法,早于實(shí)例pro1的4000毫秒,所以最后輸出的是:實(shí)例2操作失敗。

以上就是對(duì)Promise對(duì)象的內(nèi)容講解,上面提到了一個(gè)概念:回調(diào)地獄;指的是過多地使用回調(diào)函數(shù)嵌套,使得調(diào)試和維護(hù)起來極其的不便。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)

JavaScript (JS) 是一種編程語言,為通常用于客戶端(client-side)的網(wǎng)頁動(dòng)態(tài)腳本,不過,也常通過像Node.js這樣的包,用于 服務(wù)器端(server-side)。

今天,發(fā)一篇關(guān)于Js基礎(chǔ)知識(shí)點(diǎn)的文章,為更多的新人指路??倳?huì)有人在你的前方為你探路,前行之路,你不孤單~

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


先來個(gè)目錄結(jié)構(gòu)

───1、變量聲明
│?└───JavaScript?的數(shù)據(jù)類型分類和判斷
│?└───引用類型和值類型
───2、原型與原型鏈(繼承)
│?└───原型和原型鏈
───3、作用域和閉包
│?└───作用域
│?└───什么是閉包,如何形成?
───4、如何理解同步和異步
│?└───同步?vs?異步
│?└───異步和單線程
│?└───前端異步的場(chǎng)景描述
───5、簡(jiǎn)單描述一下對(duì)?ES6/ES7?的了解
│?└───解構(gòu)賦值
│?└───箭頭函數(shù)
│?└───Promise?對(duì)象
│?└───Set?和?Map?數(shù)據(jù)結(jié)構(gòu)
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


1、變量聲明

1-1、JavaScript 的數(shù)據(jù)類型分類和判斷

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

  • string,

  • number,

  • bigint,

  • boolean,

  • null,

  • undefined,

  • symbol (ECMAScript 2016新增)。

其中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";
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


  • typeof null 結(jié)果是 object ,JavaScript 誕生以來便如此,由于 null 代表的是空指針(大多數(shù)平臺(tái)下值為 0x00),因此,null 的類型標(biāo)簽是 0,typeof null 也因此返回 "object"。

  • typeof [1, 2] 結(jié)果是 object ,結(jié)果中沒有array 這一項(xiàng),引用類型除了function其他的全部都是 object

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

2、instanceof

用于實(shí)例和構(gòu)造函數(shù)的對(duì)應(yīng)。例如判斷一個(gè)變量是否是數(shù)組,使用 typeof 無法判斷,但可 以使用 [1, 2] instanceof Array 來判斷,返回true。因?yàn)椋?[1, 2] 是數(shù)組,它的構(gòu)造函數(shù)就是 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
復(fù)制代碼

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

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

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

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


//?值類型
var?a?=?1;
var?b?=?a;
b?=?3
console.log(a)?//?1
console.log(b)?//?3
//?a?b?都是值類型,兩者分別修改賦值,相互之間沒有任何影響。
復(fù)制代碼
//?引用類型
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}
復(fù)制代碼

a 和 b 都是引用類型。在執(zhí)行了 b = a 之后,修改 b 的屬性值, a 的也跟著 變化。因?yàn)?a 和 b 都是引用類型,指向了同一個(gè)內(nèi)存地址,即兩者引用的是同一個(gè)值,因 此 b 修改屬性時(shí), a 的值隨之改動(dòng)。

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

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

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

在javascript中,函數(shù)可以有屬性。 每個(gè)函數(shù)都有一個(gè)特殊的屬性叫作原型(prototype),正如下面所展示的。請(qǐng)注意,下面的代碼是獨(dú)立的一段(在網(wǎng)頁中沒有其他代碼的情況下,這段代碼是安全的)。為了最好的學(xué)習(xí)體驗(yàn),你最好打開一個(gè)控制臺(tái) (在Chrome和Firefox中,可以按Ctrl+Shift+I 來打開)切換到"Console"選項(xiàng)卡, 復(fù)制粘貼下面的JavaScript代碼,然后按回車來運(yùn)行。

function?doSomething(){}
console.log(?doSomething.prototype?);
//?不管您如何聲明函數(shù),javascript中的函數(shù)總是有一個(gè)默認(rèn)的原型屬性
var?doSomething?=?function(){};?
console.log(?doSomething.prototype?);
復(fù)制代碼

正如上面所看到的, doSomething 函數(shù)有一個(gè)默認(rèn)的原型屬性,它在控制臺(tái)上面呈現(xiàn)了出來. 運(yùn)行這段代碼之后,控制臺(tái)上面應(yīng)該出現(xiàn)了像這樣的一個(gè)對(duì)象.

{
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
}
復(fù)制代碼

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

function?doSomething(){}
doSomething.prototype.foo?=?"bar";
console.log(?doSomething.prototype?);
復(fù)制代碼

輸出:

{
?foo:?"bar",
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
}
復(fù)制代碼

然后,我們可以使用 new 運(yùn)算符來在現(xiàn)在的這個(gè)原型基礎(chǔ)之上,創(chuàng)建一個(gè) doSomething 的實(shí)例。

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?);
復(fù)制代碼

輸出:

{
?prop:?"some?value",
?__proto__:?{
?foo:?"bar",
?constructor:???doSomething(),
?__proto__:?{
?constructor:???Object(),
?hasOwnProperty:???`hasOwnProperty`(),
?isPrototypeOf:???`isPrototypeOf`(),
?propertyIsEnumerable:???`propertyIsEnumerable`(),
?toLocaleString:???`toLocaleString`(),
?toString:???`toString`(),
?valueOf:???`valueOf`()
?}
?}
}
復(fù)制代碼

就像上面看到的, doSomeInstancing 的 __proto__ 屬性就是doSomething.prototype. 但是這又有什么用呢? 好吧,當(dāng)你訪問 doSomeInstancing 的一個(gè)屬性, 瀏覽器首先查找 doSomeInstancing 是否有這個(gè)屬性. 如果 doSomeInstancing 沒有這個(gè)屬性, 然后瀏覽器就會(huì)在 doSomeInstancing 的 __proto__ 中查找這個(gè)屬性(也就是 doSomething.prototype). 如果 doSomeInstancing 的 __proto__ 有這個(gè)屬性, 那么 doSomeInstancing 的 __proto__ 上的這個(gè)屬性就會(huì)被使用. 否則, 如果 doSomeInstancing 的 __proto__ 沒有這個(gè)屬性, 瀏覽器就會(huì)去查找 doSomeInstancing 的 __proto__ 的 __proto__ ,看它是否有這個(gè)屬性. 默認(rèn)情況下, 所有函數(shù)的原型屬性的 __proto__ 就是 window.Object.prototype. 所以 doSomeInstancing 的 __proto__ 的 __proto__ (也就是 doSomething.prototype 的 __proto__ (也就是 Object.prototype)) 會(huì)被查找是否有這個(gè)屬性. 如果沒有在它里面找到這個(gè)屬性, 然后就會(huì)在 doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 里面查找. 然而這有一個(gè)問題: doSomeInstancing 的 __proto__ 的 __proto__ 的 __proto__ 不存在. 最后, 原型鏈上面的所有的 __proto__ 都被找完了, 瀏覽器所有已經(jīng)聲明了的 __proto__ 上都不存在這個(gè)屬性,然后就得出結(jié)論,這個(gè)屬性是 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);
復(fù)制代碼

輸出:

doSomeInstancing.prop:?some?value
doSomeInstancing.foo:?bar
doSomething.prop:?undefined
doSomething.foo:?undefined
doSomething.prototype.prop:?undefined
doSomething.prototype.foo:?bar
復(fù)制代碼

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


是不是看的頭大了,別擔(dān)心??纯催@個(gè):

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

  • 所有的引用類型(數(shù)組、對(duì)象、函數(shù)),都有一個(gè) __proto__ 屬性,屬性值是一個(gè)普通的對(duì)象

  • 所有的函數(shù),都有一個(gè) prototype 屬性,屬性值也是一個(gè)普通的對(duì)象

  • 所有的引用類型(數(shù)組、對(duì)象、函數(shù)), __proto__ 屬性值指向它的構(gòu)造函數(shù)的prototype 屬性值

//?要點(diǎn)一:自由擴(kuò)展屬性
var?obj?=?{};?obj.a?=?100;
var?arr?=?[];?arr.a?=?100;
function?fn?()?{}
fn.a?=?100;
//?要點(diǎn)二:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
//?要點(diǎn)三:函數(shù)有?prototype
console.log(fn.prototype)
//?要點(diǎn)四:引用類型的?__proto__?屬性值指向它的構(gòu)造函數(shù)的?prototype?屬性值
console.log(obj.__proto__?===?Object.prototype)
復(fù)制代碼

2-1、原型和原型鏈

原型

//?構(gòu)造函數(shù)
function?Foo(name,?age)?{
?this.name?=?name
}
Foo.prototype.alertName?=?function?()?{
?alert(this.name)
}
//?創(chuàng)建示例
var?f?=?new?Foo('zhangsan')
f.printName?=?function?()?{
?console.log(this.name)
}
//?測(cè)試
f.printName()
f.alertName()
復(fù)制代碼

執(zhí)行 printName 時(shí)很好理解,但是執(zhí)行 alertName 時(shí)發(fā)生了什么?這里再記住一個(gè)重點(diǎn) 當(dāng) 試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的 __proto__ (即它的構(gòu)造函數(shù)的 prototype )中尋找,因此 f.alertName 就會(huì)找到 Foo.prototype.alertName 。

那么如何判斷這個(gè)屬性是不是對(duì)象本身的屬性呢?使用 hasOwnProperty ,常用的地方是遍 歷一個(gè)對(duì)象的時(shí)候。

var?item
for?(item?in?f)?{
?//?高級(jí)瀏覽器已經(jīng)在?for?in?中屏蔽了來自原型的屬性,但是這里建議大家還是加上這個(gè)判斷,保證程序正常輸出
?if?(f.hasOwnProperty(item))?{
?console.log(item)
?}
}
復(fù)制代碼

原型鏈

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

f.printName()
復(fù)制代碼

因?yàn)?f 本身沒有 toString() ,并且 f.__proto__ (即 Foo.prototype )中也沒有 toString 。這個(gè)問題還是得拿出剛才那句話——當(dāng)試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果 這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的 __proto__ (即它的構(gòu)造函數(shù)的 prototype ) 中尋找。

如果在 f.proto?中沒有找到 toString ,那么就繼續(xù)去 f.proto.proto?中尋 找,因?yàn)?f.proto就是一個(gè)普通的對(duì)象而已嘛!

  • f.__proto__ 即 Foo.prototype ,沒有找到 toString ,繼續(xù)往上找

  • f.__proto__.__proto__ 即 Foo.prototype.__proto__ 。 Foo.prototype 就是一個(gè)普通 的對(duì)象,因此 Foo.prototype.__proto__ 就是 Object.prototype ,在這里可以找到toString

  • 因此 f.toString 最終對(duì)應(yīng)到了 Object.prototype.toString

這樣一直往上找,你會(huì)發(fā)現(xiàn)是一個(gè)鏈?zhǔn)降慕Y(jié)構(gòu),所以叫做“原型鏈”。如果一直找到最上 層都沒有找到,那么就宣告失敗,返回 undefined 。最上層是什么 ——Object.prototype.__proto__ === null.原型鏈并不是無限的,原型鏈最終指向null。

參考文章:簡(jiǎn)單粗暴地理解js原型鏈--js面向?qū)ο缶幊?/p>

微信請(qǐng)點(diǎn)擊閱讀原文更好的查看。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


3、作用域和閉包

作用域和閉包是前端面試中,最可能考查的知識(shí)點(diǎn)

3-1、作用域

作用域就是一個(gè)獨(dú)立的地盤,讓變量不會(huì)外泄、暴露出去。

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

全局作用域:

最外層函數(shù)定義的變量擁有全局作用域,即對(duì)任何內(nèi)部函數(shù)來說,都是可以訪問的

var?outerVar?=?"outer";
function?fn(){
?console.log(outerVar);
}
fn();?//?result:outer
復(fù)制代碼

局部作用域:

和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,而對(duì)于函數(shù)外部是無法訪問的,最常見的例如函數(shù)內(nèi)部

function?fn(){
?var?innerVar?=?"inner";
}
fn();
console.log(innerVar);?//?ReferenceError:?innerVar?is?not?defined
復(fù)制代碼

這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會(huì)放在 (function(){....})() 中。因?yàn)榉旁诶锩娴乃凶兞?,都不?huì)被外泄和暴露,不會(huì)污染到外面,不會(huì)對(duì)其他的庫 或者 JS 腳本造成影響。這是函數(shù)作用域的一個(gè)體現(xiàn)。

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

if?(true)?{
?let?name?=?'Tom'
}
console.log(name)?//?報(bào)錯(cuò),因?yàn)閘et定義的name是在if這個(gè)塊級(jí)作用域
復(fù)制代碼

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

var?a?=?5
function?fn()?{
?var?b?=?10
?console.log(a)
?console.log(b)
}
fn()
復(fù)制代碼

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

那么什么叫閉包?觀點(diǎn)很多,出現(xiàn)頻率最高的有以下兩個(gè)觀點(diǎn):

  • 函數(shù)套函數(shù)。

  • 在函數(shù)外獲取函數(shù)內(nèi)變量的技術(shù)。

function?F1()?{
?var?a?=?100
?return?function?()?{
?console.log(a)
?}
}
var?f1?=?F1()
var?a?=?200
f1()
復(fù)制代碼

閉包主要有兩個(gè)應(yīng)用場(chǎng)景:

  • 函數(shù)作為返回值,上面的例子就是

  • 函數(shù)作為參數(shù)傳遞,看以下例子

function?F1()?{
?var?a?=?100
?return?function?()?{
?console.log(a)
?}
}
function?F2(f1)?{
?var?a?=?200
?console.log(f1())
}
var?f1?=?F1()
F2(f1)
復(fù)制代碼

關(guān)于this對(duì)象

var?name?=?"The?Window";
var?object?=?{
?name?:?"My?Object",
?getNameFunc?:?function(){
?return?function(){
?return?this.name;
?};
?}
};
alert(object.getNameFunc()());?//?result:The?Window
復(fù)制代碼

this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),this等于那個(gè)對(duì)象。不過,匿名函數(shù)具有全局性,因此this對(duì)象同常指向window。

4、如何理解同步和異步

4-1、同步 vs 異步

先看下面的栗子,根據(jù)程序閱讀起來表達(dá)的意思,應(yīng)該是先打印 100 ,1秒鐘之后打印 200 ,最后打印 300 。但是實(shí)際運(yùn)行根本不是那么回事。

console.log(100)
setTimeout(function?()?{
?console.log(200)
},?1000)
console.log(300)
復(fù)制代碼

再對(duì)比以下程序。先打印 100 ,再彈出 200 (等待用戶確認(rèn)),最后打印 300 。這個(gè)運(yùn)行 效果就符合預(yù)期要求。

console.log(100)
alert(200)?//?1秒鐘之后點(diǎn)擊確認(rèn)
console.log(300)
復(fù)制代碼

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

4-2、異步和單線程

setTimeout(function(){
?a?=?false;
},?100)
while(a){
?console.log('while執(zhí)行了')
}
復(fù)制代碼

因?yàn)镴S是單線程的,一次只能做一件事情,所以進(jìn)入while循環(huán)之后,沒有「時(shí)間」(線程)去跑定時(shí)器了,所以這個(gè)代碼跑起來是個(gè)死循環(huán)!

4-3、前端異步的場(chǎng)景描述

  • 定時(shí)任務(wù):setTimeout, setInterval

  • 綁定事件:addEventListener(click等等)

  • 網(wǎng)絡(luò)請(qǐng)求:ajax和 img 動(dòng)態(tài)加載

5、簡(jiǎn)單描述一下對(duì) ES6/ES7 的了解

5-1、解構(gòu)賦值

ES6允許按照一定模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值,這被稱為解構(gòu)(Destructuring)。

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

let?a?=?1;
let?b?=?2;
let?c?=?3;
復(fù)制代碼

ES6 允許寫成下面這樣。

let?[a,?b,?c]?=?[1,?2,?3];
復(fù)制代碼

賦值的代碼大大減少了,不需要分別把變量a,b,c分別聲明定義和賦值,只需要將變量a,b,c作為一個(gè)數(shù)組的元素,然后將數(shù)組[1,2,3]賦值給數(shù)組[a,b,c]即可,變量a,b,c即可分別得到對(duì)應(yīng)的值。

1、結(jié)構(gòu)賦值可以嵌套的

let?[?a,b,[?c1,c2?]?]?=?[?1,2,[?3.1,3.2?]?];
console.log(c1);//?c1的值為3.1
console.log(c2);//?c2的值為3.2
復(fù)制代碼

2、不完全解構(gòu)

let?[a,?b,?c]?=?[1,?2];
console.log(a);//?a的值為1
console.log(b);//?b的值為2
復(fù)制代碼

3.解構(gòu)不成功,變量的值就等于undefined。

let?[a,b,c]?=?[1,2];
console.log(a);//?a的值為1
console.log(b);//?b的值為2
console.log(c);//?結(jié)果:c的值為undefined
復(fù)制代碼

4.解構(gòu)賦值允許指定默認(rèn)值

let?[foo?=?true]?=?[];
foo?//?true
let?[x,?y?=?'b']?=?['a'];?//?x='a',?y='b'
let?[x,?y?=?'b']?=?['a',?undefined];?//?x='a',?y='b'
復(fù)制代碼

注意,ES6 內(nèi)部使用嚴(yán)格相等運(yùn)算符(===),判斷一個(gè)位置是否有值。所以,只有當(dāng)一個(gè)數(shù)組成員嚴(yán)格等于undefined,默認(rèn)值才會(huì)生效。

對(duì)象的解構(gòu)賦值

var?{?a,b,c?}?=?{"a":1,"c":3,"b":2};
?console.log(a);//結(jié)果:a的值為1
?console.log(b);//結(jié)果:b的值為2
?console.log(c);//結(jié)果:c的值為3
復(fù)制代碼

字符串的解構(gòu)賦值

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);//鳥
復(fù)制代碼

解構(gòu)賦值的用途

一、交換變量的值

?var?x?=?1;
?var?y?=?2;
?[x,y]?=?[y,x];
復(fù)制代碼

二、提取函數(shù)返回的多個(gè)值

function?demo(){
?return?{"name":?"張三","age":?21}
}
var?{name,age}?=?demo();
console.log(name);//?結(jié)果:張三
console.log(age);//?結(jié)果:21
復(fù)制代碼

三、定義函數(shù)參數(shù)

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

四、提取 JSON 數(shù)據(jù)

let?jsonData?=?{
?id:?42,
?status:?"OK",
?data:?[867,?5309]
};
let?{?id,?status,?data:?number?}?=?jsonData;
console.log(id,?status,?number);
//?42,?"OK",?[867,?5309]
復(fù)制代碼

五、輸入模塊的指定方法

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

const?{?SourceMapConsumer,?SourceNode?}?=?require("source-map");
復(fù)制代碼

5-2、Module 的語法

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

//?CommonJS模塊
let?{?stat,?exists,?readFile?}?=?require('fs');
//?等同于
let?_fs?=?require('fs');
let?stat?=?_fs.stat;
let?exists?=?_fs.exists;
let?readfile?=?_fs.readfile;
復(fù)制代碼

上面代碼的實(shí)質(zhì)是整體加載fs模塊(即加載fs的所有方法),生成一個(gè)對(duì)象(_fs),然后再從這個(gè)對(duì)象上面讀取 3 個(gè)方法。這種加載稱為“運(yùn)行時(shí)加載”,因?yàn)橹挥羞\(yùn)行時(shí)才能得到這個(gè)對(duì)象,導(dǎo)致完全沒辦法在編譯時(shí)做“靜態(tài)優(yōu)化”。

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

導(dǎo)入Import:作為一個(gè)模塊,可以根據(jù)需要,引入其他模塊的提供的屬性或者方法,供自己模塊使用。

模塊化實(shí)現(xiàn)

//---module-B.js文件---
//導(dǎo)出變量:name
export?var?name?=?"模塊化";?
復(fù)制代碼

模塊B我們使用關(guān)鍵字export關(guān)鍵字,對(duì)外暴露了一個(gè)屬性:name的值為:字符串 “模塊化”。一個(gè)關(guān)鍵字,一句代碼就實(shí)現(xiàn)了,是不是很簡(jiǎn)單。

//---module-A.js文件---
//導(dǎo)入?模塊B的屬性?name
import?{?name?}?from?"./module-B.js";
console.log(name)
//打印結(jié)果:模塊化
復(fù)制代碼

模塊A我們使用關(guān)鍵字import導(dǎo)入了模塊B的name屬性,并且賦值給變量name。關(guān)鍵字from的作用是指定你想要引入的模塊,我們這里指定的是module-B.js文件,也就是上面的模塊B。打印結(jié)果:“模塊化”正是模塊B的對(duì)外暴露的屬性。

5-3、箭頭函數(shù)

箭頭函數(shù)中的this指向的是定義時(shí)的this,而不是執(zhí)行時(shí)的this。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


//定義一個(gè)對(duì)象
var?obj?=?{
?x:100,//屬性x
?show(){
?//延遲500毫秒,輸出x的值
?setTimeout(
?//不同處:箭頭函數(shù)
?()?=>?{?console.log(this.x)},
?500
?);
?}
};
obj.show();//打印結(jié)果:100
復(fù)制代碼

當(dāng)定義obj的show( )方法的時(shí)候,我們?cè)诩^函數(shù)編寫this.x,此時(shí)的this是指的obj,所以this.x指的是obj.x。而在show()被調(diào)用的時(shí)候,this依然指向的是被定義時(shí)候所指向的對(duì)象,也就是obj對(duì)象,故打印出:100。

5-4、Promise 對(duì)象

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對(duì)象。

ES6 規(guī)定,Promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成Promise實(shí)例。

Promise對(duì)象有三種狀態(tài):

  • pending:剛剛創(chuàng)建一個(gè)Promise實(shí)例的時(shí)候,表示初始狀態(tài);

  • fulfilled:resolve方法調(diào)用的時(shí)候,表示操作成功;

  • rejected:reject方法調(diào)用的時(shí)候,表示操作失??;

狀態(tài)只能從 初始化 -> 成功 或者 初始化 -> 失敗,不能逆向轉(zhuǎn)換,也不能在成功fulfilled 和失敗rejected之間轉(zhuǎn)換。

const?pro?=?new?Promise(function(resolve,?reject)?{
?//?...?some?code
?if?(/*?異步操作成功?*/){
?resolve(value);
?}?else?{
?reject(error);
?}
});
復(fù)制代碼

了解了Promise的創(chuàng)建和狀態(tài),我們來學(xué)習(xí)一個(gè)最重要的實(shí)例方法:then( )方法。

pro.then(function?(res)?{
?//操作成功的處理程序
},function?(error)?{
?//操作失敗的處理程序
});
//?參數(shù)是兩個(gè)函數(shù),第一個(gè)用于處理操作成功后的業(yè)務(wù),第二個(gè)用于處理操作異常后的業(yè)務(wù)。
復(fù)制代碼

catch( )方法

pro.catch(function?(error)?{
?//操作失敗的處理程序
});
復(fù)制代碼

之所以能夠使用鏈?zhǔn)秸{(diào)用,是因?yàn)閠hen方法和catch方法調(diào)用后,都會(huì)返回promise對(duì)象。

如果你之前一點(diǎn)都沒接觸過Promise的話,現(xiàn)在一定很懵逼,沒關(guān)系,下面我們用一個(gè)案例來串聯(lián)前面的知識(shí)點(diǎn),演示一下,認(rèn)真閱讀注釋:

//用new關(guān)鍵字創(chuàng)建一個(gè)Promise實(shí)例
?let?pro?=?new?Promise(function(resolve,reject){
?//假設(shè)condition的值為true
?let?condition?=?true;
?if(condition){
?//調(diào)用操作成功方法
?resolve('操作成功');
?//狀態(tài):pending->fulfilled
?}else{
?//調(diào)用操作異常方法
?reject('操作異常');
?//狀態(tài):pending->rejected
?}
?});
?//用then處理操作成功,catch處理操作異常
?pro.then(function?(res)?{
?//操作成功的處理程序
?console.log(res)
?}).catch(function?(error)?{
?//操作失敗的處理程序
?console.log(error)
?});
?//控制臺(tái)輸出:操作成功
復(fù)制代碼

上面案例的注釋十分詳細(xì),串聯(lián)起了上面介紹的所有知識(shí)點(diǎn):創(chuàng)建實(shí)例,狀態(tài)轉(zhuǎn)換,then方法和catch方法的使用。

由于我們?cè)O(shè)置了變量condition的值為true,所以執(zhí)行后控制臺(tái)輸出的結(jié)果是:“操作成功”。

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

?let?pro?=?new?Promise(function(resolve,reject){
?if(true){
?//調(diào)用操作成功方法
?resolve('操作成功');
?}else{
?//調(diào)用操作異常方法
?reject('操作異常');
?}
?});
?//用then處理操作成功,catch處理操作異常
?pro.then(requestA)
?.then(requestB)
?.then(requestC)
?.catch(requestError);
?function?requestA(){
?console.log('請(qǐng)求A成功');
?return?'請(qǐng)求B,下一個(gè)就是你了';
?}
?function?requestB(res){
?console.log('上一步的結(jié)果:'+res);
?console.log('請(qǐng)求B成功');
?return?'請(qǐng)求C,下一個(gè)就是你了';
?}
?function?requestC(res){
?console.log('上一步的結(jié)果:'+res);
?console.log('請(qǐng)求C成功');
?}
?function?requestError(){
?console.log('請(qǐng)求失敗');
?}
?//打印結(jié)果:
?//請(qǐng)求A成功
?//上一步的結(jié)果:請(qǐng)求B,下一個(gè)就是你了
?//請(qǐng)求B成功
?//上一步的結(jié)果:請(qǐng)求C,下一個(gè)就是你了
?//請(qǐng)求C成功
復(fù)制代碼

案例中,先是創(chuàng)建一個(gè)實(shí)例,還聲明了4個(gè)函數(shù),其中三個(gè)是分別代表著請(qǐng)求A,請(qǐng)求B,請(qǐng)求C;有了then方法,三個(gè)請(qǐng)求操作再也不用層層嵌套了。我們使用then方法,按照調(diào)用順序,很直觀地完成了三個(gè)操作的綁定,并且,如果請(qǐng)求B依賴于請(qǐng)求A的結(jié)果,那么,可以在請(qǐng)求A的程序用使用return語句把需要的數(shù)據(jù)作為參數(shù),傳遞給下一個(gè)請(qǐng)求,案例中我們就是使用return實(shí)現(xiàn)傳遞參數(shù)給下一步操作的。

更直觀的圖解

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


Promise.all( )方法

Promise.all( )方法:接受一個(gè)數(shù)組作為參數(shù),數(shù)組的元素是Promise實(shí)例對(duì)象,當(dāng)參數(shù)中的實(shí)例對(duì)象的狀態(tài)都為fulfilled時(shí),Promise.all( )才會(huì)有返回。

//創(chuàng)建實(shí)例pro1
?let?pro1?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例1操作成功');
?},5000);
?});
?
?//創(chuàng)建實(shí)例pro2
?let?pro2?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例2操作成功');
?},1000);
?});
?
?Promise.all([pro1,pro2]).then(function(result){
?console.log(result);
?});
?//打印結(jié)果:["實(shí)例1操作成功",?"實(shí)例2操作成功"]
復(fù)制代碼

Promise.race( )方法

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

//初始化實(shí)例pro1
?let?pro1?=?new?Promise(function(resolve){
?setTimeout(function?()?{
?resolve('實(shí)例1操作成功');
?},4000);
?});
?//初始化實(shí)例pro2
?let?pro2?=?new?Promise(function(resolve,reject){
?setTimeout(function?()?{
?reject('實(shí)例2操作失敗');
?},2000);
?});
?Promise.race([pro2,pro1]).then(function(result){
?console.log(result);
?}).catch(function(error){
?console.log(error);
?});
?//打印結(jié)果:實(shí)例2操作失敗
復(fù)制代碼

同樣是兩個(gè)實(shí)例,實(shí)例pro1不變,不同的是實(shí)例pro2,這次我們調(diào)用的是失敗函數(shù)reject。

由于pro2實(shí)例中2000毫秒之后就執(zhí)行reject方法,早于實(shí)例pro1的4000毫秒,所以最后輸出的是:實(shí)例2操作失敗。

以上就是對(duì)Promise對(duì)象的內(nèi)容講解,上面提到了一個(gè)概念:回調(diào)地獄;指的是過多地使用回調(diào)函數(shù)嵌套,使得調(diào)試和維護(hù)起來極其的不便。

JavaScript 基礎(chǔ)知識(shí)點(diǎn)


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI