欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

現(xiàn)代 JavaScript 參考

 更新時(shí)間:2017年10月21日 15:19:40   投稿:mdxy-dxy  
一份現(xiàn)代 JavaScript 參考,你在現(xiàn)代項(xiàng)目中會(huì)經(jīng)常遇到,以及最新的代碼示例,需要的朋友可以參考下

簡介

初心

本文檔是一份 JavaScript 速查表,你在現(xiàn)代項(xiàng)目中會(huì)經(jīng)常遇到,以及最新的代碼示例。

本指南不是為了教你從頭開始學(xué)習(xí) JavaScript ,而是為了幫助那些可能不熟悉當(dāng)前代碼庫(例如 React)所用到 JavaScript 概念的開發(fā)人員。

此外,我有時(shí)還會(huì)提供一些個(gè)人的建議,這些建議可能是有爭議的,但我也會(huì)注意到,當(dāng)我這么做的時(shí)候,這是我個(gè)人的建議。

注意: 這里介紹的大多數(shù)概念來自于最新的 JavaScript 語言( ES2015,通常稱為 ES6)。你可以在 這里 找到新增的特性,網(wǎng)站做得很棒。

補(bǔ)充資源

當(dāng)您努力了解一個(gè)新概念時(shí),我建議你在以下資源網(wǎng)站上尋找相關(guān)的答案:

目錄

概念

變量聲明: var, const, let

在 JavaScript 中,有 3 個(gè)關(guān)鍵字可用于聲明一個(gè)變量,他們之間有些差異。那些是 var ,letconst

簡單說明

使用 const 關(guān)鍵字聲明的變量無法重新分配,而 letvar 可以。

我建議總是默認(rèn)使用 const 來聲明你的變量,如果你需要改變它,或者稍后需要重新分配,那么實(shí)用 let

作用域 可否重新分配 可變性 暫時(shí)性死區(qū)
const Block No Yes Yes
let Block Yes Yes Yes
var Function Yes Yes No

簡單的示例

const person = "Nick";
person = "John" // 將會(huì)引起錯(cuò)誤,person 不能重新分配
let person = "Nick";
person = "John";
console.log(person) // "John", 使用 let 允許重新分配

詳細(xì)說明

變量的 作用域 大致意思是“在哪里可以訪問這個(gè)變量”。

var

var 聲明的變量是 函數(shù)作用域 ,這意味著當(dāng)在函數(shù)中創(chuàng)建變量時(shí),該函數(shù)中的所有內(nèi)容都可以訪問該變量。
此外,函數(shù)中創(chuàng)建的 函數(shù)作用域 變量無法在此函數(shù)外訪問。

我建議你把它看作一個(gè) X 作用域 變量,意味著這個(gè)變量是 X 的屬性。

function myFunction() {
 var myVar = "Nick";
 console.log(myVar); // "Nick" - 在這個(gè)函數(shù)中 myVar 可被訪問到
}
console.log(myVar); // 拋出錯(cuò)誤 ReferenceError, 在函數(shù)之外 myVar 則無法訪問

繼續(xù)關(guān)注變量的作用域,這里是一個(gè)更微妙的例子:

function myFunction() {
 var myVar = "Nick";
 if (true) {
 var myVar = "John";
 console.log(myVar); // "John"
 // 實(shí)際上,myVar是函數(shù)作用域,我們只是將之前 myVar 的值 "Nick" 抹去,重新聲明為 "John"
 }
 console.log(myVar); // "John" - 看看 if 語句塊中的指令是如何影響這個(gè)值的 
}
console.log(myVar); // 拋出錯(cuò)誤 ReferenceError, 在函數(shù)之外 myVar 則無法訪問

此外,在執(zhí)行過程中,var聲明的變量被移動(dòng)到范圍的頂部。這就是我們所說的變量聲明提升(var hoisting)

這部分的代碼:

console.log(myVar) // undefined -- 不會(huì)報(bào)錯(cuò)
var myVar = 2;

在執(zhí)行時(shí),被解析為:

var myVar;
console.log(myVar) // undefined -- 不會(huì)報(bào)錯(cuò)
myVar = 2;

愚人碼頭注:變量提升和函數(shù)聲明提升可以查看:JavaScript 中的 Hoisting (變量提升和函數(shù)聲明提升)

let

varlet 大致相同,但是用 let 聲明的變量時(shí)有以下幾個(gè)特性:

  • 塊級(jí)作用域( block scoped )
  • 在被分配之前, 無法 訪問使用
  • 在同一個(gè)作用域之下,不能重新聲明

我們來看看我們前面的例子,采用塊級(jí)作用域( block scoping )后的效果:

function myFunction() {
 let myVar = "Nick";
 if (true) {
 let myVar = "John";
 console.log(myVar); // "John"
 // 實(shí)際上 myVar 在塊級(jí)作用域中,(在 if 語句塊中)我們只是創(chuàng)建了一個(gè)新變量 myVar。
 // 此變量在 if 語句塊之外不可訪問,
 // 完全獨(dú)立于創(chuàng)建的第一個(gè) myVar 變量!
 }
 console.log(myVar); // "Nick", 可以看到 if 語句塊中的指令不會(huì)影響這個(gè)值
}
console.log(myVar); // 拋出錯(cuò)誤 ReferenceError,myVar 無法在函數(shù)外部被訪問。

現(xiàn)在,我們來看看 let(和 const)變量在被賦值之前不可訪問是什么意思:

console.log(myVar) // 拋出一個(gè)引用錯(cuò)誤 ReferenceError !
let myVar = 2;

var 變量不同的是,如果你在 let 或者 const 變量被賦值之前讀寫,那么將會(huì)出現(xiàn)錯(cuò)誤。這種現(xiàn)象通常被稱為暫存死區(qū)(Temporal dead zone)或者 TDZ。

注意: 從技術(shù)上講,letconst 變量聲明時(shí)也存在提升,但并不代表它們的賦值也會(huì)被提升。但由于它被設(shè)計(jì)成了賦值之前無法使用,所以我們直觀感覺上它沒有被提升,但其實(shí)是存在提升的。如果想了解更多細(xì)節(jié),請(qǐng)看這篇文章。

此外,您不能重新聲明一個(gè) let 變量:

let myVar = 2;
let myVar = 3; // 拋出語法錯(cuò)誤 SyntaxError
const

const 聲明的變量很像 let ,但它不能被重新賦值。

總結(jié)一下 const 變量:

  • 塊級(jí)作用域
  • 分配之前無法訪問
  • 在同一個(gè)作用域內(nèi)部,不能重新聲明
  • 不能被重新分配
const myVar = "Nick";
myVar = "John" // 拋出一個(gè)錯(cuò)誤, 不允許重新分配
const myVar = "Nick";
const myVar = "John" // 拋出一個(gè)錯(cuò)誤, 不允許重新聲明

但這里有一個(gè)小細(xì)節(jié):const 變量并不是不可變,具體來說,如果 const 聲明的變量是 objectarray 類型的值,那它是 可變的 。

對(duì)于對(duì)象:

const person = {
 name: 'Nick'
};
person.name = 'John' // 這是有效的!person 變量并非完全重新分配,只是值被改變
console.log(person.name) // "John"
person = "Sandra" // 拋出一個(gè)錯(cuò)誤, 因?yàn)橛?const 聲明的變量不能被重新分配

對(duì)于數(shù)組:

const person = [];
person.push('John'); // 這是有效的!person 變量并非完全重新分配,只是值被改變
console.log(person[0]) // "John"
person = ["Nick"] // 拋出一個(gè)錯(cuò)誤, 因?yàn)橛?const 聲明的變量不能被重新分配

擴(kuò)展閱讀

箭頭函數(shù)

ES6 JavaScript 更新引入了 箭頭函數(shù) ,這是另一種聲明和使用函數(shù)的方法。以下是他們帶來的好處:

  • 更簡潔
  • this 的值繼承自外圍的作用域
  • 隱式返回

簡單的示例

  • 簡潔性和隱式返回
function double(x) { return x * 2; } // 傳統(tǒng)的方法
console.log(double(2)) // 4
const double = x => x * 2; // 相同的函數(shù)寫成帶有隱式返回的箭頭函數(shù)
console.log(double(2)) // 4
  • this 的引用

在箭頭函數(shù)中, this 意味著封閉執(zhí)行上下文的 this 值?;旧希褂眉^函數(shù),在函數(shù)中調(diào)用函數(shù)之前,您不需要執(zhí)行 “that = this” 這樣的的技巧。

function myFunc() {
 this.myVar = 0;
 setTimeout(() => {
 this.myVar++;
 console.log(this.myVar) // 1
 }, 0);
}

詳細(xì)說明

簡潔性

箭頭函數(shù)在許多方面比傳統(tǒng)函數(shù)更簡潔。讓我們來看看所有可能的情況:

  • 隱式 VS 顯式返回

顯式返回 (explicit return) 是指在函數(shù)體中明確的使用 return 這個(gè)關(guān)鍵字。

 function double(x) {
 return x * 2; // 這個(gè)函數(shù)顯示返回 x * 2, 并且使用了 *return* 關(guān)鍵字
 }

在函數(shù)傳統(tǒng)的寫法中,返回總是顯式的。但是如果是使用箭頭函數(shù),你可以執(zhí)行 隱式返回(implicit return),這表示你不需要使用關(guān)鍵字 return 來返回一個(gè)值。

要做隱式回傳,代碼必須用一行語句寫完。

 const double = (x) => {
 return x * 2; // 這里是顯式返回
 }

由于這里只有一個(gè)返回值,我們可以做一個(gè)隱式的返回。

 const double = (x) => x * 2;

這樣做,我們只需要 移除括號(hào) 以及 return 關(guān)鍵字。這就是為什么它會(huì)被稱為 隱式 返回,return 關(guān)鍵字不在了,但是這個(gè)函數(shù)確實(shí)會(huì)返回 x * 2。

注意: 如果你的函數(shù)沒有回傳一個(gè)值 (這種作法有 副作用),那么它將不會(huì)做顯式或隱式返回。

另外,如果你想隱式地返回一個(gè) 對(duì)象(object),你必須用括號(hào)包裹,否則它將與塊大括號(hào)沖突:

const getPerson = () => ({ name: "Nick", age: 24 })
console.log(getPerson()) // { name: "Nick", age: 24 } -- 箭頭函數(shù)隱式地返回一個(gè)對(duì)象
  • 只有一個(gè)參數(shù)

如果你的函數(shù)只接受一個(gè)參數(shù),你可以省略包裹它的括號(hào)。如果我們拿上述的 double 代碼做為舉例:

 const double = (x) => x * 2; // 這個(gè)箭頭函數(shù)只接受一個(gè)參數(shù)

包裹參數(shù)的括號(hào)是可以被省略:

 const double = x => x * 2; // 這個(gè)箭頭函數(shù)只接受一個(gè)參數(shù)
  • 沒有參數(shù)

當(dāng)沒有為箭頭函數(shù)提供任何參數(shù)時(shí),你就必須加上括號(hào),否則將會(huì)拋出語法錯(cuò)誤。

 () => { // 提供括號(hào),一切都能正常運(yùn)行
 const x = 2;
 return x;
 }
 => { // 沒有括號(hào),這不能正常運(yùn)行!
 const x = 2;
 return x;
 }

this 引用

要理解箭頭函數(shù)的精妙之處,你就必須知道 this 在 JavaScript 中是如何運(yùn)作的。

在一個(gè)箭頭函數(shù)中,this 等同于封閉執(zhí)行上下文的 this 值。這意味著,一個(gè)箭頭函數(shù)并不會(huì)創(chuàng)造一個(gè)新的 this,而是從它的外圍作用域中抓取的。

如果沒有箭頭函數(shù),你想在一個(gè)函數(shù)內(nèi)部的函數(shù)中通過 this 訪問變量,你就只能使用 that = this 或者是 self = this 這樣的技巧。

舉例來說,你在 myFunc 中使用 setTimeout 函數(shù):

function myFunc() {
 this.myVar = 0;
 var that = this; // that = this 技巧
 setTimeout(
 function() { // 在這個(gè)函數(shù)作用域中,一個(gè)新的 *this* 被創(chuàng)建
  that.myVar++;
  console.log(that.myVar) // 1

  console.log(this.myVar) // undefined -- 見上訴函數(shù)聲明
 },
 0
 );
}

但是如果你使用箭頭函數(shù),this 是從它的外圍作用域中抓取的:

function myFunc() {
 this.myVar = 0;
 setTimeout(
 () => { // this 值來自它的外圍作用域, 在這個(gè)示例中,也就是 myFunc 函數(shù)
  this.myVar++;
  console.log(this.myVar) // 1
 },
 0
 );
}

有用的資源

函數(shù)參數(shù)默認(rèn)值

從 ES2015 JavaScript 更新之后開始,你可以通過下列的語法為函數(shù)的參數(shù)設(shè)定默認(rèn)值:

function myFunc(x = 10) {
 return x;
}
console.log(myFunc()) // 10 -- 沒有提供任何值,所以在 myFunc 中 10 做為默認(rèn)值分配給 x
console.log(myFunc(5)) // 5 -- 有提供一個(gè)參數(shù)值,所以在 myFunc 中 x 等于 5 

console.log(myFunc(undefined)) // 10 -- 提供 undefined 值,所以默認(rèn)值被分配給 x 
console.log(myFunc(null)) // null -- 提供一個(gè)值 (null),詳細(xì)資料請(qǐng)見下文 

默認(rèn)參數(shù)應(yīng)用于兩種且僅兩種情況:

  • 沒有提供參數(shù)
  • 提供 undefined 未定義參數(shù)

換句話說,如果您傳入 null ,則不會(huì)應(yīng)用默認(rèn)參數(shù)。

注意: 默認(rèn)值分配也可以與解構(gòu)參數(shù)一起使用(參見下一個(gè)概念以查看示例)。

擴(kuò)展閱讀

解構(gòu)對(duì)象和數(shù)組

解構(gòu) (destructuring) 是通過從存儲(chǔ)在對(duì)象或數(shù)組中的數(shù)據(jù)中提取一些值來創(chuàng)建新變量的簡便方法。

舉個(gè)簡單的實(shí)例,destructuring 可以被用來解構(gòu)函數(shù)中的參數(shù),或者像是 React 項(xiàng)目中 this.props 這樣的用法。

用示例代碼說明

  • Object

我們考慮一下以下對(duì)象的所有屬性:

const person = {
 firstName: "Nick",
 lastName: "Anderson",
 age: 35,
 sex: "M"
}

