本文最初發表於 "博客園" ,併在 "GitHub" 上持續更新 前端的系列文章 。歡迎在GitHub上關註我,一起入門和進階前端。 以下是正文。 前言 ECMAScript 是 JS 的語言標準。而 ES6 是新的 JS 語法標準。 PS:嚴格來說,ECMAScript 還包括其他很多語言的語言標 ...
以下是正文。
前言
ECMAScript 是 JS 的語言標準。而 ES6 是新的 JS 語法標準。
PS:嚴格來說,ECMAScript 還包括其他很多語言的語言標準。
ECMAScript 發展歷史
1995年:ECMAScript 誕生。
1997年:ECMAScript 標準確立。
1999年:ES3 出現,與此同時,IE5 風靡一時。
2009年,ES5 出現,例如 foreach、Object.keys、Object.create 和 json 標準。
2015年6月,ES6正式發佈。
ES6 的目標是:讓 JS 語言可以編寫複雜的大型應用程式,成為企業級開發語言。
ES6 的其他優勢
使用 babel 語法轉換器,支持低端瀏覽器。
流行的庫基本都是基於 ES6 構建。 React 預設使用 ES6 標準開發。
ES6的環境配置
掌握 ES6 之後,如果要考慮 ES5 的相容性,可以這樣做:寫 ES6 語法的 js 代碼,然後通過 Babel
將 ES6 轉換為 ES5。
但是,在這之前,我們需要配置一下相關的環境。
建立工程目錄
(1)先建立一個空的工程目錄 ES6Demo
,併在目錄下建立兩個文件夾 src
和 dist
:
src
:書寫ES6代碼,我們寫的 js 程式都放在這裡。dist
:利用 Babel 編譯生成的 ES5 代碼。我們在 HTML 頁面需要引入 dist 里的 js 文件。
(2)在 src 里新建文件 index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 我們引入 ES5 中的 js 文件,而不是引入 ES6 中的 js 文件。 -->
<script src="./dist/index.js"></script>
</head>
<body>
</body>
</html>
註意,上方代碼中,我們引入的是dist
目錄下的 js 文件。
然後我們新建文件 src/index.js
:
let a = 'smyhvae';
const b = 'vitateam';
console.log(a);
console.log(b);
這個文件是一個 ES6語法 的js文件,稍後,我們嘗試把這個 ES6 語法的 js 文件轉化為 ES5 的 js 文件。
PS:我們在寫代碼時,能用單引號儘量用單引號,而不是雙引號,前者在壓縮之後,程式執行會更快。
全局安裝 Babel-cli
(1)初始化項目:
在安裝Babel之前,需要先用 npm init 先初始化我們的項目。打開終端或者通過cmd打開命令行工具,進入項目目錄,輸入如下命令:
npm init -y
上方代碼中,-y
代表全部預設同意,就不用一次次按回車了(稍後再根據需要,在文件中手動修改)。命令執行完成後,會在項目的根目錄下生成package.json文件:
{
"name": "es6demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "smyhvae",
"license": "ISC"
}
PS:VS Code 里打開終端的快捷鍵是:Contol + ~
。
(2)全局安裝 Babel-cli:
在終端中輸入以下命令:
npm install -g babel-cli
如果安裝比較慢的話,Mac 下可以使用cnpm
進行安裝 ,windows 下可以使用nrm
切換到 taobao 的鏡像。
(3)本地安裝 babel-preset-es2015 和 babel-cli:
npm install --save-dev babel-preset-es2015 babel-cli
安裝完成後,會發現package.json
文件,已經多了 devDependencies 選項:
(4)新建.babelrc:
在根目錄下新建文件.babelrc
,輸入如下內容:
{
"presets":[
"es2015"
],
"plugins":[]
}
(5)開始轉換:
現在,我們應該可以將 ES6 的文件轉化為 ES5 的文件了,命令如下:(此命令略顯複雜)
babel src/index.js -o dist/index.js
我們可以將上面這個命令進行簡化一下。操作如下:
在文件 package.json
中修改鍵 scripts
中的內容:
"scripts": {
"build": "babel src/index.js -o dist/index.js"
},
修改後的效果如下:
目前為止,環境配置好了。以後,我們執行如下命令,即可將src/index.js
這個 ES6 文件轉化為 dist/index.js
這個 ES5 文件:
npm run build
我們執行上面的命令之後,會發現, dist目錄下會生成 ES5 的 js 文件:
index.js:
'use strict';
var a = 'smyhvae';
var b = 'vitateam';
console.log(a);
console.log(b);
當我們打開網頁後,就可以在瀏覽器的控制台,看到代碼的輸出結果。
接下來,我們講一下 ES6的常見語法。
ES6 的變數聲明
ES6 中新增了 let 和 const 來定義變數:
var
:,ES5 和 ES6中,定義全局變數(是variable的簡寫)。let
:定義局部變數,替代 var。const
:定義常量(定義後,不可修改)。
var:全局變數
看下麵的代碼:
{
var a = 1;
}
console.log(a); //這裡的 a,指的是 區塊 里的 a
上方代碼是可以輸出結果的,輸出結果為 1。因為 var 是全局聲明的,所以,即使是在區塊里聲明,但仍然在全局起作用。
再來看下麵這段代碼:
var a = 1;
{
var a = 2;
}
console.log(a); //這裡的 a,指的是 區塊 里的 a
上方代碼的輸出結果為 2 ,因為 var 是全局聲明的。
總結:
用 var 定義的全部變數,有時候會污染整個 js 的作用域。
let:局部變數
var a = 2;
{
let a = 3;
}
console.log(a);
上方代碼的輸出結果為 2。用 let 聲明的變數,只在局部起作用。
let是防止數據污染,我們來看下麵這個例子:
用 var 聲明變數:(可以列印結果,說明迴圈體外的變數 i 被污染了)
for (var i = 0; i < 10; i++) {
console.log('迴圈體中:' + i);
}
console.log('迴圈體外:' + i);
用let聲明變數:(不能列印結果)
for (let i = 0; i < 10; i++) {
console.log('迴圈體中:' + i);
}
console.log('迴圈體外:' + i);
總結:我們要習慣用 let 聲明,減少var聲明帶來的污染全局空間。
為了進一步說明 let 不會帶來污染,需要說明的是:當我們定義了let a = 1
時,如果我們在同一個作用域內繼續定義let a = 2
,是會報錯的。
const:聲明常量
在程式開發中,有些變數是希望聲明後,在業務層就不再發生變化,此時可以用 const 來定義。
舉例:
const name = 'smyhvae'; //定義常量
變數的解構賦值
ES6允許我們,通過數組或者對象的方式,對一組變數進行賦值,這被稱為解構。
解構賦值在實際開發中可以大量減少我們的代碼量,並且讓程式結構更清晰。
數組的解構賦值
舉例:
通常情況下,我們在為一組變數賦值時,一般是這樣寫:
let a = 0;
let b = 1;
let c = 2;
現在我們可以通過數組解構的方式進行賦值:
let [a, b, c] = [1, 2, 3];
二者的效果是一樣的。
解構的預設值:
在解構賦值時,是允許使用預設值的。舉例如下:
{
//一個變數時
let [foo = true] = [];
console.log(foo); //輸出結果:true
}
{
//兩個變數時
let [a, b] = ['生命壹號'] //a 賦值為:生命壹號。b沒有賦值
console.log(a + ',' + b); //輸出結果:生命壹號,undefined
}
{
//兩個變數時
let [a, b = 'smyhvae'] = ['生命壹號'] //a 賦值為:生命壹號。b 採用預設值 smyhvae
console.log(a + ',' + b); //輸出結果:生命壹號,smyhvae
}
undefined
和null
的區別:
如果我們在賦值時,採用的是 undefined
或者null
,那會有什麼區別呢?
{
let [a, b = 'smyhvae'] = ['生命壹號', undefined]; //b 雖然被賦值為 undefined,但是 b 會採用預設值
console.log(a + ',' + b); //輸出結果:生命壹號,smyhvae
}
{
let [a, b = 'smyhvae'] = ['生命壹號', null]; //b 被賦值為 null
console.log(a + ',' + b); //輸出結果:生命壹號,null
}
上方代碼分析:
undefined:相當於什麼都沒有,此時 b 採用預設值。
null:相當於有值,但值為 null。
對象的解構賦值
我們同樣可以針對對象,進行結構賦值。
舉例如下:
let { foo, bar } = { bar: '我是 bar 的值', foo: '我是 foo 的值' };
console.log(foo + ',' + bar); //輸出結果:我是鍵 foo 的值,我是鍵 bar 的值
上方代碼可以看出,對象的解構與數組的結構,有一個重要的區別:數組的元素是按次序排列的,變數的取值由它的位置決定;而對象的屬性沒有次序,是根據鍵來取值的。
圓括弧的使用:
如果變數 foo 在解構之前就已經定義了,此時你再去解構,就會出現問題。下麵是錯誤的代碼,編譯會報錯:
let foo = 'haha';
{ foo } = { foo: 'smyhvae' };
console.log(foo);
要解決報錯,只要在解構的語句外邊,加一個圓括弧即可:
let foo = 'haha';
({ foo } = { foo: 'smyhvae' });
console.log(foo); //輸出結果:smyhvae
字元串結構
字元串也可以解構,這是因為,此時字元串被轉換成了一個類似數組的對象。舉例如下:
const [a, b, c, d] = 'smyhvae';
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(typeof a); //輸出結果:string
輸出結果:
擴展運算符和 rest 運算符
擴展運算符的格式為
...
rest運算符的格式為
...變數名
擴展運算符
有了ES6,當我們在定義一個方法,但是不確定其參數的個數時,我們就可以用擴展運算符作為參數。
以前,我們在定義方法時,參數要確定個數,如下:(程式會報錯)
function fn(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
console.log(d);
}
fn(1, 2, 3);
上方代碼中,因為方法的參數是三個,但使用時是用到了四個參數,所以會報錯:
現在,我們有了擴展運算符,就不用擔心報錯的問題了。代碼可以這樣寫:
function fn(...arg) { //當不確定方法的參數時,可以使用擴展運算符
console.log(arg[0]);
console.log(arg[1]);
console.log(arg[2]);
console.log(arg[3]);
}
fn(1, 2, 3); //方法中定義了四個參數,但只引用了三個參數,ES6 中並不會報錯。
舉例:數組賦值的問題
我們來分析一段代碼:(將數組 arr1 賦值給 arr2)
let arr1 = ['www', 'smyhvae', 'com'];
let arr2 = arr1; // 將 arr1 賦值給 arr2,其實是讓 arr2 指向 arr1 的記憶體地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');
arr2.push('你懂得'); //往arr2 里添加一部分內容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
運行結果:
上方代碼中,我們往往 arr2 里添加了你懂的
,卻發現,arr1 里也有這個內容。原因是:let arr2 = arr1;
其實是讓 arr2 指向 arr1 的地址。也就是說,二者指向的是同一個記憶體地址。
如果不想讓 arr1 和 arr2 指向同一個記憶體地址,我們可以藉助擴展運算符來做:
let arr1 = ['www', 'smyhvae', 'com'];
let arr2 = [...arr1]; //arr2 會重新開闢記憶體地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');
arr2.push('你懂得'); //往arr2 里添加一部分內容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
運行結果:
我們明白了這個例子,就可以避免開發中的很多業務邏輯上的 bug。
rest
運算符
rest
在英文中指的是剩餘部分(不是指休息)。我們來舉個例子,理解剩餘部分的含義:
function fn(first, second, ...arg) {
console.log(arg.length);
}
fn(0, 1, 2, 3, 4, 5, 6); //調用函數後,輸出結果為 5
上方代碼的輸出結果為 5。 調用fn()
時,裡面有七個參數,而arg
指的是剩下的部分(因為除去了firt
和second
)。
從上方例子中可以看出,rest
運算符適用於:知道前面的一部分參數的數量,但對於後面剩餘的參數數量未知的情況。
for ... of 迴圈
ES6 中,如果我們要遍歷一個數組,可以這樣做:
let arr1 = [1, 2, 3, 4, 5];
for (let value of arr1) {
console.log(value);
}
輸出結果:
for…of 的迴圈可以避免我們開拓記憶體空間,增加代碼運行效率,所以建議大家在以後的工作中使用for…of迴圈。
模板字元串
我們以前讓字元串進行拼接的時候,是這樣做的:(傳統寫法的字元串拼接)
var name = 'smyhvae';
var age = '26';
console.log('name:'+name+',age:'+age); //傳統寫法
這種寫法,比較繁瑣,而且容易出錯。
現在有了 ES6 語法,字元串拼接可以這樣寫:
var name = 'smyhvae';
var age = '26';
console.log('name:'+name+',age:'+age); //傳統寫法
console.log(`name:${name},age:${age}`); //ES6 寫法
註意,上方代碼中,倒數第二行用的是單引號,最後一行用的是反引號(在tab鍵的上方)。
參考鏈接:
箭頭函數
定義和調用函數:(傳統寫法)
function fn1(a, b) {
return a + b;
}
console.log(fn1(1, 2)); //輸出結果:3
定義和調用函數:(ES6中的寫法)
var fn2 = (a, b) => a + b;
console.log(fn2(1, 2)); //輸出結果:3
上方代碼中,箭頭後面的內容,就相當於 return 的內容。
在箭頭函數中,如果方法體內有兩句話,那就需要在方法體外邊加上{}括弧。如下:
var fn2 = (a, b) => {
console.log('haha');
return a + b;
};
console.log(fn2(1, 2)); //輸出結果:3
從上面的箭頭函數中,我們可以很清晰地找到函數名、參數名、方法體。
當然,在 ES6 中定義方法時,我們還可以給方法里的參數加一個預設值(預設值):
方法被調用時,如果沒有給參數賦值,那就是用預設值;
方法被調用時,如果給參數賦值了新的值,那就用新的值。
如下:
var fn2 = (a, b = 5) => {
console.log('haha');
return a + b;
};
console.log(fn2(1)); //第二個參數使用預設值 5。輸出結果:6
console.log(fn2(1, 8)); //輸出結果:9
更多 ES6 的語法,本文會陸續更新。
參考鏈接:
我的公眾號
想學習代碼之外的軟技能?不妨關註我的微信公眾號:生命團隊(id:vitateam
)。
掃一掃,你將發現另一個全新的世界,而這將是一場美麗的意外: