溫馨提示×

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

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

對(duì)現(xiàn)代JavaScript的了解有多少

發(fā)布時(shí)間:2021-09-15 10:55:28 來(lái)源:億速云 閱讀:100 作者:柒染 欄目:web開(kāi)發(fā)

今天就跟大家聊聊有關(guān)現(xiàn)代JavaScript ,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

概念

變量聲明: var, const, let

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

簡(jiǎn)單說(shuō)明

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

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


作用域可否重新分配可變性暫時(shí)性死區(qū)
constBlockNoYesYes
letBlockYesYesYes
varFunctionYesYesNo
簡(jiǎn)單的示例
const person = "Nick";
person = "John" // 將會(huì)引起錯(cuò)誤,person 不能重新分配
let person = "Nick";
person = "John";
console.log(person) // "John", 使用 let 允許重新分配
詳細(xì)說(shuō)明

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

var

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

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

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

繼續(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 語(yǔ)句塊中的指令是如何影響這個(gè)值的 
}
console.log(myVar); // 拋出錯(cuò)誤 ReferenceError, 在函數(shù)之外 myVar 則無(wú)法訪問(wèn)

此外,在執(zhí)行過(guò)程中,var聲明的變量被移動(dòng)到范圍的頂部。這就是我們所說(shuō)的變量聲明提升(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 )

  • 在被分配之前, 無(wú)法 訪問(wèn)使用

  • 在同一個(gè)作用域之下,不能重新聲明

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

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

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

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

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

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

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

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

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

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

  • 塊級(jí)作用域

  • 分配之前無(wú)法訪問(wèn)

  • 在同一個(gè)作用域內(nèi)部,不能重新聲明

  • 不能被重新分配

const myVar = "Nick";
myVar = "John" // 拋出一個(gè)錯(cuò)誤, 不允許重新分配
const myVar = "Nick";
const myVar = "John" // 拋出一個(gè)錯(cuò)誤, 不允許重新聲明

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

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

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

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

const person = [];
person.push('John'); // 這是有效的!person 變量并非完全重新分配,只是值被改變
console.log(person[0]) // "John"
person = ["Nick"] // 拋出一個(gè)錯(cuò)誤, 因?yàn)橛?nbsp;const 聲明的變量不能被重新分配
擴(kuò)展閱讀
  • How let and const are scoped in JavaScript – WesBos

  • Temporal Dead Zone (TDZ) Demystified

箭頭函數(shù)

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

  • 更簡(jiǎn)潔

  • this 的值繼承自外圍的作用域

  • 隱式返回

簡(jiǎn)單的示例
  • 簡(jiǎn)潔性和隱式返回

function double(x) { return x * 2; } // 傳統(tǒng)的方法
console.log(double(2)) // 4
const double = x => x * 2; // 相同的函數(shù)寫(xiě)成帶有隱式返回的箭頭函數(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ì)說(shuō)明
簡(jiǎn)潔性

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

  • 隱式 VS 顯式返回

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

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

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

要做隱式回傳,代碼必須用一行語(yǔ)句寫(xiě)完。

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

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

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

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

注意: 如果你的函數(shù)沒(méi)有回傳一個(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ù)
  • 沒(méi)有參數(shù)

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

 () => { // 提供括號(hào),一切都能正常運(yùn)行
 const x = 2;
 return x;
 }
 => { // 沒(méi)有括號(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,而是從它的外圍作用域中抓取的。

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

舉例來(lái)說(shuō),你在 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 -- 見(jiàn)上訴函數(shù)聲明
 },
 0
 );
}

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

function myFunc() {
 this.myVar = 0;
 setTimeout(
 () => { // this 值來(lái)自它的外圍作用域, 在這個(gè)示例中,也就是 myFunc 函數(shù)
  this.myVar++;
  console.log(this.myVar) // 1
 },
 0
 );
}
有用的資源
  • Arrow functions introduction – WesBos

  • JavaScript arrow function – MDN

  • Arrow function and lexical this

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

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