不使用解構(gòu):

const first = person.firstName;
const age = person.age;
const city = person.city || "Paris";

使用解構(gòu),只需要 1 行代碼:

const { firstName: first, age, city = "Paris" } = person; // 就是這么簡單!

console.log(age) // 35 -- 一個(gè)新變量 age 被創(chuàng)建,并且其值等同于 person.age
console.log(first) // "Nick" -- 一個(gè)新變量 first 被創(chuàng)建,并且其值等同于 person.firstName A new variable first is created and is equal to person.firstName
console.log(firstName) // Undefined -- person.firstName 雖然存在,但是新變量名為 first
console.log(city) // "Paris" -- 一個(gè)新變量 city 被創(chuàng)建,并且因?yàn)?person.city 為 undefined(未定義) ,所以 city 將等同于默認(rèn)值也就是 "Paris"。

注意:const { age } = person; 中, const 關(guān)鍵字后的括號(hào)不是用于聲明對(duì)象或代碼塊,而是 解構(gòu)(structuring) 語法。

  • 函數(shù)參數(shù)

解構(gòu)(structuring) 經(jīng)常被用來解構(gòu)函數(shù)中的對(duì)象參數(shù)。

不使用解構(gòu):

function joinFirstLastName(person) {
 const firstName = person.firstName;
 const lastName = person.lastName;
 return firstName + '-' + lastName;
}

joinFirstLastName(person); // "Nick-Anderson"

在解構(gòu)對(duì)象參數(shù) person 這個(gè)參數(shù)時(shí),我們可以得到一個(gè)更簡潔的函數(shù):

function joinFirstLastName({ firstName, lastName }) { // 通過解構(gòu) person 參數(shù),我們分別創(chuàng)建了 firstName 和 lastName 這兩個(gè)變數(shù)
 return firstName + '-' + lastName;
}

joinFirstLastName(person); // "Nick-Anderson"

箭頭函數(shù)中使用解構(gòu),使得開發(fā)過程更加愉快:

const joinFirstLastName = ({ firstName, lastName }) => firstName + '-' + lastName;

joinFirstLastName(person); // "Nick-Anderson"
  • Array

我們考慮一下以下數(shù)組:

const myArray = ["a", "b", "c"];

不使用解構(gòu):

const x = myArray[0];
const y = myArray[1];

使用解構(gòu):

const [x, y] = myArray; // 就是這么簡單!

console.log(x) // "a"
console.log(y) // "b"

有用的資源

數(shù)組方法 – map / filter / reduce

map,filterreduce 都是數(shù)組提供的方法,它們源自于 函數(shù)式編程 。

總結(jié)一下:

  • Array.prototype.map() 接受一組數(shù)組,在其元素上執(zhí)行某些操作,并返回具有轉(zhuǎn)換后的元素?cái)?shù)組。
  • Array.prototype.filter() 接受一組數(shù)組,依照元素本身決定是否保留,并返回一個(gè)僅包含保留元素的數(shù)組。
  • Array.prototype.reduce() 接受一組數(shù)組,將這些元素合并成單個(gè)值(并返回)。

我建議盡可能地使用它們來遵循函數(shù)式編程 (functional programming) 的原則,因?yàn)樗鼈兪强山M合的,簡潔的且優(yōu)雅的。

通過這三種方法,您可以避免在大多數(shù)情況下使用 forforEach 。當(dāng)你試圖做一個(gè) for 循環(huán)時(shí),嘗試用 map,filterreduce 來組合試試。起初你可能很難做到這一點(diǎn),因?yàn)樗枰銓W(xué)習(xí)一種新的思維方式,但一旦你得到它,事情會(huì)變得更容易。

愚人碼頭注:JavaScript 函數(shù)式編程建議看看以下幾篇文章

簡單的示例

const numbers = [0, 1, 2, 3, 4, 5, 6];
const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12]
const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6]
const sum = numbers.reduce((prev, next) => prev + next, 0); // 21

通過組合 map,filter 和 reduce 來計(jì)算 10 分以上的學(xué)生成績總和 sum :

const students = [
 { name: "Nick", grade: 10 },
 { name: "John", grade: 15 },
 { name: "Julia", grade: 19 },
 { name: "Nathalie", grade: 9 },
];

const aboveTenSum = students
 .map(student => student.grade) // 我們將學(xué)生數(shù)組映射到他們成績的數(shù)組中
 .filter(grade => grade >= 10) // 我們過濾成績數(shù)組以保持10分以上的元素
 .reduce((prev, next) => prev + next, 0); // 我們將合計(jì)所有10分以上的成績

console.log(aboveTenSum) // 44 -- 10 (Nick) + 15 (John) + 19 (Julia), 低于10的 Nathalie 被忽略 

說明

讓我們考慮一下下列數(shù)組:

const numbers = [0, 1, 2, 3, 4, 5, 6];

Array.prototype.map()
const doubledNumbers = numbers.map(function(n) {
 return n * 2;
});
console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]

這里發(fā)生了什么?我們在 numbers 這個(gè)數(shù)組中使用 .map 方法,map 將會(huì)迭代數(shù)組的每個(gè)元素,并傳遞給我們的函數(shù)。該函數(shù)的目標(biāo)是生成并返回一個(gè)新的值,以便 map 可以替換掉原本的數(shù)組。

我們來解釋一下這個(gè)函數(shù),使之更清楚一點(diǎn):

const doubleN = function(n) { return n * 2; };
const doubledNumbers = numbers.map(doubleN);
console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]

numbers.map(doubleN) 將會(huì)產(chǎn)生 [doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)] 而它們分別等同于 [0, 2, 4, 6, 8, 10, 12]

注意: 如果你不需要返回一個(gè)新的數(shù)組, 且只想執(zhí)行一個(gè)帶有副作用的循環(huán),使用 for / forEach 循環(huán)會(huì)更為符合你的需求。

Array.prototype.filter()
const evenNumbers = numbers.filter(function(n) {
 return n % 2 === 0; // 如果 "n" 符合條件返回 true , 如果 "n" 不符合條件則 false 。
});
console.log(evenNumbers); // [0, 2, 4, 6]

我們在 numbers 數(shù)組中使用 .filter 方法,過濾器遍歷數(shù)組中的每個(gè)元素,并將其傳遞給我們的函數(shù)。函數(shù)的目標(biāo)是返回一個(gè)布爾值,它將確定當(dāng)前值是否被保留。過濾之后返回的數(shù)組將只包含保留值。

Array.prototype.reduce()

reduce 方法的目標(biāo)是將迭代數(shù)組中的所有元素,減少 到只留下單一值。如何聚合這些元素取決于你。

const sum = numbers.reduce(
 function(acc, n) {
 return acc + n;
 },
 0 // 累加器迭代變量的初始值
);

console.log(sum) //21

就像 .map 和 .filter 方法一樣, .reduce 方法被應(yīng)用在數(shù)組上并接收一個(gè)函數(shù)做為第一個(gè)參數(shù)。

下面是一些差異:

  • .reduce 接受兩個(gè)參數(shù)

第一個(gè)參數(shù)是一個(gè)函數(shù),將在每個(gè)迭代步驟中被調(diào)用。

第二個(gè)參數(shù)是在第一個(gè)迭代步驟(讀取下一個(gè)用的)的累加器變量的值(此處是 acc)。

  • 函數(shù)參數(shù)

