2015 年是銘心刻骨的一年,這一年,股市崩盤,千古跌停,手裡的兩個票更是挨了腰斬,寶寶心裡苦。同是這一年,ES6 標準落地,作為一名前端開發,寶寶心裡又樂開了花。 ...
閱讀目錄
我是 Jser 我驕傲
JavaScript 如今可謂是屌絲逆襲高富帥的代名詞哈,從當初鬧著玩似的誕生到現在 Github 上力壓群雄的人氣王,JS 搭著互聯網的順風車一路高歌,本身也從一個爺爺不疼奶奶不愛的殺馬特小腳本蛻變為一門高大上的主流編程語言。運氣固然重要,ES6 也讓大家看到了這門語言自身的努力和上進,相信 JavaScript 定大有可為。
ES6 發佈於 2015 年 6 月,因此也叫 ES2015,距今已有兩年多。2015 年是銘心刻骨的一年,這一年,股市崩盤,千古跌停,手裡的兩個票更是挨了腰斬,寶寶心裡苦。同是這一年,ES6 標準落地,作為一名前端開發,寶寶心裡又樂開了花。
ES6 的新語法很多很碎,如果想系統地學習一遍,強烈推薦阮一峰老師的 ECMAScript 6 入門。本文的目的不是要介紹 ES6 的新語法,而是想談談 ES6 為JavaScript 這門語言本身帶來了什麼,同時這也是在咱看來 ES6 最重要最激動人心的特性。
作用域
作用域一直是程式設計語言的基礎設施。JavaScript 也有作用域,但它的作用域有點怪,稍不留神就會掉到坑裡。
JavaScript 的作用域(scope)有三種類型,分別是 Local,Closure 和 Global,對應的三種變數類型分別是局部變數,閉包變數和全局變數。
比如下麵一段代碼:
var a = 'global'
function outer(){
var b = 'closure'
return function inner(){
var c = 'local'
console.log(a + ':' + b + ':' + c)
}
}
outer()()
在 Chrome 控制台執行上面代碼,當執行 outer()()
時,a
和 outer
都是全局變數,瀏覽器環境中的全局變數全部掛在全局 window
對象上。b
是閉包變數, 對 outer
函數內定義的函數內部保持可見。c
則是局部變數,只對 inner
函數內部可見。
目前為止看上去似乎沒什麼問題,接下來看點有問題的:
if(true){
var _a = 1
}
console.log(_a)
正常情況下變數 _a
的值應該是 undefined
才對,然而不要以為你以為就是你以為的。在 JS 中 if
語句塊里定義的變數 _a
直接變成了全局變數,也就是通過 window._a
可訪問。如果是從C語言,Java等主流編程語言轉過來的開發者就會更加迷惑了,這簡直就是TMD不科學。
在 JS 中,諸如 for
,while
,switch
等可接大括弧的代碼塊都是不能定義局部變數的,在其中定義的變數通通會是全局變數,也就是說大家已經習以為常的塊作用域在 JavaScript 中是不存在的,能夠定義局部變數的只有函數這個第一公民。這不科學,不合理,不符合社會主義核心價值觀\抓狂。
一聲霹靂,ES6 落地,完美地彌補掉了上面 JavaScript 暴露的作用域缺陷。這就意味著從此以後你可以以為你以為的就是你以為的了。ES6 為了向後相容,var
還是原來的 var
保持不變,在此基礎上又新添了一個聲明變數的關鍵字 let
和一個聲明常量的關鍵字 const
。
{
let a = 1
}
console.log(a)
// Uncaught ReferenceError: a is not defined
完善的作用域機制可有效地避免命名污染,提高程式的可靠性,減少隱晦的bug,尤其是在開發大型多人協作項目時作用尤為明顯。
鑒於以上所分析的各種利弊,請自始至終地使用 let
,而不要再讓 var
這個混小子出現在視線里。用 var
危險,後果自負。
模塊系統
模塊化,模塊化,模塊化,重要的事情說三遍,這是工程化的第一要務,是控制軟體複雜度的最重要措施,沒有之一。然而 2015 年之前的 JavaScript 語言是沒有模塊系統的。C語言有 include
,Java 有 import
,連 CSS 都有個蹩腳的 @import
,wrnmmp,JavaScript 卻只有 undefined
。有條件要上,沒條件創造條件也要上。於是乎,JavaScript 社區里的各位勇士造出了各種各樣實現模塊化的輪子,AMD,CMD,UMD 全蹦出來了。不想用又苦於沒有好的解決方案,只能在內心吶喊,這不科學,不合理,不符合···\抓狂。
霹靂嘩啦,ES6 落地,帶來了原生的模塊系統,優雅又簡單,絲滑般的體驗沖淡了腰斬給咱帶來的痛,臉上又洋溢出了久違的笑容。
最基本的使用:
/* add.js */
export function add(a, b){
return a + b
}
上面定義並導出了一個 ES 模塊。接下來就可以在任一個邏輯文件里引用:
/* main.js */
import {add} from './add'
add(1, 3)
模塊系統哪家強,ES6 最猖狂。ES6 一齣,快刀斬亂麻,直接終結了前端長期的模塊化亂象,實現了大一統。唯一的遺憾是 Node.js 對 ES6 模塊系統的支持還不是無縫過渡。不過這個還好,畢竟 Node.js 的 CommonJS 用起來還是相當順手的。
有了語言層面的模塊系統,JavaScript 也在語言性質上完成了蛻變,不再是一個打遍互聯網醬油的小腳本,而成長為一門舉重若輕的主流編程語言。
模塊系統是大型項目不可或缺的標準配置,對項目的開發和維護都非常重要。所以把它列為 ES6 所帶來的最重要特性絲毫不為過。
類(Class)
其實這個特性相比前兩個倒是沒那麼重要,說白了,類(Class)做的就是 JS 原型(prototype)做的那點事——繼承,咱覺得這是一個沒它也行,有它更好的語法糖功能。之所以把它列在這是因為咱看 prototype 不爽很久了。
JS 基於原型的繼承機制借鑒自 Self 語言。正如 JS 的設計者所言“它是 C 語言和 Self 語言一葉情的產物”。原型繼承是很不直觀的,或者可以說是非主流的。面向對象程式設計中典型的繼承是基於類的繼承,例如 Java,C++ 等主流編程語言實現的均是基於類的繼承。
使用 prototype 會出現 jQuery 的 jQuery.prototype.init.prototype = jQuery.prototype
這種讓人瞬間石化的代碼。況且咱左看右看,上看下看也沒看出來原型繼承相較於類式繼承有啥優點可言,反而是越看越拙。咱第一眼看到 prototype 這貨就很不順眼,也許是因為從 C++ 開發過來的緣故吧。
好在 ES6 帶來了 class
,剛發現這個特性的時候咱樂的一宿沒睡著覺,有一種久別重逢,相見恨晚的趕腳。
來看個最簡單示例:
/* Shape.js */
export class Shape{
this.x = 0
this.y = 0
}
英雄聯盟里的盲僧說過“如果類不是為了繼承,那將毫無意義”。
/* main.js */
import {Point} from "./Point"
class Triangle extends Point{
constructor(){
super()
this.sides = 3
}
}
let a = new Triangle()
這代碼看著多直觀,多大方,多舒坦。面向對象的概念已經在前端被越來越廣泛的傳播開來,咱是非常推崇 class
的繼承方式的,不知道大家喜歡用哪個,反正在咱的代碼里已經是看不到 prototype 的影子了。
以上就是咱認為 ES6 帶來的三把火。肯定有人要質疑了“怎麼沒有 Promise 啊”,需要申明一下,Promise 在 node 偏後端開發中比較實用,在前端的日常開發中並不常用,而本文是從偏前端的角度來說的,這是咱這兩年開發中受益最大的三個特性。