function myFunc(x = 10) {
 return x;
}
console.log(myFunc()) // 10 -- 沒(méi)有提供任何值,所以在 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)見(jiàn)下文

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

  • 沒(méi)有提供參數(shù)

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

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

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

擴(kuò)展閱讀
  • Default parameter value – ES6 Features

  • Default parameters – MDN

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

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

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

用示例代碼說(shuō)明
  • 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; // 就是這么簡(jiǎn)單!

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)?nbsp;person.city 為 undefined(未定義) ,所以 city 將等同于默認(rèn)值也就是 "Paris"。

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

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

解構(gòu)(structuring) 經(jīng)常被用來(lái)解構(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è)更簡(jiǎn)潔的函數(shù):

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

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

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

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; // 就是這么簡(jiǎn)單!

console.log(x) // "a"
console.log(y) // "b"
有用的資源
  • ES6 Features – Destructuring Assignment

  • Destructuring Objects – WesBos

  • ExploringJS – Destructuring

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

map,filterreduce 都是數(shù)組提供的方法,它們?cè)醋杂?函數(shù)式編程

總結(jié)一下:

  • Array.prototype.map() 接受一組數(shù)組,在其元素上執(zhí)行某些操作,并返回具有轉(zhuǎn)換后的元素?cái)?shù)組。

  • Array.prototype.filter() 接受一組數(shù)組,依照元素本身決定是否保留,并返回一個(gè)僅包含保留元素的數(shù)組。

  • Array.prototype.reduce() 接受一組數(shù)組,將這些元素合并成單個(gè)值(并返回)。

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

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

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

  • JavaScript 中的 Currying(柯里化) 和 Partial Application(偏函數(shù)應(yīng)用)

  • 一步一步教你 JavaScript 函數(shù)式編程(第一部分)

  • 一步一步教你 JavaScript 函數(shù)式編程(第二部分)

  • 一步一步教你 JavaScript 函數(shù)式編程(第三部分)

  • JavaScript 函數(shù)式編程術(shù)語(yǔ)大全

簡(jiǎn)單的示例
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

通過(guò)組合 map,filter 和 reduce 來(lái)計(jì)算 10 分以上的學(xué)生成績(jī)總和 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ù)組映射到他們成績(jī)的數(shù)組中
 .filter(grade => grade >= 10) // 我們過(guò)濾成績(jī)數(shù)組以保持10分以上的元素
 .reduce((prev, next) => prev + next, 0); // 我們將合計(jì)所有10分以上的成績(jī)

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

讓我們考慮一下下列數(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ā)生了什么?我們?cè)?numbers 這個(gè)數(shù)組中使用 .map 方法,map 將會(huì)迭代數(shù)組的每個(gè)元素,并傳遞給我們的函數(shù)。該函數(shù)的目標(biāo)是生成并返回一個(gè)新的值,以便 map 可以替換掉原本的數(shù)組。

我們來(lái)解釋一下這個(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]

我們?cè)?numbers 數(shù)組中使用 .filter 方法,過(guò)濾器遍歷數(shù)組中的每個(gè)元素,并將其傳遞給我們的函數(shù)。函數(shù)的目標(biāo)是返回一個(gè)布爾值,它將確定當(dāng)前值是否被保留。過(guò)濾之后返回的數(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ù)的返回值。在迭代過(guò)程的第一步,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ò)展閱讀
  • Understanding map / filter / reduce in JS

展開(kāi)操作符 “…”

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

簡(jiǎn)單的代碼示例
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 }
說(shuō)明
應(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 展開(kāi)(spread)arr2。

通過(guò)展開(kāi)操作符:

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

但是如果說(shuō),我們希望創(chuàng)造的是一個(gè)包含各科成績(jī)和平均成績(jī)的新學(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ì)算平均成績(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)槲覀儾](méi)有去檢查 grades.length 是否存在又或者它等于 0 的情況。但是這個(gè)例子現(xiàn)在這樣寫(xiě),能夠更好的幫助我們理解剩余參數(shù)的運(yùn)作,所以我沒(méi)有處理上述的這種情況。

對(duì)象屬性展開(kāi)

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

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ì)象的屬性展開(kāi)到 n 中
擴(kuò)展資源
  • TC39 – Object rest/spread

  • Spread operator introduction – WesBos

  • JavaScript & the spread operator

  • 6 Great uses of the spread operator

對(duì)象屬性簡(jiǎn)寫(xiě)

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

const x = 10;
const myObj = { x };
console.log(myObj.x) // 10
說(shuō)明

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

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

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

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

const x = 10;
const y = 20;

const myObj = {
 x,
 y
};

console.log(myObj.x) // 10
console.log(myObj.y) // 20
擴(kuò)展資源
  • Property shorthand – ES6 Features

Promises

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

可以使用 Promises 來(lái)避免 回調(diào)地獄 (callback hell) ,而且它們?cè)诂F(xiàn)代 JavaScript 項(xiàng)目中越來(lái)越頻繁地遇到。

簡(jiǎn)單的代碼示例
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));
說(shuō)明

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

為了處理這類(lèi)情況,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ò)誤),我們必須通過(guò)執(zhí)行以下操作來(lái)附加處理程序:

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)用,
因此在異步操作完成和其處理程序被附加之間沒(méi)有競(jìng)爭(zhēng)條件。 (參考: MDN)(MDN)。

擴(kuò)展閱讀
  • JavaScript Promises for dummies – Jecelyn Yeen

  • JavaScript Promise API – David Walsh

  • Using promises – MDN

  • What is a promise – Eric Elliott

  • JavaScript Promises: an Introduction – Jake Archibald

  • Promise documentation – MDN

模板字符串

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

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

簡(jiǎn)單的代碼示例
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ò)展閱讀
  • String interpolation – ES6 Features

  • ES6 Template Strings – Addy Osmani

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

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

注意: 名為styled-components的著名庫(kù)很大程度上依賴(lài)于此功能。

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

function highlight(strings, ...values) {
 // 愚人碼頭注:為了更好的理解函數(shù)的參數(shù),我增加了這樣兩行代碼,特殊說(shuō)明見(jiàn)示例代碼下面的說(shuō)明;
 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>."

——-愚人碼頭注開(kāi)始——-

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

標(biāo)簽函數(shù)的第一個(gè)參數(shù)是一個(gè)包含了字符串字面值的數(shù)組(在本例中分別為”I like “,” on “和”.”)。如果我們這樣調(diào)用 highlight`I like ${condiment} on ${meal}` (注意最后面沒(méi)”.”),那么第一個(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)過(guò)轉(zhuǎn)義之前在 模板字符串(Template literals) 中由用戶輸入的字符串。我們來(lái)看一個(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è)字符串長(zhǎng)度是不同的。

String.raw 是ES2015,內(nèi)置對(duì)象 String 的一個(gè)靜態(tài)方法,把它作為T(mén)ag,可以做到只替換嵌入表達(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ī)避問(wèn)題

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

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

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

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

不再展開(kāi),具體參考: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ò)展閱讀
  • Wes Bos on Tagged Template Literals

  • Library of common template tags

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

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

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

用示例代碼說(shuō)明
命名導(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)入 -- 類(lèi)似于解構(gòu)語(yǔ)法
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)入看起來(lái)像是 解構(gòu)(destructuring),但它們具有不同的語(yǔ)法,并且不一樣。 他們不支持默認(rèn)值,也不支持深層次的解構(gòu)。

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

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ù),類(lèi),對(duì)象或其他任何東西。這個(gè)值被認(rèn)為是“主要”的導(dǎo)出值,因?yàn)樗鼘⑹亲詈?jiǎn)單的導(dǎo)入。 參考: MDN。

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

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