作為 .reduce 的第一個(gè)參數(shù)傳遞的函數(shù)需要兩個(gè)參數(shù)。第一個(gè)(此處是 acc)是累加器變量,而第二個(gè)參數(shù)(n)則是當(dāng)前元素。

累加器變量的值等于 上一次 迭代步驟中函數(shù)的返回值。在迭代過程的第一步,acc 等于你做為 .reduce 時(shí)第二個(gè)參數(shù)所傳遞的值(愚人碼頭注:也就是累加器初始值)。

進(jìn)行第一次迭代

acc = 0 因?yàn)槲覀儼?0 做為 reduce 的第二個(gè)參數(shù)

n = 0 number 數(shù)組的第一個(gè)元素

函數(shù)返回 acc + n –> 0 + 0 –> 0

進(jìn)行第二次迭代

acc = 0 因?yàn)樗巧洗蔚祷氐闹?/p>

n = 1 number 數(shù)組的第二個(gè)元素

函數(shù)返回 acc + n –> 0 + 1 –> 1

進(jìn)行第三次迭代

acc = 1 因?yàn)樗巧洗蔚祷氐闹?/p>

n = 2 number 數(shù)組的第三個(gè)元素

函數(shù)返回 acc + n –> 1 + 2 –> 3

進(jìn)行第四次迭代

acc = 3 因?yàn)樗巧洗蔚祷氐闹?/p>

n = 3 number 數(shù)組的第四個(gè)元素

函數(shù)返回 acc + n –> 3 + 3 –> 6

[…] 進(jìn)行最后一次迭代

acc = 15 因?yàn)樗巧洗蔚祷氐闹?/p>

n = 6 number 數(shù)組的最后一個(gè)元素

函數(shù)返回 acc + n –> 15 + 6 –> 21

因?yàn)樗亲詈笠粋€(gè)迭代步驟了, .reduce 將返回 21 。

擴(kuò)展閱讀

展開操作符 “…”

ES2015 已經(jīng)引入了展開操作符 ... ,可以將可迭代多個(gè)元素(如數(shù)組)展開到適合的位置。

簡單的代碼示例

const arr1 = ["a", "b", "c"];
const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"]
function myFunc(x, y, ...params) {
 console.log(x);
 console.log(y);
 console.log(params)
}

myFunc("a", "b", "c", "d", "e", "f")
// "a"
// "b"
// ["c", "d", "e", "f"]
const { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

const n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }

說明

應(yīng)用于迭代 (如數(shù)組)

如果我們有以下兩個(gè)數(shù)組:

const arr1 = ["a", "b", "c"];
const arr2 = [arr1, "d", "e", "f"]; // [["a", "b", "c"], "d", "e", "f"]

arr2 的第一個(gè)元素是一個(gè)數(shù)組 ,因?yàn)?arr1 是被注入到 arr2 之中的。但我們真正想要得到的 arr2 是一個(gè)純字母的數(shù)組。為了做到這點(diǎn),我們可以將 arr1 展開(spread)arr2

通過展開操作符:

const arr1 = ["a", "b", "c"];
const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"]

函數(shù)剩余參數(shù)

在函數(shù)參數(shù)中,我們可以使用 rest 操作符將參數(shù)注入到我們可以循環(huán)的數(shù)組中。這里已經(jīng)有一個(gè) argument 對(duì)象綁定到每個(gè)函數(shù)上,等同于把數(shù)組中的所有參數(shù)都傳遞給函數(shù)。

function myFunc() {
 for (var i = 0; i < arguments.length; i++) {
 console.log(arguments[i]);
 }
}

myFunc("Nick", "Anderson", 10, 12, 6);
// "Nick"
// "Anderson"
// 10
// 12
// 6

但是如果說,我們希望創(chuàng)造的是一個(gè)包含各科成績和平均成績的新學(xué)生。將前兩個(gè)參數(shù)提取為兩個(gè)單獨(dú)的變量,并把剩下的元素生成一個(gè)可迭代的數(shù)組是不是更加方便呢?

這正是 rest 操作符允許我們做的事情!

function createStudent(firstName, lastName, ...grades) {
 // firstName = "Nick"
 // lastName = "Anderson"
 // [10, 12, 6] -- "..." 將傳遞所有剩余參數(shù),并創(chuàng)建一個(gè)包含它們的 "grades" 數(shù)組變量

 const avgGrade = grades.reduce((acc, curr) => acc + curr, 0) / grades.length; // 根據(jù) grade 計(jì)算平均成績

 return {
 firstName: firstName,
 lastName: lastName,
 grades: grades,
 avgGrade: avgGrade
 }
}

const student = createStudent("Nick", "Anderson", 10, 12, 6);
console.log(student);
// {
// firstName: "Nick",
// lastName: "Anderson",
// grades: [10, 12, 6],
// avgGrade: 9.333333333333334
// }

注意: 在這個(gè)示例中, createStudent 函數(shù)其實(shí)并不太好,因?yàn)槲覀儾]有去檢查 grades.length 是否存在又或者它等于 0 的情況。但是這個(gè)例子現(xiàn)在這樣寫,能夠更好的幫助我們理解剩余參數(shù)的運(yùn)作,所以我沒有處理上述的這種情況。

對(duì)象屬性展開

對(duì)于這一點(diǎn),我建議你閱讀有關(guān) rest 操作符以前的有關(guān)迭代和函數(shù)參數(shù)的相關(guān)說明。

const myObj = { x: 1, y: 2, a: 3, b: 4 };
const { x, y, ...z } = myObj; // 這里是對(duì)象被解構(gòu)
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

// z是對(duì)象解構(gòu)后的剩余部分:myObj 對(duì)象除去 x 和 y 屬性后剩余部分被解構(gòu)

const n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }

// 這里 z 對(duì)象的屬性展開到 n 中

擴(kuò)展資源

對(duì)象屬性簡寫

將變量分配給一個(gè)對(duì)象屬性時(shí),如果變量名稱和屬性名稱相同,你可以執(zhí)行以下操作:

const x = 10;
const myObj = { x };
console.log(myObj.x) // 10

說明

通常(ES2015之前)當(dāng)你聲明一個(gè)新的 對(duì)象字面量 并且想要使用變量作為對(duì)象屬性值時(shí),你會(huì)寫這樣類似的代碼:

const x = 10;
const y = 20;

const myObj = {
 x: x, // 將 x 分配給 myObj.x
 y: y // 將 y 變量給 myObj.y
};

console.log(myObj.x) // 10
console.log(myObj.y) // 20

如您所見,這樣的作法其實(shí)相當(dāng)重復(fù),因?yàn)?myObj 的屬性名稱與要分配給這些屬性的變量名相同。

使用ES2015,當(dāng)變量名與屬性名稱相同時(shí),您可以進(jìn)行以下簡寫:

const x = 10;
const y = 20;

const myObj = {
 x,
 y
};

console.log(myObj.x) // 10
console.log(myObj.y) // 20

擴(kuò)展資源

Promises

Promises 是一個(gè)可以從異步函數(shù) (參考) 同步返回的對(duì)象。

可以使用 Promises 來避免 回調(diào)地獄 (callback hell) ,而且它們在現(xiàn)代 JavaScript 項(xiàng)目中越來越頻繁地遇到。

簡單的代碼示例

const fetchingPosts = new Promise((res, rej) => {
 $.get("/posts")
 .done(posts => res(posts))
 .fail(err => rej(err));
});

fetchingPosts
 .then(posts => console.log(posts))
 .catch(err => console.log(err));

說明

當(dāng)你在執(zhí)行 Ajax 請(qǐng)求 時(shí),響應(yīng)不是同步的,因?yàn)橘Y源請(qǐng)求需要時(shí)間。如果你要的資源由于某些原因 (404) 不可用,甚至可能永遠(yuǎn)都不會(huì)請(qǐng)求到。

為了處理這類情況,ES2015 為我們提供了 promises。 Promises 可以有三種不同的狀態(tài):

  • 等待中 (Pending)
  • 達(dá)成 (Fulfilled)
  • 拒絕 (Rejected)

假設(shè)我們希望使用 promises 去進(jìn)行 Ajax 請(qǐng)求以獲取 X 資源。

創(chuàng)建 promise

我們首先要?jiǎng)?chuàng)建一個(gè) promise。我們將會(huì)使用 jQuery 的 get 方法對(duì) X 資源執(zhí)行 Ajax 請(qǐng)求。

const xFetcherPromise = new Promise( // 使用 "new" 關(guān)鍵字創(chuàng)建promise,并把它保存至一個(gè)變量
 function(resolve, reject) { // Promise 構(gòu)造函數(shù)需要一個(gè)帶有著 resolve 和 reject 這兩個(gè)參數(shù)的函數(shù)作為參數(shù)
 $.get("X") // 執(zhí)行 Ajax 請(qǐng)求
  .done(function(X) { // 一旦請(qǐng)求完成...
  resolve(X); // ... 把 X 值做為參數(shù)去 resolve promise
  })
  .fail(function(error) { // 如果請(qǐng)求失敗...
  reject(error); // ... 把 error 做為參數(shù)去 reject promise
  });
 }
)

如上示例所示,Promise 對(duì)象需要一個(gè)帶有兩個(gè)參數(shù) ( resolvereject ) 的執(zhí)行函數(shù)。這兩個(gè)參數(shù)會(huì)把 pending 狀態(tài)的 promise 分別進(jìn)行 fulfilledrejected 的處理。

Promise 在實(shí)例創(chuàng)建后處于待處理狀態(tài),并且它的執(zhí)行器函數(shù)立即執(zhí)行。一旦在執(zhí)行函數(shù)中調(diào)用了 resolvereject 函數(shù),Promise 將調(diào)用相關(guān)的處理程序。

Promise 處理器的用法

為了獲得 promise 結(jié)果(或錯(cuò)誤),我們必須通過執(zhí)行以下操作來附加處理程序:

xFetcherPromise
 .then(function(X) {
 console.log(X);
 })
 .catch(function(err) {
 console.log(err)
 })

如果 promise 成功,則執(zhí)行 resolve ,并執(zhí)行 .then 參數(shù)所傳遞的函數(shù)。

如果失敗,則執(zhí)行 reject ,并執(zhí)行 .catch 參數(shù)所傳遞的函數(shù)。

注意: 如果 promise 在相應(yīng)的處理程序附加時(shí)已經(jīng) fulfilledrejected,處理程序?qū)⒈徽{(diào)用,
因此在異步操作完成和其處理程序被附加之間沒有競爭條件。 (參考: MDN)(MDN)。

擴(kuò)展閱讀

模板字符串

模板字符串是一種單行和多行字符串的 表達(dá)式插值 (expression interpolation)

換句話說,它是一種新的字符串語法,你可以更方便地在 JavaScript 表達(dá)式中使用 (例如變量)。

簡單的代碼示例

const name = "Nick";
`Hello ${name}, the following expression is equal to four : ${2+2}`;

// Hello Nick, the following expression is equal to four: 4

擴(kuò)展閱讀

帶標(biāo)簽(tag)的模板字符串

模板標(biāo)簽是可以作為模板字符串(template literal)的前綴函數(shù)。當(dāng)一個(gè)函數(shù)被這鐘方式調(diào)用時(shí),第一個(gè)參數(shù)是出現(xiàn)在模板插值變量之間的字符串?dāng)?shù)組,并且隨后的參數(shù)是插值變量的值??梢允褂谜归_運(yùn)算符 ... 捕獲所有這些參數(shù)。 (參考: MDN)。

注意: 名為styled-components的著名庫很大程度上依賴于此功能。

以下是他們工作的玩具示例。

function highlight(strings, ...values) {
 // 愚人碼頭注:為了更好的理解函數(shù)的參數(shù),我增加了這樣兩行代碼,特殊說明見示例代碼下面的說明;
 console.log(strings);//(3) ["I like ", " on ", ".", raw: Array(3)]
 console.log(values);//(2) ["jam", "toast"]

 const interpolation = strings.reduce((prev, current) => {
 return prev + current + (values.length ? "<mark>" + values.shift() + "</mark>" : "");
 }, "");

 return interpolation;
}

const condiment = "jam";
const meal = "toast";

highlight`I like ${condiment} on ${meal}.`;
// "I like <mark>jam</mark> on <mark>toast</mark>."

——-愚人碼頭注開始——-

愚人碼頭注,關(guān)于第一個(gè)參數(shù):

標(biāo)簽函數(shù)的第一個(gè)參數(shù)是一個(gè)包含了字符串字面值的數(shù)組(在本例中分別為”I like “,” on “和”.”)。如果我們這樣調(diào)用 highlight`I like ${condiment} on ${meal}` (注意最后面沒”.”),那么第一個(gè)參數(shù)還是一個(gè) 3 個(gè)元素的數(shù)組:[“I like “, ” on “, “”],特別注意最后一個(gè)元素是的空字符串””;

字符串的 raw 屬性

strings 確實(shí)是一個(gè)數(shù)組,但是它還有一個(gè) raw 屬性。其屬性值 strings.raw 也是一個(gè)數(shù)組,其元素分別表示 strings 中對(duì)應(yīng)的,經(jīng)過轉(zhuǎn)義之前在 模板字符串(Template literals) 中由用戶輸入的字符串。我們來看一個(gè)例子:

const myTag = (strs, ...exprs) => {
 console.log(strs);     //(3) ["x", "\y", "", raw: Array(3)]
 console.log(strs.raw);    //(3) ["x", "\\y", ""
 console.log(exprs);     //[1, 2]
};

const obj = { a: 1, b: 2 };
const result = myTag`x${obj.a}\\y${obj.b}`;

上例中 "\y" 未轉(zhuǎn)義之前對(duì)應(yīng)的字符串為 "\\y" ,顯然這兩個(gè)字符串長度是不同的。

String.raw 是ES2015,內(nèi)置對(duì)象 String 的一個(gè)靜態(tài)方法,把它作為Tag,可以做到只替換嵌入表達(dá)式而不轉(zhuǎn)義字符。

const raw = String.raw`1\\2\\${1+2}`;

console.log(raw);   //1\\2\\3
console.log(raw.length); //7

const x = `1\\2\\${1+2}`;
console.log(x);    //1\2\3
console.log(x.length);  //5
規(guī)避問題

Template literals遵從字符串的轉(zhuǎn)義規(guī)則:

(1)以 \u 開頭,后跟4個(gè)16進(jìn)制數(shù)字,例如,\u00B1表示±
(2)以 \u 開頭,使用大括號(hào)括起來的16進(jìn)制數(shù)字,例如,\u{2F804} 表示
(3)以 \x 開頭,后跟2個(gè)16進(jìn)制數(shù)字,例如,\xB1 表示 ±
(4)以 \ 開頭,后跟10進(jìn)制數(shù)字,用來表示八進(jìn)制字面量(注:strict mode下不支持)

解釋器遇到 \u\x ,如果發(fā)現(xiàn)后面的字符不滿足以上條件,就會(huì)報(bào)語法錯(cuò)。例如,

> latex`\unicode`
> Uncaught SyntaxError: Invalid Unicode escape sequence

不再展開,具體參考:Template literals。

愚人碼頭注,關(guān)于后面的剩余參數(shù):

在第一個(gè)參數(shù)后的每一個(gè)參數(shù),都是已經(jīng)求值后的替換表達(dá)式。 看下面這個(gè)例子:

var a = 5;
var b = 10;
function tag(strings, ...values) {
 console.log(values[0]);  // 15
 console.log(values[1]);  // 50

 return values[0]+values[1]; //65
}

tag`Hello ${ a + b } world ${ a * b}`;

上例中,剩余的2個(gè)參數(shù)值分別是 1550;

——-愚人碼頭注結(jié)束——-

一個(gè)更有趣的例子:

function comma(strings, ...values) {
 return strings.reduce((prev, next) => {
 let value = values.shift() || [];
 value = value.join(", ");
 return prev + next + value;
 }, "");
}

const snacks = ['apples', 'bananas', 'cherries'];
comma`I like ${snacks} to snack on.`;
// "I like apples, bananas, cherries to snack on."

擴(kuò)展閱讀

ES6 模塊的導(dǎo)入 / 導(dǎo)出 (imports / exports)

ES6模塊用于訪問模塊中顯式導(dǎo)出的模塊中的變量或函數(shù)。

我強(qiáng)烈建議您查看 MDN 上有關(guān) import/export(請(qǐng)參閱下面的擴(kuò)展閱讀資源),它們寫的既簡潔又完整。

用示例代碼說明

命名導(dǎo)出

命名導(dǎo)出用于從一個(gè)模塊導(dǎo)出多個(gè)值。

注意: 您命名導(dǎo)出的變量是一等公民(first-class citizens)。

// mathConstants.js
export const pi = 3.14;
export const exp = 2.7;
export const alpha = 0.35;

// -------------

// myFile.js
import { pi, exp } from './mathConstants.js'; // 命名導(dǎo)入 -- 類似于解構(gòu)語法
console.log(pi) // 3.14
console.log(exp) // 2.7

// -------------

// mySecondFile.js
import * as constants from './mathConstants.js'; // 將所有導(dǎo)出的值注入到 constants 變量中
console.log(constants.pi) // 3.14
console.log(constants.exp) // 2.7

雖然命名導(dǎo)入看起來像是 解構(gòu)(destructuring),但它們具有不同的語法,并且不一樣。 他們不支持默認(rèn)值,也不支持深層次的解構(gòu)。

此外,您可以使用別名,但語法不同于解構(gòu)中使用的語法:

import { foo as bar } from 'myFile.js'; // foo 被導(dǎo)入并注入到一個(gè)新的 bar 變量中

默認(rèn)導(dǎo)入 / 導(dǎo)出 (imports / exports)

關(guān)于默認(rèn)導(dǎo)出,每個(gè)模塊只能有一個(gè)默認(rèn)導(dǎo)出。默認(rèn)導(dǎo)出可以是函數(shù),類,對(duì)象或其他任何東西。這個(gè)值被認(rèn)為是“主要”的導(dǎo)出值,因?yàn)樗鼘⑹亲詈唵蔚膶?dǎo)入。 參考: MDN。

// coolNumber.js
const ultimateNumber = 42;
export default ultimateNumber;

// ------------

// myFile.js
import number from './coolNumber.js';
// 默認(rèn)導(dǎo)出,將獨(dú)立于其名稱,自動(dòng)注入到 number 這個(gè)變量; 
console.log(number) // 42

函數(shù)導(dǎo)出:

// sum.js
export default function sum(x, y) {
 return x + y;
}
// -------------

// myFile.js
import sum from './sum.js';
const result = sum(1, 2);
console.log(result) // 3

擴(kuò)展閱讀

JavaScript 中的 this

this 操作符的行為與其他語言不同,在大多數(shù)情況之下是由函數(shù)的調(diào)用方式?jīng)Q定。 (參考: MDN)。

this 概念有很多細(xì)節(jié),并不是那么容易理解,我強(qiáng)烈建議你深入了解下面的擴(kuò)展閱讀。因此,我會(huì)提供我個(gè)人對(duì)于 this 的一點(diǎn)理解和想法。我是從 Yehuda Katz 寫的這篇文章 學(xué)到了這個(gè)提示。

function myFunc() {
 ...
}

// 在每個(gè)述句后面,你都可以在 myFunc 中找到 this 的值

myFunc.call("myString", "hello") // "myString" -- 首先, .call的參數(shù)值被注入到 *this*

// 在非嚴(yán)格模式下(non-strict-mode)
myFunc("hello") // window -- myFunc() 是 myFunc.call(window, "hello") 的語法糖

// 在嚴(yán)格模式下(strict-mode)
myFunc("hello") // undefined -- myFunc() 是 myFunc.call(undefined, "hello") 的語法糖
var person = {
 myFunc: function() { ... }
}

person.myFunc.call(person, "test") // person 對(duì)象 -- 首先, .call的參數(shù)值被注入到 *this*
person.myFunc("test") // person 對(duì)象 -- person.myFunc() 是 person.myFunc.call(person, "test") 的語法糖

var myBoundFunc = person.myFunc.bind("hello") // 創(chuàng)造了一個(gè)函數(shù),并且把 "hello" 注入到 *this* 
person.myFunc("test") // person 對(duì)象 -- bind 方法對(duì)原有方法并無造成影響
myBoundFunc("test") // "hello" -- myBoundFunc 是把帶有 "hello" 的 person.myFunc 綁定到 *this*

擴(kuò)展閱讀

類(Class)

JavaScript 是一個(gè)基于原型 的語言(然而Java 是基于類別 的語言)。 ES6 引入了 JavaScript 類,它們是用于基于原型的繼承的語法糖,而 不是 一種新的基于類繼承的模型(參考).

如果您熟悉其他語言的類,那么 類 (class) 這個(gè)詞的確容易理解出錯(cuò)。 如果真的有此困擾,請(qǐng)避免在這樣的認(rèn)知下思考 JavaScript 類的行為,并將其視為完全不同的新概念。

由于本文檔不是從根本上教你 JavaScript 語言,我會(huì)相信你知道什么是原型,以及它們的行為。 如果沒有,請(qǐng)參閱示例代碼下面列出的擴(kuò)展閱讀,以方便你去理解這些概念:

簡單的示例

ES6 之前,原型語法:

var Person = function(name, age) {
 this.name = name;
 this.age = age;
}
Person.prototype.stringSentence = function() {
 return "Hello, my name is " + this.name + " and I'm " + this.age;
}

使用 ES6 類(class)* 語法:

class Person {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }

 stringSentence() {
 return "Hello, my name is " + this.name + " and I'm " + this.age;
 }
}

const myPerson = new Person("Manu", 23);
console.log(myPerson.age) // 23
console.log(myPerson.stringSentence()) // "Hello, my name is Manu and I'm 23

擴(kuò)展閱讀

更好的理解原型:

更好的理解類:

Extendssuper 關(guān)鍵字

extends 關(guān)鍵字用于類聲明或類表達(dá)式中,以創(chuàng)建一個(gè)類,該類是另一個(gè)類的子類(參考: MDN)。 子類繼承超類的所有屬性,另外可以添加新屬性或修改繼承的屬性。

super 關(guān)鍵字用于調(diào)用對(duì)象的父對(duì)象的函數(shù),包括其構(gòu)造函數(shù)。

  • super 關(guān)鍵字必須在構(gòu)造函數(shù)中使用 this 關(guān)鍵字之前使用。
  • 調(diào)用 super() 調(diào)用父類構(gòu)造函數(shù)。 如果要將類的構(gòu)造函數(shù)中的一些參數(shù)傳遞給其父構(gòu)造函數(shù),則可以使用 super(arguments) 來調(diào)用它。
  • 如果父類有一個(gè) X 的方法(甚至靜態(tài)),可以使用 super.X() 在子類中調(diào)用。

簡單的代碼示例

class Polygon {
 constructor(height, width) {
 this.name = 'Polygon';
 this.height = height;
 this.width = width;
 }

 getHelloPhrase() {
 return `Hi, I am a ${this.name}`;
 }
}

class Square extends Polygon {
 constructor(length) {
 // 這里,它調(diào)用父類的構(gòu)造函數(shù)的 length, 
 // 提供給 Polygon 的 width 和 height。
 super(length, length);
 // 注意: 在派生的類中, 在你可以使用 'this' 之前, 必須先調(diào)用 super() 。
 // 忽略這個(gè), 這將導(dǎo)致引用錯(cuò)誤。
 this.name = 'Square';
 this.length = length;
 }

 getCustomHelloPhrase() {
 const polygonPhrase = super.getHelloPhrase(); // 通過 super.X() 語法訪問父級(jí)方法
 return `${polygonPhrase} with a length of ${this.length}`;
 }

 get area() {
 return this.height * this.width;
 }
}

const mySquare = new Square(10);
console.log(mySquare.area) // 100
console.log(mySquare.getHelloPhrase()) // 'Hi, I am a Square' -- Square 繼承自 Polygon 并可以訪問其方法
console.log(mySquare.getCustomHelloPhrase()) // 'Hi, I am a Square with a length of 10'

注意 : 如果我們在 Square 類中調(diào)用 super() 之前嘗試使用 this,將引發(fā)一個(gè)引用錯(cuò)誤:

class Square extends Polygon {
 constructor(length) {
 this.height; // 引用錯(cuò)誤, 必須首先調(diào)用 super() !

 // 這里,它調(diào)用父類的構(gòu)造函數(shù)的 length, 
 // 提供給 Polygon 的 width 和 height。
 super(length, length);

 // 注意: 在派生的類中, 在你可以使用 'this' 之前, 必須先調(diào)用 super() 。
 // 忽略這個(gè), 這將導(dǎo)致引用錯(cuò)誤。
 this.name = 'Square';
 }
}

擴(kuò)展閱讀

異步和等待(Async Await)

promises 之外,還有一種新的語法可能會(huì)遇到,那就是異步的 async / await。

async / await 函數(shù)的目的是簡化同步使用 promise 的行為,并對(duì)一組 promises 執(zhí)行一些處理。正如 promises 類似于結(jié)構(gòu)化的回調(diào),async / await 類似于組合生成器(combining generators) 和 promises。異步函數(shù) 總是 返回一個(gè) Promise。 (參考: MDN)

注意: 您必須了解什么是 promises 以及它們是如何工作的,然后再嘗試了解 async / await ,因?yàn)樗鼈円蕾囉?promises 。
注意2: [await 必須在async函數(shù)中使用](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial- c7ec10518dd9#f3f0),這意味著你不能程式碼的頂部使用 await,因?yàn)樗⒉辉诋惒胶瘮?shù)之內(nèi)。

簡單的代碼示例

async function getGithubUser(username) { // async 關(guān)鍵字允許在函數(shù)中使用 await ,意味著函數(shù)返回一個(gè) promise 
 const response = await fetch(`https://api.github.com/users/${username}`); // 執(zhí)行在這里暫停,直到fetch返回的 Promise 被 resolved 
 return response.json();
}

getGithubUser('mbeaudru')
 .then(user => console.log(user)) // 記錄用戶響應(yīng) - 不能使用 await 語法,因?yàn)榇舜a不在 async 函數(shù)中 
 .catch(err => console.log(err)); // 如果在我們的異步函數(shù)中拋出一個(gè)錯(cuò)誤,我們將在這里捕獲它 

用示例代碼說明

Async / Await 是建立在 promises 概念之上的,但它們允許更強(qiáng)制的代碼風(fēng)格。

async 操作符將一個(gè)函數(shù)標(biāo)記為異步,并將始終返回一個(gè) Promise 。你可以在 async 函數(shù)中使用 await 操作符來暫停該行代碼的執(zhí)行,直到表達(dá)式中返回的 Promise resolves 或 rejects 。

async function myFunc() {
 // 我們可以使用 *await* 操作符 因?yàn)檫@個(gè)函數(shù)是 異步(async) 的 
 return "hello world";
}

myFunc().then(msg => console.log(msg)) // "hello world" -- 由于 async 操作符,myFunc 的返回值將變成了一個(gè) promise 

當(dāng)異步函數(shù)運(yùn)行到 return 語句時(shí),將會(huì)使用返回的值來 fulfilled Promise。 如果在 async 函數(shù)中拋出錯(cuò)誤,則 Promise 狀態(tài)將轉(zhuǎn)為 rejected 。 如果沒有從 async 函數(shù)返回任何值,則在執(zhí)行 async 函數(shù)完成時(shí),仍然會(huì)返回 Promise 并 resolves 為無值。

await 操作符用于等待 Promise fulfilled ,只能在 async 函數(shù)體內(nèi)使用。 遇到這種情況時(shí),代碼執(zhí)行將暫停,直到 promise fulfilled。

注意: fetch 是一個(gè)允許執(zhí)行 AJAX 請(qǐng)求,返回一個(gè) Promise 的函數(shù)。

首先,我們來看看如何通過 promises 來獲取一個(gè) github 用戶:

function getGithubUser(username) {
 return fetch(`https://api.github.com/users/${username}`).then(response => response.json());
}

getGithubUser('mbeaudru')
 .then(user => console.log(user))
 .catch(err => console.log(err));

等價(jià)于這里 async / await :

async function getGithubUser(username) { // promise + await 關(guān)鍵字使用允許
 const response = await fetch(`https://api.github.com/users/${username}`); // 在此處執(zhí)行停止,直到 promise 獲得 fulfilled
 return response.json();
}

getGithubUser('mbeaudru')
 .then(user => console.log(user))
 .catch(err => console.log(err));

當(dāng)你需要鏈接(chain)相互依賴的 promises 時(shí),async / await 語法特別方便 。

例如,如果您需要獲取一個(gè)令牌(token) ,以便能夠在數(shù)據(jù)庫上獲取博客文章,然后獲取作者信息:

注意: await 表達(dá)式需要包含在括號(hào)中,這樣可以在同一行上調(diào)用其 resolved 值的方法和屬性。

async function fetchPostById(postId) {
 const token = (await fetch('token_url')).json().token;
 const post = (await fetch(`/posts/${postId}?token=${token}`)).json();
 const author = (await fetch(`/users/${post.authorId}`)).json();

 post.author = author;
 return post;
}

fetchPostById('gzIrzeo64')
 .then(post => console.log(post))
 .catch(err => console.log(err));

錯(cuò)誤處理

除非我們在 await 表達(dá)式外面包裹 try / catch 語句塊,否則不會(huì)捕獲異常 – 不管它們是在你的異步函數(shù)中被拋出還是在 await 期間被暫停 – 他們將 reject async 函數(shù)返回的承諾。
async 函數(shù)中使用 throw 語句與返回 reject 的 Promise 是相同。 (參考: PonyFoo).

Note : Promises 的行為相同的!

下面是的示例顯示了 promises 是如何處理錯(cuò)誤鏈的:

function getUser() { // 這個(gè) promise 將被 rejected!
 return new Promise((res, rej) => rej("User not found !"));
}

function getAvatarByUsername(userId) {
 return getUser(userId).then(user => user.avatar);
}

function getUserAvatar(username) {
 return getAvatarByUsername(username).then(avatar => ({ username, avatar }));
}

getUserAvatar('mbeaudru')
 .then(res => console.log(res))
 .catch(err => console.log(err)); // "User not found !"

等價(jià)于實(shí)用 async / await

async function getUser() { // 這個(gè) promise 將被 rejected!
 throw "User not found !";
}

async function getAvatarByUsername(userId) => {
 const user = await getUser(userId);
 return user.avatar;
}

async function getUserAvatar(username) {
 var avatar = await getAvatarByUsername(username);
 return { username, avatar };
}

getUserAvatar('mbeaudru')
 .then(res => console.log(res))
 .catch(err => console.log(err)); // "User not found !"

擴(kuò)展閱讀

真值/假值(Truthy / Falsy)

在 JavaScript 中,truthy 或 falsy 值是在布爾上下文中求值時(shí)被轉(zhuǎn)換為布爾值的值。布爾上下文的一個(gè)最常見的例子是求值 if 條件:

每個(gè)值將被轉(zhuǎn)換為true,除非它們等于以下值:

  • false
  • 0
  • "" (空字符串)
  • null
  • undefined
  • NaN

下面是 布爾上下文(boolean context) 的例子:

  • if 條件求值
if (myVar) {}

myVar 可以是任何 一等公民(first-class citizen) (變量, 函數(shù), 布爾值) ,但它會(huì)被轉(zhuǎn)換成一個(gè)布爾值,因?yàn)樗鼤?huì)在布爾上下文中進(jìn)行就值。

  • 邏輯 ! 操作符后面

