摘要:但是,的本質仍然是函數,是構造函數的另外一種寫法。報錯原生構造函數的繼承對于一些原生的構造函數,比如,,,等,在是無法通過方法實現原生函數的內部屬性,原生函數內部的無法綁定,內部屬性獲得不了。
在沒有學習 ES6 之前,學習 React,真的是一件非常痛苦的事情。即使之前你對 ES5 有著很好的基礎,包括閉包、函數、原型鏈和繼承,但是 React 中已經普遍使用 ES6 的語法,包括 modules、class、箭頭函數等,還有 JSX 語法。所以,在學習 React 之前一定要先學習 ES6。
關于 ES6 你必須要知道的一個教程,ECMAScript 6入門。這本書對于 ES6 的講解非常詳細,一步一步跟著來,絕對會對 ES6 的語法都了解到。
學習 ES6,還要知道一個 ES6 的語法編譯器,Babel。ES6 出來很久了,并不是所有瀏覽器都支持,Babel 就可以把 ES6 代碼轉換成 ES5,讓所有瀏覽器都支持你寫的代碼。Babel 內嵌了對 JSX 的支持,學習 React 必備。在線實驗是一個 Babel 的在線編譯器,可以用來練習 ES6 語法,并實時觀測轉換成 ES5 的代碼效果。
準備工作做完了,接下來開始今天的主題,你不得不學的 ES6!
箭頭函數講真,自從出了箭頭函數之后,再也不用擔心 this 問題了,而且就簡化代碼這一方面來說,箭頭函數可謂是裝逼神器。
箭頭函數有幾點需要注意,如果 return 值就只有一行表達式,可以省去 return,默認表示該行是返回值,否則需要加一個大括號和 return。如果參數只有一個,也可以省去括號,兩個則需要加上括號。比如下面的例子:
var f = v => v*2; // 等價于 var f = function(v){ return v*2; } // 判斷偶數 var isEven = n => n % 2 == 0; // 需要加 return var = (a, b) => { if(a >= b) return a; return b; }
還有 this 的問題,我覺得這篇文章說的非常好。普通函數的 this 是可變的,我們把函數歸為兩種狀態,一種是定義時,一種是執行時,如果仔細研究會發現,函數中的 this 始終是指向函數執行時所在的對象。比如全局函數執行時,this 執行 window,對象的方法執行時,this 執行該對象,這就是函數 this 的可變。而箭頭函數中的 this 是固定的,看下面的例子:
function obj(){ setTimeout(()=>console.log(this.id), 20); } var id = 1; obj.call({id: 2}); // 2
執行的結果是 2 而不是全局的 1,表示 setTimeout 函數執行的時候,this 指向的不是 window,這和普通函數是有區別的。
實際上,箭頭函數并沒有 this 對象,將箭頭函數轉成 ES5 會發現:
// ES6 function obj() { setTimeout(()=>console.log(this.id), 20); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log(_this.id); }, 20); }
通過 call aply 等方法是無法綁定 箭頭函數中的 this:
var f = () => this.x; var x = 1; f.call({x: 2}); // 1
對 this 的一個總結就是 在對象的方法中直接使用箭頭函數,會指向 window,其他箭頭函數 this 會指向上一層的 this,箭頭函數并沒有存儲 this:
var obj = { id: 1, foo: ()=>{ return this.id; } } var id = 2; obj.foo(); // 2
除了 this 之外,箭頭函數的 arguments 也是不存在,不能使用 new 來構造,也不能使用 yield 命令。
class盼星星盼月亮,終于盼來了 JS 的繼承。但是 ES6 中的繼承和已經很完善的 ES5 中流行的繼承庫,到底有多少差異?
先來看一個例子:
//定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } // 注意函數構造的方式 toString() { return "(" + this.x + ", " + this.y + ")"; } } var p1 = new Point(5, 5); p1.toString(); //"(5, 5)" typeof Point // function p1.constructor == Point //true
直接使用 class 關鍵字,constructor 作為構造方法,函數可以直接 toString(){} 的方式。
但是,class 的本質仍然是函數,是構造函數的另外一種寫法。既然 class 的本質是函數,那么必不可少的一些 proto,prototype 方法也是存在的。
關于 class 的繼承通過關鍵字 extends 可以實現 class 的繼承,
class Square extends Point{ constructor(x){ super(x, x); } toString(){ return super.toString() + "Square!"; } } var s1 = new Square(4); s1.toString(); //"(4, 4)Square!" s1 instanceof Point // true s1 instanceof Square // true
既然說到了繼承,對 es5 中繼承了解到小伙伴,肯定會疑惑關于 class 中的 proto 和 prototype 是一個什么樣的關系。
子類的 proto 指向父類,子類的 prototype 的 proto 指向父類的 prototype,這和 ES5 并沒有區別。
Square.__proto__ === Point // true Square.prototype.__proto__ === Point.prototype // truesuper 關鍵字
在 Java 等語言中,是有 super 繼承父類函數,JS 中更加靈活,可以用作父類的構造函數,又可以用作對象。
子類的 constructor 必須要調用 super 方法,且只能在 constructor 方法中調用,其他地方調用會報錯。
class A { constructor(a){ this.x = a; } } A.prototype.y = 2; class B extends A{ constructor(a){ super(); } getY(){ super() // 報錯 return super.y } }原生構造函數的繼承
對于一些原生的構造函數,比如 Array,Error,Object,String 等,在 ES5 是無法通過 Object.create 方法實現原生函數的內部屬性,原生函數內部的 this 無法綁定,內部屬性獲得不了。原生構造函數的繼承。
ES6 的 class 可以解決這個問題。
class MyArray extends Array { constructor(...args) { super(...args); } } var arr = new MyArray(); arr[0] = 12; arr.length // 1 arr.length = 0; arr[0] // undefined
extends 關鍵字不僅可以用來繼承類,還能用來繼承原生的構造函數,在原生函數的基礎上,自定義自己的函數。
靜態方法ES6 支持 static 關鍵字,該關鍵字定義的方法,不會被實例繼承,但可以被子類繼承:
class A{ static add(x, y){ return x + y; } } A.add(1, 2); var a = new A(); a.add()// error class B extends A{} B.add(2, 2)// 4Module
ES6 之前,JS 一直沒有 modules 體系,解決外部包的問題通過 CommonJS 和 AMD 模塊加載方案,一個用于服務器,一個用于瀏覽器。ES6 提出的 modules (import/export)方案完全可以取代 CommonJS 和 AMD 成為瀏覽器和服務器通用的模塊解決方案。
關于模塊,就只有兩個命令,import 用于導入其他模塊,export 用于輸出模塊。
// profile.js var firstName = "Michael"; var lastName = "Jackson"; var year = 1958; export {firstName, lastName, year}; // main.js import {firstName, lastName, year} from "./profile"; console.log(firstName, lastName) // Michael Jackson
import 加載的模塊可以只加載用到的,但是必須使用同名的原則,可以用 as 來解決名字問題,同樣,as 也可以解決 export 問題:
//main.js import { lastName as surname } from "./profile"; console.log(surname); // Jackson //profile.js export {firstName as name}
export 可以輸出的內容很多,包括變量、函數、類,貌似都可以輸出,還可以借助 export default 來加載默認輸出。
//default.js function add(a, b){ return a + b; } export default add; // 實際上 export {add as default}; // main.js import add from "./default" //實際上 add 名字可以隨便起 import {default as add} from "./default"模塊加載的實質
這部分 ES6模塊加載的實質 完全只能參考了,因為對模塊加載用的不多,沒有一點經驗,但是看到作者提到了拷貝和引用,感覺逼格很高的樣子。
ES6模塊加載的機制,與CommonJS模塊完全不同。CommonJS模塊輸出的是一個值的拷貝,而ES6模塊輸出的是值的引用。
比如一個 CommonJS 加載的例子:
// lib.js var counter = 3; function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, }; // main.js var mod = require("./lib"); console.log(mod.counter); // 3 mod.incCounter(); console.log(mod.counter); // 3
這個值會被 mod 緩存,而取不到原始的值。
ES6 中不一樣,它只是生成一個引用,當真正需要的時候,才會到模塊里去取值,
// lib.js export let counter = 3; export function incCounter() { counter++; } // main.js import { counter, incCounter } from "./lib"; console.log(counter); // 3 incCounter(); console.log(counter); // 4循環加載
循環加載也比較有意思,經常能看到 nodejs 中出現加載同一個模塊,而循環加載卻不常見,nodejs 使用 CommonJS 模塊機制,CommonJS 的循環加載采用的是加載多少,輸出多少,就像是我們平時打了斷點一樣,會跳到另外一個文件,執行完在跳回來。
//a.js exports.done = "1"; var a = require("./b.js"); console.log("half a=%s", a); exports.done = "3"; console.log("done a"); //b.js exports.done = "2"; var b = require("./a.js"); console.log("half b=%s", b); exports.done = "4"; console.log("done b"); //main.js var a = require("./a.js"); var b = require("./b.js"); console.log("all done! a=%s,b=%s",a,b)
node main.js 的結果:
half a=2 done a half b=3 done b all done! a=3,b=4
這就是 CommonJS 所謂的循環加載。
而 ES6 采用的加載模式也不一樣,因為使用動態引用,必須要開發者保證能 import 到值:
// a.js如下 import {bar} from "./b.js"; console.log("a.js"); console.log(bar); export let foo = "foo"; // b.js import {foo} from "./a.js"; console.log("b.js"); console.log(foo); export let bar = "bar";
結果:
$ babel-node a.js b.js undefined a.js bar
循環加載稍有不慎,就會 underfined。
字符串模版ES6 在字符串上面可是下了不少功夫,先是解決了字符 unicode 的 bug,增加了一些處理多字節字符串 codePointAt 函數,還多了字符串的遍歷接口 for...of,這個遍歷借口有點仿造 python 的感覺。只要有迭代器功能的對象,都可以用 for...of 來遍歷。
ES6 添加了一些有意思的函數,比如 repeat(),前幾天比較火的文章‘五道經典的前端面試題’,就有提到一個在字符串上實現原生的重復方法,這里的 repeat 可以直接解決。
關于字符串上的新內容,非常有幫助的還是模版字符串。之前在 js 中跨行的字符串實現起來很別扭,而 python 可以用三個反引號來實現。
ES6 中的模版字符串使用需要注意以下內容:
// ` 可以跨行 var html = `
用過 ejs 、swig 或 hbs 等模版,它們可以嵌入 js 代碼,ES6 的模版字符串也可以。使用 <%...%> 放置 JavaScript 代碼,使用 <%= ... %> 輸出 JavaScript 表達式。
var template = `
下面就可以寫正則表達式替換掉自定義字符并執行函數:
function compile(str){ var evalExpr = /<%=(.+?)%>/g; var expr = /<%([sS]+?)%>/g; str = str.replace(evalExpr, "`); join( $1 ); join(`") .replace(expr, "`); $1 join(`"); str = "join(`" + str + "`);"; var script = ` (function parse(data){ var output = ""; function join(html){ output += html; } ${ str } return output; }) ` return script; } var strParse = eval(compile(template)); // 使用 var html = strParse(["shanghai", "beijing", "nanjing"]); //
通過兩次使用字符串模版,并使用 eval 函數,一個 ES6 簡易模版就這樣完成了。
一些其他核心功能 let constES5 通過 var 來申明變量,ES6 新添 let 和 const,且作用域是 塊級作用域。
let 使用和 var 非常類似,let 不存在變量提升,也不允許重復申明,let 的聲明只能在它所在的代碼塊有效,比如 for 循環,非常適合使用 let:
for(let i = 0; i < data.length; i++){ console.log(data[i]); } console.log(i); // error
如果用 var 來申明 i,最后不會報錯。之前學閉包的時候,有一個利用閉包解決循環的問題,用 let 可以解決:
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
const 就是申明常量用的,一旦申明即被鎖定,后面無法更改。
const PI = 3.14; PI = 3; //error
let 和 const 都是塊級作用域,塊級作用域可以任意嵌套,且 {} 內定義的變量,外層作用域是無法獲得的,且內外層的作用域可以同名。
function fn() { let n = 1; if (true) { let n = 2; } console.log(n); // 1 }解構賦值
解構賦值真的很好用,但是我每次都忘記使用。ES6 解構賦值基本語法 var [a, b, c] = [1, 2, 3];,從數組中取值,并按照先后次序來賦值。如果解構賦值不成功,就會返回 underfined,解構賦值也允許指定默認值:
var [a, b] = [1]; b // undefined // 指定默認值 var [a, b = 2] = [1]; b // 2
除了數組,對象也可以解構賦值,但是數組是有順序的,而對象沒有順序,如果想要成功賦值,必須與對象屬性同名,才能成功賦值,否則返回 underfined:
var {a, b} = {a: 1, b: 2}; a // 1 b // 2 var {a, c} = {a: 1, b: 2}; c // undefined
字符串的解構賦值比較有意思,既可以把字符串當作可以迭代的數組,又可以當作對象,比如:
var [a1,a2,a3,a4,a5] = "hello"; a2 // e var {length : len} = "hello"; len // 5
函數參數的解構賦值,看一個 forEach 的例子:
var data = [[1, 2], [3, 4]]; data.forEach(([a, b]) => console.log(a+b)); // 3 // 7Promise 解決回掉
一直以來,回掉問題都是一件令人頭疼的事,調試的時候感覺代碼跳來跳去,玩著玩著就暈了。ES6 提供 Promise 對象(函數),專門用來處理回掉。
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(error) { // failure });
resolve 和 reject 是兩個異步操作調用函數,當異步操作完成時,調用 resolve,error 則調用 reject,這兩個函數的功能就是把參數傳遞給回掉函數。then 函數用來處理成功或失敗狀態。
function loadImageAsync(url) { var p = new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(url); }; image.src = url; }); p.then(function(image){ document.body.appendChild(image); }, function(url){ throw new Error("Could not load "+ url); }) } loadImageAsync("http://yuren.space/images/bg.gif");
上面是一個用 Promise 實現的異步加載圖片的函數。
for of 與 ...Python 中有 for in 運算符,ES6 就搞了個 for...of。當使用 for...of 循環遍歷某種數據結構時,該循環會自動去尋找 Iterator 接口。一種數據結構只要部署了 Iterator 接口,我們就稱這種數據結構是可遍歷的,對象、數組、字符串都是可遍歷的。
var str = "hello"; for(let i of str){ console.log(i); } // "h" "e" "l" "l" "o"
...也非常好用,可以直接把可遍歷對象直接轉換成數組:
var str = "hello"; [...str] //["h", "e", "l", "l", "o"] let arr = ["b", "c"]; ["a", ...arr, "d"] // ["a", "b", "c", "d"]
有了 ... 之后,方便對非數組可遍歷的對象進行轉換,比如 arguments 和 querySelectAll 的結果:
[...arguments] // Array var selects = document.querySelectAll("a"); [...selects] // Arrayset 集合和 Map 結構
ES6 新增 Set 集合對象,其實像其他語言早都支持了,不過,吃瓜群眾,不覺明厲,以后,再遇到數組去重算法題,就可以:
[...(new Set([1, 2, 2, 3]))]; //[1, 2, 3]
Set 方法分為操作和遍歷,操作方法有 add-添加成員, delete-刪除成員, has-擁有判斷返回布爾值, clear-清空集合。
遍歷操作有 keys(),values(),entries(),forEach(),...,for of,map 和 filter 函數也可以用于 Set,不過要進行巧妙操作,先轉換成數組,在進行操作:
let set = new Set([1,2,3]); set = new Set([...set].map(a => a*2)); // Set {2, 4, 6}
Map 用來解決對象只接受字符串作為鍵名,Map 類似于對象,也是鍵值對集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。
Map 可以通過 [set、 get、 has、 delete] 方法來操作:
var m = new Map(); var arr = [1, 2]; m.set(arr, "array"); m.get(arr); // "array" m.has(arr) // true m.delete(arr) // true m.has(arr) // false參數默認
參數默認這個功能使用起來還是比較方便的,以前參數都是通過 || 來實現默認,現在可以使用默認參數。不過這個功能在 Python 等語言中已經是支持的。
// 以前寫代碼 var sayHello = function(name){ var name = name || "world"; console.log("hello " + name); } //參數默認 var sayHello = function(name = "world"){ console.log("hello " + name); } sayHello() // "hello world" sayHello("ES6") // "hello ES6"
對于不定參數,以前都是對 arguments 對象處理,且 arguments 對象還是個偽數組,現在方便了:
var add = function(...arr){ console.log(arr.constructor.name) // Array return arr.reduce((a, b) => a+b, 0); } add(1,2,3) // 6總結
總之,對于 ES6 的學習還是要活用,當我看了一遍 ECMAScript 6入門時候,感覺知識點還是很多,有點亂。當接觸了 react 之后,發現很多語法都非常的熟悉,于是就從頭溫習了 ES6,并整理了這篇文章。可能,你還不知道,這篇文章,大部分都是參考阮一峰老師的。共勉!
參考ECMAScript 6入門
30分鐘掌握ES6/ES2015核心內容(上)
30分鐘掌握ES6/ES2015核心內容(下)
ES6 學習筆記
ES6新特性概覽
ES6js.com 官網
歡迎來我博客交流。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86495.html
摘要:我的第一個項目是一個基于和的后臺管理系統,當時我還沒有任何的前端開發知識,時間也比較緊,就在學習和的基礎后,有針對性的對和進行了了解。 第一篇博文,寫在從零開始學前端的兩個月后,期間經過了春節,之后又經歷了一些動蕩。算是在邊做邊學中堅持下來,現在基本上可以完成一些業務邏輯上的開發工作。想到應該總結一下這兩個月的學習,也是對自己的知識掌握情況做一個梳理。 我的第一個項目是一個基于vue和...
摘要:首先,要確認安裝了,并且創建了目錄并執行初始化。想必看見上面的那么多包會一臉懵逼,沒關系,我第一眼看見這些的那刻,和你現在的表情一樣,下面在適當的時候我會逐個解釋的,你只需要相信我上面的包都是跑所必須的,缺一不可。 關于介紹,只說一句:Angular 2是一個強大、全面、龐大的MVVM框架。 安裝 安裝,也算是一個坎,因為你需要安裝一大堆東西,卻不知道每個東西是做什么的,盡管有Angu...
摘要:我從今年的月份開始在知乎上連續回答前端開發相關的問題,至今已有將近三個月,回顧寫過的一百多條回答,不少是給迷茫的前端工作者的建議。今天我把我的思考提煉整理成文,希望能給予在迷茫中前行中的前端學習工作者一些有用的建議。 本文首發于知乎專欄——前端指南作者:Mark MFS老師轉載請注明來源。 我從今年的2月份開始在知乎上連續回答前端開發相關的問題,至今已有將近三個月,回顧寫過的一百多條回...
摘要:命令用來聲明變量,它的用法類似,但是命令聲明的變量只在所在的代碼塊中有效。不允許重復聲明不允許在同一作用域聲明兩個相同的變量。對于內部的數據結構的變化是無法控制的。 let命令 用來聲明變量,它的用法類似var,但是let命令聲明的變量只在所在的代碼塊中有效。 { var a = 1; let b = 2; } console.log(a); // 1 con...
摘要:不過今天我希望能夠更進一步,不僅僅再抱怨現狀,而是從我個人的角度來給出一個逐步深入學習生態圈的方案。最后,我還是想提到下對于的好的學習方法就是回顧參照各種各樣的代碼庫,學習人家的用法與實踐。 本文翻譯自A-Study-Plan-To-Cure-JavaScript-Fatigue。筆者看到里面的幾張配圖著實漂亮,順手翻譯了一波。本文從屬于筆者的Web Frontend Introduc...
閱讀 1010·2021-11-22 13:52
閱讀 924·2019-08-30 15:44
閱讀 570·2019-08-30 15:43
閱讀 2424·2019-08-30 12:52
閱讀 3473·2019-08-29 16:16
閱讀 637·2019-08-29 13:05
閱讀 2943·2019-08-26 18:36
閱讀 1974·2019-08-26 13:46