// myFile.js
import number from './coolNumber.js';
// 默認(rèn)導(dǎo)出,將獨(dú)立于其名稱(chēng),自動(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ò)展閱讀
  • ECMAScript 6 Modules(模塊)系統(tǒng)及語(yǔ)法詳解

  • ES6 Modules in bulletpoints

  • Export – MDN

  • Import – MDN

  • Understanding ES6 Modules

  • Destructuring special case – import statements

  • Misunderstanding ES6 Modules – Kent C. Dodds

  • Modules in JavaScript

JavaScript 中的 this

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

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

// 在嚴(yán)格模式下(strict-mode)
myFunc("hello") // undefined -- myFunc() 是 myFunc.call(undefined, "hello") 的語(yǔ)法糖
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") 的語(yǔ)法糖

var myBoundFunc = person.myFunc.bind("hello") // 創(chuàng)造了一個(gè)函數(shù),并且把 "hello" 注入到 *this* 
person.myFunc("test") // person 對(duì)象 -- bind 方法對(duì)原有方法并無(wú)造成影響
myBoundFunc("test") // "hello" -- myBoundFunc 是把帶有 "hello" 的 person.myFunc 綁定到 *this*
擴(kuò)展閱讀
  • Understanding JavaScript Function Invocation and “this” – Yehuda Katz

  • JavaScript this – MDN

類(lèi)(Class)

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

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

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

  • Understanding Prototypes in JS – Yehuda Katz

  • A plain English guide to JS prototypes – Sebastian Porto

  • Inheritance and the prototype chain – MDN

簡(jiǎn)單的示例

ES6 之前,原型語(yǔ)法:

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 類(lèi)(class)* 語(yǔ)法:

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ò)展閱讀

更好的理解原型:

  • Understanding Prototypes in JS – Yehuda Katz

  • A plain English guide to JS prototypes – Sebastian Porto

  • Inheritance and the prototype chain – MDN

更好的理解類(lèi):

  • ES6 Classes in Depth – Nicolas Bevacqua

  • ES6 Features – Classes

  • JavaScript Classes – MDN

Extendssuper 關(guān)鍵字

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

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)用父類(lèi)構(gòu)造函數(shù)。 如果要將類(lèi)的構(gòu)造函數(shù)中的一些參數(shù)傳遞給其父構(gòu)造函數(shù),則可以使用 super(arguments) 來(lái)調(diào)用它。

  • 如果父類(lèi)有一個(gè) X 的方法(甚至靜態(tài)),可以使用 super.X() 在子類(lèi)中調(diào)用。

簡(jiǎn)單的代碼示例
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)用父類(lèi)的構(gòu)造函數(shù)的 length, 
 // 提供給 Polygon 的 width 和 height。
 super(length, length);
 // 注意: 在派生的類(lèi)中, 在你可以使用 'this' 之前, 必須先調(diào)用 super() 。
 // 忽略這個(gè), 這將導(dǎo)致引用錯(cuò)誤。
 this.name = 'Square';
 this.length = length;
 }

 getCustomHelloPhrase() {
 const polygonPhrase = super.getHelloPhrase(); // 通過(guò) super.X() 語(yǔ)法訪問(wèn)父級(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 并可以訪問(wèn)其方法
console.log(mySquare.getCustomHelloPhrase()) // 'Hi, I am a Square with a length of 10'

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

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

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

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

  • Super operator – MDN

  • Inheritance – MDN

異步和等待(Async Await)

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

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

注意: 您必須了解什么是 promises 以及它們是如何工作的,然后再?lài)L試了解 async / await ,因?yàn)樗鼈円蕾?lài)于 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)。

簡(jiǎn)單的代碼示例
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ǔ)法,因?yàn)榇舜a不在 async 函數(shù)中 
 .catch(err => console.log(err)); // 如果在我們的異步函數(shù)中拋出一個(gè)錯(cuò)誤,我們將在這里捕獲它