如果其單獨(dú)的操作數(shù)可以轉(zhuǎn)換為 true ,則此操作符返回 false ; 否則返回 true 。

!0 // true -- 0 是 falsy(假) 值,所以它返回 true
!!0 // false -- 0 是 falsy(假) 值, 所以 !0 返回 true , 所以 !(!0) 返回 false
!!"" // false -- 空字符串是 falsy(假) 值, 所以 NOT (NOT false) 等于 false
  • 通過 Boolean 對(duì)象構(gòu)造函數(shù)
new Boolean(0) // false
new Boolean(1) // true
  • 在一個(gè)三元表達(dá)式求值時(shí)
myVar ? "truthy" : "falsy"

myVar 在布爾上下文中進(jìn)行求值。

擴(kuò)展閱讀

靜態(tài)方法

簡單說明

static 關(guān)鍵字用于聲明靜態(tài)方法。靜態(tài)方法是屬于 class(類) 對(duì)象,而該類的任何實(shí)例都不可以訪問的方法。

簡單的代碼示例

class Repo{
 static getName() {
 return "Repo name is modern-js-cheatsheet"
 }
}

// 請(qǐng)注意,我們不必創(chuàng)建 Repo 類的實(shí)例 
console.log(Repo.getName()) //Repo name is modern-js-cheatsheet

let r = new Repo();
console.log(r.getName()) // 拋出一個(gè) TypeError: repo.getName is not a function

詳細(xì)說明

靜態(tài)方法可以通過使用 this 關(guān)鍵字在另一個(gè)靜態(tài)方法中調(diào)用,這不適用于非靜態(tài)方法。非靜態(tài)方法無法使用 this 關(guān)鍵字直接訪問靜態(tài)方法。

在靜態(tài)方法調(diào)用另一個(gè)靜態(tài)方法。

要在在靜態(tài)方法調(diào)用另一個(gè)靜態(tài)方法,可以使用 this 關(guān)鍵字 ,像這樣;

class Repo{
 static getName() {
 return "Repo name is modern-js-cheatsheet"
 }

 static modifyName(){
 return this.getName() + '-added-this'
 }
}

console.log(Repo.modifyName()) //Repo name is modern-js-cheatsheet-added-this

在非靜態(tài)方法調(diào)用靜態(tài)方法。

非靜態(tài)方法可以通過2種方式調(diào)用靜態(tài)方法;

  1. ###### 使用 class(類) 名

要在非靜態(tài)方法訪問靜態(tài)方法,我們可以使用類名,并像屬性一樣調(diào)用靜態(tài)方法就可以了。 例如ClassName.StaticMethodName

class Repo{
 static getName() {
 return "Repo name is modern-js-cheatsheet"
 }

 useName(){
 return Repo.getName() + ' and it contains some really important stuff'
 }
}

// 我們需要實(shí)例化這個(gè) class(類),以使用非靜態(tài)方法
let r = new Repo()
console.log(r.useName()) //Repo name is modern-js-cheatsheet and it contains some really important stuff
  1. ###### 實(shí)用構(gòu)造函數(shù)

靜態(tài)方法可以在構(gòu)造函數(shù)對(duì)象上作為屬性被調(diào)用。

class Repo{
 static getName() {
 return "Repo name is modern-js-cheatsheet"
 }

 useName(){
 //作為構(gòu)造函數(shù)的屬性來調(diào)用靜態(tài)方法
 return this.constructor.getName() + ' and it contains some really important stuff'
 }
}

// 我們需要實(shí)例化這個(gè) class(類),以使用非靜態(tài)方法 
let r = new Repo()
console.log(r.useName()) //Repo name is modern-js-cheatsheet and it contains some really important stuff

擴(kuò)展閱讀

詞匯表

作用域(Scope)

在上下文之中有著 “可見的 (visible)” 值和表達(dá)式,又或者是可以被引用的。如果變量或是表達(dá)式并不在 “當(dāng)前作用域中”,那么它將會(huì)是不可用的。

來源: MDN

變量的可變性(Variable mutation)

如果變量在其初始值后發(fā)生變化時(shí)我們就稱其為可變的。

var myArray = [];
myArray.push("firstEl") // myArray 發(fā)生改變

如果一個(gè)變量不能被改變的話,我們會(huì)說這個(gè)變量是 不可變的 (immutable) 。

查看 MDN Mutable 文章 了解更多詳情。

原項(xiàng)目

mbeaudru / modern-js-cheatsheet
Cheatsheet for the JavaScript knowledge you will frequently encounter in modern projects.
https://mbeaudru.github.io/modern-js-cheatsheet/

相關(guān)文章

最新評(píng)論