ES6补充
let var const?區(qū)別
var可以重復(fù)定義
let只可以定義一次,否則報(bào)錯(cuò)
const?擁有?let?的所有優(yōu)點(diǎn),不同的是,通過?const?聲明的變量是只讀的。?
const?聲明并不會(huì)真的保護(hù)數(shù)據(jù)不被改變。 為了確保數(shù)據(jù)不被改變,JavaScript 提供了一個(gè)函數(shù)?Object.freeze()
var被視作全局變量
如果你創(chuàng)建一個(gè)函數(shù),將它存儲(chǔ)起來,稍后在使用?i?變量的?for?循環(huán)中使用。這么做可能會(huì)出現(xiàn)問題。 這是因?yàn)榇鎯?chǔ)的函數(shù)會(huì)總是指向更新后的全局?i?變量的值。
var printNumTwo; for (var i = 0; i < 3; i++) {if (i === 2) {printNumTwo = function() {return i;};} } console.log(printNumTwo());這里控制臺(tái)將顯示值?3。
可以看到,printNumTwo()?打印了 3,而不是 2。 這是因?yàn)橘x值給?i?的值已經(jīng)更新,printNumTwo()?返回全局的?i,而不是在 for 循環(huán)中創(chuàng)建函數(shù)時(shí)?i?的值。?let?關(guān)鍵字就不會(huì)出現(xiàn)這種現(xiàn)象:
let printNumTwo; for (let i = 0; i < 3; i++) {if (i === 2) {printNumTwo = function() {return i;};} } console.log(printNumTwo()); console.log(i);在這里控制臺(tái)將顯示值?2?和一個(gè)錯(cuò)誤提示?i is not defined。
i?未定義,因?yàn)樗鼪]有在全局范圍內(nèi)聲明。 它只在?for?循環(huán)語句中被聲明。?printNumTwo()?返回了正確的值,因?yàn)?let?關(guān)鍵字在循環(huán)語句中使?i?變量產(chǎn)生了三個(gè)不同的值(分別為 0、1、2)。
開發(fā)者會(huì)用大寫字母作為常量標(biāo)識(shí)符,用小寫字母或者駝峰命名作為變量(對(duì)象或數(shù)組)標(biāo)識(shí)符。
對(duì)象(包括數(shù)組和函數(shù))在使用?const?聲明的時(shí)候依然是可變的。 使用?const?來聲明只會(huì)保證變量不會(huì)被重新賦值。
箭頭函數(shù)
ES6 提供了其他寫匿名函數(shù)的方式的語法糖。 你可以使用箭頭函數(shù):
const myFunc = () => {const myVar = "value";return myVar; }當(dāng)不需要函數(shù)體,只返回一個(gè)值的時(shí)候,箭頭函數(shù)允許你省略?return?關(guān)鍵字和外面的大括號(hào)。 這樣就可以將一個(gè)簡單的函數(shù)簡化成一個(gè)單行語句。
const myFunc = () => "value";這段代碼默認(rèn)會(huì)返回字符串?value。
和一般的函數(shù)一樣,你也可以給箭頭函數(shù)傳遞參數(shù)。
const doubler = (item) => item * 2; doubler(4);doubler(4)?將返回?8。
如果箭頭函數(shù)只有一個(gè)參數(shù),則可以省略參數(shù)外面的括號(hào)。
const doubler = item => item * 2;可以給箭頭函數(shù)傳遞多個(gè)參數(shù)。
const multiplier = (item, multi) => item * multi; multiplier(4, 2);ES6 里允許給函數(shù)傳入默認(rèn)參數(shù),來構(gòu)建更加靈活的函數(shù)。
請(qǐng)看以下代碼:
const greeting = (name = "Anonymous") => "Hello " + name;console.log(greeting("John")); console.log(greeting());控制臺(tái)將顯示字符串?Hello John?和?Hello Anonymous。
?rest
?rest 操作符可以用于創(chuàng)建有一個(gè)變量來接受多個(gè)參數(shù)的函數(shù)。 這些參數(shù)被儲(chǔ)存在一個(gè)可以在函數(shù)內(nèi)部讀取的數(shù)組中。
請(qǐng)看以下代碼:
function howMany(...args) {return "You have passed " + args.length + " arguments."; } console.log(howMany(0, 1, 2)); console.log(howMany("string", null, [1, 2, 3], { }));控制臺(tái)將顯示字符串?You have passed 3 arguments.?和?You have passed 4 arguments.。
使用 rest 參數(shù),就不需要查看?args?數(shù)組,并且允許我們?cè)趨?shù)數(shù)組上使用?map()、filter()?和?reduce()。
spread
下面的 ES5 代碼使用了?apply()?來計(jì)算數(shù)組的最大值:
var arr = [6, 89, 3, 45]; var maximus = Math.max.apply(null, arr);maximus?的值為?89。
我們必須使用?Math.max.apply(null, arr),因?yàn)?Math.max(arr)?返回?NaN。?Math.max()?函數(shù)中需要傳入的是一系列由逗號(hào)分隔的參數(shù),而不是一個(gè)數(shù)組。 展開操作符可以提升代碼的可讀性,使代碼易于維護(hù)。
const arr = [6, 89, 3, 45]; const maximus = Math.max(...arr);maximus?的值應(yīng)該是?89。
...arr?返回一個(gè)解壓的數(shù)組。 也就是說,它展開數(shù)組。 然而,展開操作符只能夠在函數(shù)的參數(shù)中或者數(shù)組中使用。 下面的代碼將會(huì)報(bào)錯(cuò):
const spreaded = ...arr;解構(gòu)賦值
解構(gòu)賦值是 ES6 引入的新語法,用來從數(shù)組和對(duì)象中提取值,并優(yōu)雅地對(duì)變量進(jìn)行賦值。
有如下 ES5 代碼:
const user = { name: 'John Doe', age: 34 };const name = user.name; const age = user.age;name?的值應(yīng)該是字符串?John Doe,?age?的值應(yīng)該是數(shù)字?34。
下面是使用 ES6 解構(gòu)賦值語句,實(shí)現(xiàn)相同效果:
const { name, age } = user;同樣,name?的值應(yīng)該是字符串?John Doe,?age?的值應(yīng)該是數(shù)字?34。
?從對(duì)象中分配變量
可以給解構(gòu)的值賦予一個(gè)新的變量名, 通過在賦值的時(shí)候?qū)⑿碌淖兞棵旁诿疤?hào)后面來實(shí)現(xiàn)。
還是以上個(gè)例子的對(duì)象來舉例:
const user = { name: 'John Doe', age: 34 };這是指定新的變量名的例子:
const { name: userName, age: userAge } = user;你可以這么理解這段代碼:獲取?user.name?的值,將它賦給一個(gè)新的變量?userName,等等。?userName?的值將是字符串?John Doe,userAge?的值將是數(shù)字?34。
嵌套對(duì)象解構(gòu)
使用與前面的例子中類似的對(duì)象:
const user = {johnDoe: { age: 34,email: 'johnDoe@freeCodeCamp.com'} };這是解構(gòu)對(duì)象的屬性值賦值給具有相同名字的變量:
const { johnDoe: { age, email }} = user;這是將對(duì)象的屬性值賦值給具有不同名字的變量:
const { johnDoe: { age: userAge, email: userEmail }}?分離數(shù)組變量
與數(shù)組解構(gòu)不同,數(shù)組的擴(kuò)展運(yùn)算會(huì)將數(shù)組里的所有內(nèi)容分解成一個(gè)由逗號(hào)分隔的列表。 所以,你不能選擇哪個(gè)元素來給變量賦值。
而對(duì)數(shù)組進(jìn)行解構(gòu)卻可以讓我們做到這一點(diǎn):
const [a, b] = [1, 2, 3, 4, 5, 6]; console.log(a, b);控制臺(tái)將顯示?a?和?b?的值為?1, 2。
數(shù)組的第一個(gè)值被賦值給變量?a,數(shù)組的第二個(gè)值被賦值給變量?b。 我們甚至能在數(shù)組解構(gòu)中使用逗號(hào)分隔符,來獲取任意一個(gè)想要的值:
const [a, b,,, c] = [1, 2, 3, 4, 5, 6]; console.log(a, b, c);控制臺(tái)將顯示?a、b?和?c?的值為?1, 2, 5。
函數(shù)傳參
在某些情況下,你可以在函數(shù)的參數(shù)里直接解構(gòu)對(duì)象。
請(qǐng)看以下代碼:
const profileUpdate = (profileData) => {const { name, age, nationality, location } = profileData;}上面的操作解構(gòu)了傳給函數(shù)的對(duì)象。 這樣的操作也可以直接在參數(shù)里完成:
const profileUpdate = ({ name, age, nationality, location }) => {}當(dāng)?profileData?被傳遞到上面的函數(shù)時(shí),從函數(shù)參數(shù)中解構(gòu)出值以在函數(shù)內(nèi)使用。
模板字符串
模板字符串可以使用多行字符串和字符串插值功能。
請(qǐng)看以下代碼:
const person = {name: "Zodiac Hasbro",age: 56 };const greeting = `Hello, my name is ${person.name}! I am ${person.age} years old.`;console.log(greeting);控制臺(tái)將顯示字符串?Hello, my name is Zodiac Hasbro!?和?I am 56 years old.。
這里發(fā)生了許多事情。 首先,這個(gè)例子使用反引號(hào)(`),而不是引號(hào)('?或者?")將字符串括起來。 其次,注意代碼和輸出中的字符串都是多行的。 不需要在字符串中插入?\n。 上面使用的?${variable}?語法是一個(gè)占位符。 這樣一來,你將不再需要使用?+?運(yùn)算符來連接字符串。 當(dāng)需要在字符串里增加變量的時(shí)候,你只需要在變量的外面括上?${?和?},并將其放在模板字符串里就可以了。 同樣,你可以在字符串中包含其他表達(dá)式,例如?${a + b}。 這個(gè)新的方式使你可以更靈活地創(chuàng)建復(fù)雜的字符串。
使用簡潔的字面量聲明
const getMousePosition = (x, y) => ({x: x,y: y });getMousePosition?簡單的函數(shù),返回?fù)碛袃蓚€(gè)屬性的對(duì)象。 ES6 提供了一個(gè)語法糖,消除了類似?x: x?這種冗余的寫法。 你可以只寫一次?x,解釋器會(huì)自動(dòng)將其轉(zhuǎn)換成?x: x(或效果相同的內(nèi)容)。 下面是使用這種語法重寫的同樣的函數(shù):
const getMousePosition = (x, y) => ({ x, y });簡潔的函數(shù)聲明
const person = {name: "Taylor",sayHello: function() {return `Hello! My name is ${this.name}.`;} };用 ES6 的語法在對(duì)象中定義函數(shù)的時(shí)候,可以刪除?function?關(guān)鍵詞和冒號(hào)。 請(qǐng)看以下例子:
const person = {name: "Taylor",sayHello() {return `Hello! My name is ${this.name}.`;} };class類
constructor,然后使用?new?關(guān)鍵字來實(shí)例化一個(gè)對(duì)象:
var SpaceShuttle = function(targetPlanet){this.targetPlanet = targetPlanet; } var zeus = new SpaceShuttle('Jupiter');class?語法只是簡單地替換了構(gòu)造函數(shù)?constructor?的寫法:
class SpaceShuttle {constructor(targetPlanet) {this.targetPlanet = targetPlanet;} } const zeus = new SpaceShuttle('Jupiter');應(yīng)該注意?class?關(guān)鍵字聲明了一個(gè)新的函數(shù),里面添加了一個(gè)構(gòu)造函數(shù)。 當(dāng)用?new?創(chuàng)建一個(gè)新的對(duì)象時(shí),構(gòu)造函數(shù)會(huì)被調(diào)用。
**注意:**首字母大寫駝峰命名法 UpperCamelCase 是 ES6 class 命名的慣例,就像上面的?SpaceShuttle。
控制對(duì)象的訪問
Getter 函數(shù)的作用是可以讓對(duì)象返回一個(gè)私有變量,而不需要直接去訪問私有變量。
Setter 函數(shù)的作用是可以基于傳進(jìn)的參數(shù)來修改對(duì)象中私有變量。 這些修改可以是計(jì)算,或者是直接替換之前的值。
注意:?通常會(huì)在私有變量前添加下劃線(_)。 然而,這種做法本身并不是將變量變成私有的。
?插入HTML
?需要在 HTML 文檔里創(chuàng)建一個(gè)?type?為?module?的腳本。 例子如下:
<script type="module" src="filename.js"></script>?函數(shù)導(dǎo)入
假設(shè)有一個(gè)文件?math_functions.js,該文件包含了數(shù)學(xué)運(yùn)算相關(guān)的一些函數(shù)。 其中一個(gè)存儲(chǔ)在變量?add?里,該函數(shù)接受兩個(gè)數(shù)字作為參數(shù)返回它們的和。 你想在幾個(gè)不同的 JavaScript 文件中使用這個(gè)函數(shù)。 要實(shí)現(xiàn)這個(gè)目的,就需要?export?它。
export const add = (x, y) => {return x + y; }上面是導(dǎo)出單個(gè)函數(shù)常用方法,還可以這樣導(dǎo)出:
const add = (x, y) => {return x + y; }export { add }; import { add } from './math_functions.js';在這里,import?會(huì)在?math_functions.js?里找到?add,只導(dǎo)入這個(gè)函數(shù),忽略剩余的部分。?./?告訴程序在當(dāng)前文件的相同目錄尋找?math_functions.js?文件。 用這種方式導(dǎo)入時(shí),相對(duì)路徑(./)和文件擴(kuò)展名(.js)都是必需的。
下面是一個(gè)從同目錄下的?math_functions.js?文件中導(dǎo)入所有內(nèi)容的例子:
import * as myMathModule from "./math_functions.js";export default
還需要了解另外一種被稱為默認(rèn)導(dǎo)出的?export?的語法。 在文件中只有一個(gè)值需要導(dǎo)出的時(shí)候,通常會(huì)使用這種語法。 它也常常用于給文件或者模塊創(chuàng)建返回值。
下面是使用?export default?的例子:
export default function add(x, y) {return x + y; }export default function(x, y) {return x + y; }?import default
在下面的例子里,add?是?math_functions.js?文件的默認(rèn)導(dǎo)出。 以下是如何導(dǎo)入它:
import add from "./math_functions.js";這個(gè)語法有一處特別的地方, 被導(dǎo)入的?add?值沒有被花括號(hào)({})所包圍。?add?只是一個(gè)變量的名字,對(duì)應(yīng)?math_functions.js?文件的任何默認(rèn)導(dǎo)出值。 在導(dǎo)入默認(rèn)導(dǎo)出時(shí),可以使用任何名字。
promise
Promise 有三個(gè)狀態(tài):pending、fulfilled?和?rejected。
?Promise 成功時(shí)調(diào)用?resolve,promise 執(zhí)行失敗時(shí)調(diào)用?reject
Promise 是異步編程的一種解決方案 - 它在未來的某時(shí)會(huì)生成一個(gè)值。 任務(wù)完成,分執(zhí)行成功和執(zhí)行失敗兩種情況。?Promise?是構(gòu)造器函數(shù),需要通過?new?關(guān)鍵字來創(chuàng)建。 構(gòu)造器參數(shù)是一個(gè)函數(shù),該函數(shù)有兩個(gè)參數(shù) -?resolve?和?reject。 通過它們來判斷 promise 的執(zhí)行結(jié)果。 用法如下:
const myPromise = new Promise((resolve, reject) => {}); const myPromise = new Promise((resolve, reject) => {if(condition here) {resolve("Promise was fulfilled");} else {reject("Promise was rejected");} });then
當(dāng)程序需要花費(fèi)未知的時(shí)間才能完成時(shí)(比如一些異步操作),一般是服務(wù)器請(qǐng)求,promise 很有用。 服務(wù)器請(qǐng)求會(huì)花費(fèi)一些時(shí)間,當(dāng)結(jié)束時(shí),需要根據(jù)服務(wù)器的響應(yīng)執(zhí)行一些操作。 這可以用?then?方法來實(shí)現(xiàn), 當(dāng) promise 完成?resolve?時(shí)會(huì)觸發(fā)?then?方法。 例子如下:
myPromise.then(result => {});result?即傳入?resolve?方法的參數(shù)。
catch
當(dāng) promise 失敗時(shí)會(huì)調(diào)用?catch?方法。 當(dāng) promise 的?reject?方法執(zhí)行時(shí)會(huì)直接調(diào)用。 用法如下:
myPromise.catch(error => {});error?是傳入?reject?方法的參數(shù)。
同步異步?
舉個(gè)例子來說,一家餐廳吧來了5個(gè)客人,同步的意思就是說,來第一個(gè)點(diǎn)菜,點(diǎn)了個(gè)魚,好, 廚師去捉魚殺魚,過了半小時(shí)魚好了給第一位客人,開始下位一位客人,就這樣一個(gè)一個(gè)來,按順序來
相同,?異步呢,異步的意思就是來第一位客人,點(diǎn)什么,點(diǎn)魚,給它一個(gè)牌子,讓他去一邊等吧,下一位客人接著點(diǎn)菜,點(diǎn)完接著點(diǎn)讓廚師做去吧,哪個(gè)的菜先好就先端出來,
同步的優(yōu)點(diǎn)是:同步是按照順序一個(gè)一個(gè)來,不會(huì)亂掉,更不會(huì)出現(xiàn)上面代碼沒有執(zhí)行完就執(zhí)行下面的代碼, 缺點(diǎn):是解析的速度沒有異步的快;
異步的優(yōu)點(diǎn)是:異步是接取一個(gè)任務(wù),直接給后臺(tái),在接下一個(gè)任務(wù),一直一直這樣,誰的先讀取完先執(zhí)行誰的, 缺點(diǎn):沒有順序 ,誰先讀取完先執(zhí)行誰的 ,會(huì)出現(xiàn)上面的代碼還沒出來下面的就已經(jīng)出來了,會(huì)報(bào)錯(cuò);
總結(jié)
- 上一篇: js(菜鸡视角)
- 下一篇: 殊途同归的fork()