用示例代碼說(shuō)明

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

async 操作符將一個(gè)函數(shù)標(biāo)記為異步,并將始終返回一個(gè) Promise 。你可以在 async 函數(shù)中使用 await 操作符來(lái)暫停該行代碼的執(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 語(yǔ)句時(shí),將會(huì)使用返回的值來(lái) fulfilled Promise。 如果在 async 函數(shù)中拋出錯(cuò)誤,則 Promise 狀態(tài)將轉(zhuǎn)為 rejected 。 如果沒(méi)有從 async 函數(shù)返回任何值,則在執(zhí)行 async 函數(shù)完成時(shí),仍然會(huì)返回 Promise 并 resolves 為無(wú)值。

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

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

首先,我們來(lái)看看如何通過(guò) promises 來(lái)獲取一個(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)相互依賴(lài)的 promises 時(shí),async / await 語(yǔ)法特別方便 。

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

注意: 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ò)誤處理

除非我們?cè)?await 表達(dá)式外面包裹 try / catch 語(yǔ)句塊,否則不會(huì)捕獲異常 – 不管它們是在你的異步函數(shù)中被拋出還是在 await 期間被暫停 – 他們將 reject async 函數(shù)返回的承諾。
async 函數(shù)中使用 throw 語(yǔ)句與返回 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ò)展閱讀
  • 使用 ES2017 中的 Async(異步) 函數(shù) 和 Await(等待)

  • ES2017 新特性:Async Functions (異步函數(shù))

  • Async/Await – JavaScript.Info

  • ES7 Async/Await

  • 6 Reasons Why JavaScript's Async/Await Blows Promises Away

  • JavaScript awaits

  • Using Async Await in Express with Node 8

  • Async Function

  • Await

  • Using async / await in express with node 8

真值/假值(Truthy / Falsy)

在 JavaScript 中,truthy 或 falsy 值是在布爾上下文中求值時(shí)被轉(zhuǎn)換為布爾值的值。布爾上下文的一個(gè)最常見(jiàn)的例子是求值 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
  • 通過(guò) Boolean 對(duì)象構(gòu)造函數(shù)

new Boolean(0) // false
new Boolean(1) // true
  • 在一個(gè)三元表達(dá)式求值時(shí)

myVar ? "truthy" : "falsy"

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

擴(kuò)展閱讀
  • Truthy (MDN)

  • Falsy (MDN)

  • Truthy and Falsy values in JS – Josh Clanton

靜態(tài)方法

簡(jiǎn)單說(shuō)明

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

簡(jiǎn)單的代碼示例
class Repo{
 static getName() {
 return "Repo name is modern-js-cheatsheet"
 }
}

// 請(qǐng)注意,我們不必創(chuàng)建 Repo 類(lèi)的實(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ì)說(shuō)明

靜態(tài)方法可以通過(guò)使用 this 關(guān)鍵字在另一個(gè)靜態(tài)方法中調(diào)用,這不適用于非靜態(tài)方法。非靜態(tài)方法無(wú)法使用 this 關(guān)鍵字直接訪問(wè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)方法可以通過(guò)2種方式調(diào)用靜態(tài)方法;

  1. ###### 使用 class(類(lèi)) 名

要在非靜態(tài)方法訪問(wèn)靜態(tài)方法,我們可以使用類(lè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(類(lèi)),以使用非靜態(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ù)的屬性來(lái)調(diào)用靜態(tài)方法
 return this.constructor.getName() + ' and it contains some really important stuff'
 }
}

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

  • Static Methods- Javascript.info

  • Static Members in ES6- OdeToCode

詞匯表

作用域(Scope)

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

來(lái)源: MDN

變量的可變性(Variable mutation)

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

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

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

看完上述內(nèi)容,你們對(duì)現(xiàn)代JavaScript 有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

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

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

AI