es6涉及的那點東西
前言
ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準。因為當前版本的ES6是在2015年釋出的,所以又稱ECMAScript 2015。
也就是說,ES6就是ES2015。
目前並不是所有瀏覽器都能相容ES6全部特性,我們可以使用Babel工具來轉換成es5
Babel是一個廣泛使用的ES6轉碼器,可以將ES6程式碼轉為ES5程式碼
最常用的ES6特性
let, const, class, extends, super, arrow functions, template string, destructuring, default, rest arguments
一、let 、const命令
1、let用法類似於var,但是所宣告的變數,只在let命令所在的程式碼塊內有效
2、不存在變數提升,let不像var那樣會發生“變數提升”現象。所以,變數一定要在聲明後使用,否則報錯。
console.log(foo); // 輸出undefined console.log(bar); // 報錯ReferenceError var foo =2;let bar =2;
3、在let和const之間,建議優先使用const,尤其是在全域性環境,不應該設定變數,只應設定常量。
const宣告常量還有兩個好處,一是閱讀程式碼的人立刻會意識到不應該修改這個值,二是防止了無意間修改變數值所導致的錯誤。
4、ES6明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。
總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。也就是“暫時性死區”。
二、es6模組(module)
JavaScript一直沒有模組(module)體系,無法將一個大程式拆分成互相依賴的小檔案,再用簡單的方法拼裝起來。其他語言都有這項功能,比如Ruby的require、Python的import,甚至就連CSS都有@import,但是JavaScript任何這方面的支援都沒有,這對開發大型的、複雜的專案形成了巨大障礙。
這有可能導致兩個問題:
- 一方面js程式碼變得很臃腫,難以維護
- 另一方面我們常常得很注意每個script標籤在html中的位置,因為它們通常有依賴關係,順序錯了可能就會出bug
- 在es6之前為解決上面提到的問題,我們得利用第三方提供的一些方案,主要有兩種CommonJS(伺服器端)和AMD(瀏覽器端,如require.js)。
在ES6之前,社群制定了一些模組載入方案,最主要的有CommonJS和AMD兩種。前者用於伺服器,後者用於瀏覽器。ES6在語言規格的層面上,實現了模組功能,而且實現得相當簡單,完全可以取代現有的CommonJS和AMD規範,成為瀏覽器和伺服器通用的模組解決方案。
傳統的寫法
首先我們回顧下require.js的寫法。假設我們有兩個js檔案: index.js和content.js,現在我們想要在index.js中使用content.js返回的結果,我們要怎麼做呢?
首先定義:
//content.js define('content.js', function(){ return'A cat'; })
然後require:
//index.js require(['./content.js'], function(animal){ console.log(animal);//A cat })
CommonJS寫法
//index.js var animal = require('./content.js') //content.js module.exports = 'A cat'
ES6的寫法
//index.js import animal from'./content'//content.js export default'A cat'
// ES6模組
import { stat, exists, readFile } from 'fs';
上面程式碼的實質是從fs模組載入3個方法,其他方法不載入。這種載入稱為“編譯時載入”,即ES6可以在編譯時就完成模組載入,效率要比CommonJS模組的載入方式高。當然,這也導致了沒法引用ES6模組本身,因為它不是物件。
瀏覽器使用ES6模組的語法如下。
<script type="module" src="foo.js"></script>
上面程式碼在網頁中插入一個模組foo.js,由於type屬性設為module,所以瀏覽器知道這是一個ES6模組。
Node的預設模組格式是CommonJS,目前還沒決定怎麼支援ES6模組。所以,只能通過Babel這樣的轉碼器,在Node裡面使用ES6模組。
ES6的模組自動採用嚴格模式,不管你有沒有在模組頭部加上"use strict";。
1、export命令
模組功能主要由兩個命令構成:export和import。export命令用於規定模組的對外介面,import命令用於輸入其他模組提供的功能。
一個模組就是一個獨立的檔案。該檔案內部的所有變數,外部無法獲取。如果你希望外部能夠讀取模組內部的某個變數,就必須使用export關鍵字輸出該變數。例如,
// profile.js export var firstName ='Michael'; export var lastName ='Jackson'; export var year =1958;
也可以是下面這種寫法(優先考慮使用),
// profile.js var firstName ='Michael';var lastName ='Jackson';var year =1958; export {firstName, lastName, year};
export命令除了輸出變數,還可以輸出函式或類(class)。
export function multiply(x, y){return x * y;};
可以使用as關鍵字重新命名
function v1(){...} function v2(){...} export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };
注意:export命令可以出現在模組的任何位置,只要處於模組頂層就可以。如果處於塊級作用域內,就會報錯,import命令也是如此,這是因為處於條件程式碼塊之中,就沒法做靜態優化了,違背了ES6模組的設計初衷。下面程式碼中,export語句放在函式之中,結果報錯。
function foo(){ export default 'bar' // SyntaxError } foo()
2、import命令
使用export命令定義了模組的對外介面以後,其他JS檔案就可以通過import命令載入這個模組(檔案)。
import {firstName, lastName, year} from './profile';
上面程式碼的import命令,就用於載入profile.js檔案,並從中輸入變數。import命令接受一個物件(用大括號表示),裡面指定要從其他模組匯入的變數名
使用as關鍵字將輸入的變數重新命名
import { lastName as surname } from './profile';
import命令具有提升效果,會提升到整個模組的頭部,首先執行。
foo(); import { foo } from 'my_module';
上面的程式碼不會報錯,因為import的執行早於foo的呼叫。
import語句會執行所載入的模組,但是不輸入任何值。
import 'lodash';
import 也可以引用資料夾/目錄
如import router from './router'
這裡router是個目錄 裡面有個index.js檔案
3、模組整體載入
circle模組有2個方法
// circle.js export function area(radius){return Math.PI * radius * radius;} export function circumference(radius){return2* Math.PI * radius;} 通過使用*號載入整個模組 import * as circle from './circle'; console.log('圓面積:'+ circle.area(4)); console.log('圓周長:'+ circle.circumference(14));
三、class類
1、class的基本用法
ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為物件的模板。通過class關鍵字,可以定義類
//定義類 class Point{ constructor(x, y){ this.x = x; this.y = y; } toString(){ return'('+this.x +', '+this.y +')'; } }
constructor這就是構造方法,而this關鍵字則代表例項物件
定義“類”的方法的時候,前面不需要加上function這個關鍵字,直接把函式定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。
使用時直接對類使用new命令
class Bar{ doStuff(){ console.log('stuff'); } } var b =new Bar(); b.doStuff() // "stuff"
2、constructor構造方法
constructor方法是類的預設方法,通過new命令生成物件例項時,自動呼叫該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被預設新增。
constructor(){}
3、class表示式
與函式一樣,類也可以使用表示式的形式定義。
const MyClass = class Me{ getClassName(){ return Me.name; } };
這個類的名字是MyClass而不是Me,Me只在Class的內部程式碼可用,指代當前類。
如果類的內部沒用到的話,可以省略Me
const MyClass = class {/* ... */};
採用Class表示式,可以寫出立即執行的Class。
let person =newclass{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } }('張三'); person.sayName(); // "張三"
4、class類繼承
通過extends關鍵字實現繼承
class ColorPoint extends Point{}
四、arrow function箭頭函式
function(i){ return i + 1; } //ES5 (i) => i + 1//ES6
如果方程比較複雜,則需要用{}把程式碼包起來:
function(x, y){ x++; y--; return x + y; } (x, y) => { x++; y--; return x+y }
在物件方法中使用this
傳統的寫法需要將this傳給self,再用self來指代this
says(say){ var self = this; setTimeout(function(){ console.log(self.type + ' says ' + say) }, 1000)
使用es6的寫法
classAnimal { constructor(){ this.type = 'animal' } says(say){ setTimeout( () => { console.log(this.type + ' says ' + say) }, 1000) } } var animal = new Animal() animal.says('hi')//animal says hi
當我們使用箭頭函式時,函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,它的this是繼承外面的,因此內部的this就是外層程式碼塊的this。
五、template string
當我們要插入大段的html內容到文件中時,傳統的寫法非常麻煩,所以之前我們通常會引用一些模板工具庫,比如mustache等等
$("#result").append( "There are <b>" + basket.count + "</b> " + "items in your basket, " + "<em>" + basket.onSale + "</em> are on sale!" );
後 ``` $("#result").append(
There are${basket.count} items
in your basket,${basket.onSale}
are on sale!
); ``` 用反引號(\)來標識起始,用${}
來引用變數,而且所有的空格和縮排都會被保留在輸出之中
六、default, rest
default很簡單,意思就是預設值,呼叫animal()方法時忘了傳引數,傳統的做法就是加上這一句type = type || 'cat' 來指定預設值
function animal(type){ type = type || 'cat' console.log(type) } animal()
用ES6我們而已直接這麼寫:
function animal(type = 'cat'){ console.log(type) } animal()
rest語法也很簡單,看例子:
function animals(...types){ console.log(types) } animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]
參考閱讀
https://segmentfault.com/a/1190000004365693#articleHeader7
http://es6.ruanyifeng.com/