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