摘要:凡是部署了屬性的數據結構,就稱為部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
ES6新特性列表ES6在2015年6月就得以批準,至今已兩年了。近一年多以來陸續看過很多ES6的資料,工作項目中也逐步的用上了很多ES6的特性(let,const,promise,Template strings,Class,箭頭函數等等),不得不說,這些特性給開發帶來了非常多的便利。但是做決定我的ES6知識其實并不夠系統,這也是寫本文的初衷,希望閱讀本文能讓你也能對ES6有更系統的理解,本文并不是那種大而全的教程,而是希望在實際工作中,能想起某個新特性可以解決你當前的問題或者優化當前的代碼,之后再系統學習,應用,用過了肯定就會真的掌握了。
本文基于Github上的高贊文章ECMAScript 6 Features/Babel修改過的Learn ES2015,翻譯(寫作?)期間,重溫了阮一峰老師的ECMAScript 6 入門。
相比ES5,ES6提供了太多的更新,簡單說來,主要為以下方面(大家可以依據自己不算清晰的點選擇性查看本文):
Arrows,箭頭函數,
Classes,類
Enhanced object literals,增強的對象字面值
Template strings:模板字符串
Destructuring:解構
Default + rest + spread:參數默認值,rest參數,擴展運算符
Let + const:命名聲明的新方式
Iterators + for..of:遍歷器
Generators:生成器
Unicode:更廣泛的編碼支持
Modules:語言層面上支持的模塊機制
Module loaders:模塊加載器
Map + set + weakmap + weakset:新的數據結構
Proxies:代理器
Symbols:新的基本類型,獨一無二的值
Subclassable built-ins:類的繼承
Promises:
Math + number + string + array + object apis:拓展了一些內置對象的方法
Binary and octal literals:二進制八進制字面量
Reflect api:操作對象的新api
Tail calls:尾調用
Arrows箭頭函數箭頭函數使用類似于=>這樣的語法定義函數,支持表達式模式和語句模式,不過其最大特點在于和父作用域具有一樣的this。我們知道普通函數的this 既不指向函數自身也不指向函數的詞法作用域,this 實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用。使用箭頭函數時再也不用擔心this跳來跳去了。
此外如果箭頭函數如果定義在另一個函數里面,箭頭函數會共享它父函數的arguments變量。
// 表達式模式箭頭函數 var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i); var pairs = evens.map(v => ({even: v, odd: v + 1})); // 語句模式箭頭函數 nums.forEach(v => { if (v % 5 === 0) fives.push(v); }); // 和父作用域具有相同的this var bob = { _name: "Bob", _friends: [], printFriends() { this._friends.forEach(f => console.log(this._name + " knows " + f)); } } function square() { let example = () => { let numbers = []; for (let number of arguments) { numbers.push(number * number); } return numbers; }; return example(); } square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]Classes
JavaScript中其實并不存在真正的類,ES6的類其實是基于原型鏈模擬面向對象的一種語法糖。其本質上可以看做是構造函數的另一種寫法。
與真的類一樣,它支持super繼承,實例,靜態方法和constructor方法。
如果你也使用React,工作中定義模塊時一定沒少寫過class A extends React.Component{}吧。
// 定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } // 通過extends關鍵字實現繼承 class SkinnedMesh extends THREE.Mesh { //constructor方法是類的默認方法,通過new命令生成對象實例時,自動調用該方法。 //一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認添加。 constructor(geometry, materials) { // super表示父類的構造函數,用來新建父類的this對象, // 子類必須在constructor方法中調用super方法,否則新建實例時會報錯。如果不調用super方法,子類就得不到this對象。 super(geometry, materials); //在構造方法中綁定this,可以防止實例找不到this this.idMatrix = SkinnedMesh.defaultMatrix(); this.bones = []; this.boneMatrices = []; //... } // 非定義在this上的方法都會被直接定義在原型鏈上 update(camera) { //... // super在此處作為對象,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。 super.update(); } // 可以使用get和set關鍵字,對某個屬性設置存值函數和取值函數 get boneCount() { // 類的方法內部如果含有this,它默認指向類的實例 return this.bones.length; } set matrixType(matrixType) { this.idMatrix = SkinnedMesh[matrixType](); } // 加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用 static defaultMatrix() { return new THREE.Matrix4(); } } // 類的所有實例共享一個原型對象 let skin = new SkinnedMesh(); // 靜態方法需要直接通過類調用 SkinnedMesh.defaultMatrix()對象的拓展
ES6中對象的使用方法得以拓展,主要包括以下幾點:
屬性和方法可以簡潔表示;
允許以表達式的模式定義屬性名;
可以通過__proto__讀取或設置當前對象的prototype對象;
使用Object.is({},{})判斷兩個對象是否完全相對,類似于===;
Object.assign(target, source1, source2)合并對象;(淺拷貝)
var obj = { // __proto__用以設置當前對象的prototype對象,不推薦使用,推薦使用Object.setPrototypeOf() __proto__: theProtoObj, //‘handler:handler’可簡寫為handler(只需要寫變量名就可以實現變量名為變量名,變量值為屬性值) handler, // 簡寫在定義方法的時候同樣有效 toString() { // Super calls return "d " + super.toString(); }, // 方括號內的表達式用以計算屬性名 [ "prop_" + (() => 42)() ]: 42 };模板字符串
模板字符串是一種組合字符串的語法糖,其使用類似于Perl,Python等語言的字符串修改方法類似,它的出現讓我們拼合字符串時方便多了。目前相互中幾乎所有字符串的拼接都用這個了,異常方便。
模板字符串定義在兩個反撇號中;
在模板字符串中可以直接換行,格式會得以保留;
通過${}可以很方便的在模板字符串中添加變量;
// 把字符串放在``(注意不是引號)中就可以使用 `In JavaScript " " is a line-feed.` // 模板字符串保留了換行 `In JavaScript this is not legal.` // 在字符串中添加變量的方法,變量直接放在${}中即可 var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // 拼合請求時異常方便了 POST`http://foo.org/bar?a=${a}&b=$ Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler);Destructuring 解構
解構使用模式匹配的方法綁定變量和值,數組和對象都可使用。解構在綁定失敗的時會實現軟綁定,即沒有匹配值時,返回undefined。使用方法可見示例:
// 數組解構 var [a, , b] = [1,2,3]; // a = 1,b = 3 // React中常見以下用法 var {a, b, c} = this.props; // 對象解構也能用在函數的參數中 function g({name: x}) { console.log(x); } g({name: 5}) // 綁定失敗時返回undefined var [a] = []; a === undefined; // 解構時也可以綁定默認值 var [a = 1] = []; a === 1; // 配合默認參數使用結構 function r({x, y, w = 10, h = 10}) { return x + y + w + h; } r({x:1, y:2}) === 23默認值,剩余值和拓展值
ES6允許我們在給變量添加默認值
使用拓展值使得函數調用時可傳入數組作為連續的參數
利用剩余值特性我們可以把函數尾部的參數轉換為一個數組,現在使用rest就可以替換以前的arguments對象了。
// 給函數的參數添加默認值 function f(x, y=12) { // y is 12 if not passed (or passed as undefined) return x + y; } // 可以只傳參數x的值了 f(3) == 15 // 使用rest function f(x, ...y) { // y is an Array return x * y.length; } f(3, "hello", true) == 6 // 傳入數組作為參數 function f(x, y, z) { return x + y + z; } // 直接傳入數組當作上面函數的參數 f(...[1,2,3]) == 6Let 和 Const
ES6新增了塊作用域,新增了兩種定義變量的方法,定義變量時推薦使用let替代var,let定義的變量在塊作用域內有效,const用以指定固定值,這兩類新定義的變量不允許在定義前使用,也不允許重復定義。
function f() { { let x; { const x = "sneaky"; // 改變const x = "foo"; } // 重復定義會出錯 let x = "inner"; } } // 在這里想到一個使用var時新手特別容易犯的問題 for (var i=0; i<10; ++i) { setTimeout(function(){ console.log(i); }, i*1000); } // 使用var 所有的結果都是10 // 使用let 結果就是預想要的結果 for (let i=0; i<10; ++i) { setTimeout(function(){ console.log(i); }, i*1000); }Iterators + For..Of
ES6為部署了Iterator接口的各種不同的數據結構提供了統一的訪問機制。其本質是一個指針對象。每次調用next方法,可以把指針指向數據結構的下一個成員。具體說來,每一次調用next方法,都會返回數據結構的當前成員的信息(一個包含value和done兩個屬性的對象,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束)。
凡是部署了Symbol.iterator屬性的數據結構,就稱為部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
let fibonacci = { // 一個數據結構只要具有Symbol.iterator屬性,就可被認為是可遍歷的,`Symbol.iterator`是一個表達式,返回Symbol對象的iterator屬性,所以需要放在[]中,本質上它是當前數據結構的遍歷器生成函數。 [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [pre, cur] = [cur, pre + cur]; return { done: false, value: cur } } } } } // fibonacci部署了Symbol.iterator屬性,只要done不為true就會一直遍歷 for (var n of fibonacci) { // 調用1000以內的值做遍歷 if (n > 1000) break; console.log(n); }
原生具備Iterator接口的數據結構有以下幾種:數組、某些類似數組的對象(字符串、DOM NodeList 對象、arguments對象)、Set和Map結構。
對象(Object)之所以沒有默認部署Iterator接口,是因為對象的哪個屬性先遍歷,哪個屬性后遍歷是不確定的,需要開發者手動在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的對象具有該方法也可)。
Generators實際使用時需引入polyfill
可以從兩個角度理解Generators,它既是狀態機也是一個遍歷器對象生成函數。執行該函數可以理解為啟動了遍歷器,之后每次執行next()函數則每次執行到yield處。
值得注意的是執行next()時可添加參數,這實現了在函數運行的不同階段,可以從外部向內部注入不同的值,
生成器使用function*和yield簡化了迭代過程,使用function*定義的函數返回了一個生成器實例。
生成器是迭代器的子類,但是包含next和throw。這使得值可以回流到生成器,yield是一個可以返回值的表達式。
for...of循環可以自動遍歷 Generator 函數時生成的Iterator對象,此時不再需要調用next方法。
Generator的return方法會返回固定的值,終結遍歷Generator函數。返回值的value屬性就是return方法的參數,返回值的done屬性為true。
結合co模塊可以實現比Promise更加優雅的異步調用方式
// 使用generator函數實現上述遍歷器對象 var fibonacci = { [Symbol.iterator]: function*() { var pre = 0, cur = 1; for (;;) { var temp = pre; pre = cur; cur += temp; yield cur; } } } for (var n of fibonacci) { // truncate the sequence at 1000 if (n > 1000) break; console.log(n); } // 使用co模塊(基于 Promise 對象的自動執行器),可以實現異步函數的自動執行 var gen = function* () { var f1 = yield somethingAsync(); var f2 = yield anotherThingAsync(); }; var co = require("co"); co(gen);
Unicode實際使用時需引入polyfill
ES6完整支持所有的Unicode,包括新的Unicode字面量和u模式正則,提供了新的API來處理21bit級別的字符串。這些新加特性使得我們的JavaScript應用有能力支持各種語言。
// same as ES5.1 "?".length == 2 // 新的正則匹配模式 "?".match(/./u)[0].length == 2 // 新形式 "u{20BB7}"=="?"=="uD842uDFB7" // codePointAt()能夠正確處理4個字節儲存的字符,返回一個字符的碼點 "?".codePointAt(0) == 0x20BB7 // for-of 遍歷字符,以整體輸出 for(var c of "?") { console.log(c); } // ?
我們也可以在JS中寫出Emoji了,很有趣,對不對:
Modules現代JS應用的開發離不開模塊了,ES6對模塊的定義提供了語言層面的支持。規范化了各種JavaScript模塊加載器,支持運行時動態加載模塊,支持異步加載模塊。
ES6 模塊的設計思想,是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量,效率要比 CommonJS 模塊的加載方式高。
// lib/math.js 模塊的定義 export function sum(x, y) { return x + y; } export var pi = 3.141593; // app.js 模塊的全部引用 import * as math from "lib/math"; alert("2π = " + math.sum(math.pi, math.pi)); // otherApp.js 模塊的部分引用 import {sum, pi} from "lib/math"; alert("2π = " + sum(pi, pi)); // 模塊導出方法 // lib/mathplusplus.js export * from "lib/math"; export var e = 2.71828182846; export default function(x) { return Math.log(x); } // 混合引入方法 import ln, {pi, e} from "lib/mathplusplus"; alert("2π = " + ln(e)*pi*2);Module Loaders(其實并非ES6標準的一部分,只是草案)
模塊加載器支持以下功能:
動態加載
狀態隔離
全局命名空間隔離
編寫鉤子
嵌套
默認的模塊加載器可以被配置,新的加載器可以被配置來評估加載獨立上下文中的內容。
// 動態加載 – ‘System’ 是默認的加載器 System.import("lib/math").then(function(m) { alert("2π = " + m.sum(m.pi, m.pi)); }); // 新的加載器創建了執行沙盒 var loader = new Loader({ global: fixup(window) // replace ‘console.log’ }); loader.eval("console.log("hello world!");"); // 可以直接修改模塊的緩存 System.get("jquery"); System.set("jquery", Module({$: $})); // WARNING: not yet finalizedMap Set WeakMap WeakSet
ES6為算法提供了新的高效的數據結構,WeakMaps提供了防泄漏的鍵值對表。
// Set類似于數組,但是成員的值都是唯一的,沒有重復的值。 var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true; // Map 類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。 var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34; // WeakMap結構與Map結構類似,也是用于生成鍵值對的集合,但是WeakMap只接受對象作為鍵名(null除外),不接受其他類型的值作為鍵名,此外WeakMap的鍵名所指向的對象,不計入垃圾回收機制。 var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined // WeakSet 結構與 Set 類似,也是不重復的值的集合,但是WeakSet 的成員只能是對象,而不能是其他類型的值,此外WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用 var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set
Proxies實際使用時需引入polyfill
Proxy 用于修改某些操作的默認行為,等同于在語言層面做出修改,所以屬于一種“元編程”(meta programming),即對編程語言進行編程。
可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
需要注意的是目前未被Babel支持,使用時需謹慎
// target參數表示所要攔截的目標對象; var target = {}; // handler參數也是一個對象,用來定制攔截行為; var handler = { get: function (receiver, name) { return `Hello, ${name}!`; } }; // 生成一個Proxy實例 var p = new Proxy(target, handler); p.world === "Hello, world!"; // 對函數同樣可以使用代理 var target = function () { return "I am the target"; }; var handler = { apply: function (receiver, ...args) { return "I am the proxy"; } }; var p = new Proxy(target, handler); p() === "I am the proxy";
// Proxy支持的攔截操作如下
var handler = { get:..., set:..., has:..., deleteProperty:..., apply:..., construct:..., getOwnPropertyDescriptor:..., defineProperty:..., getPrototypeOf:..., setPrototypeOf:..., enumerate:..., ownKeys:..., preventExtensions:..., isExtensible:... }
SymbolsBabel 不支持,使用時應注意
Symbol保證每個屬性的名字都是獨一無二的,這樣就從根本上防止了屬性名的沖突;
它是一種類似于字符串的數據類型,Symbol函數可以接受一個字符串作為參數,表示對 Symbol 實例的描述;
Symbols是唯一的,單并非私有的,通過Object.getOwnPropertySymbols可以獲取對應的值;
Symbol 值作為對象屬性名時,不能用點運算符。
var MyClass = (function() { // module scoped symbol var key = Symbol("key"); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function() { ... this[key] ... } }; return MyClass; })(); var c = new MyClass("hello") c["key"] === undefined
內置類的繼承由于語言限制,Babel只提供部分支持,使用時需要注意
在ES6中,內置的Array,Date,DOM Element可以被繼承以拓展了。
// User code of Array subclass class MyArray extends Array { constructor(...args) { super(...args); } } var arr = new MyArray(); arr[1] = 12; arr.length == 2
Math + Number + String + Array + Object APIsbabel 部分支持,由于ES5引擎的限制Date,Array,Error不被支持,但是HTMLElement是被支持的
ES6 為很多舊有對象添加了新的API,這些對象包括Math,Array器,String,Object,如下:
Number.EPSILON Number.isInteger(Infinity) // false Number.isNaN("NaN") // false Math.acosh(3) // 1.762747174039086 Math.hypot(3, 4) // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2 "abcde".includes("cd") // true "abc".repeat(3) // "abcabcabc" Array.from(document.querySelectorAll("*")) // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior [0, 0, 0].fill(7, 1) // [0,7,7] [1, 2, 3].find(x => x == 3) // 3 [1, 2, 3].findIndex(x => x == 2) // 1 [1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2] ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"] ["a", "b", "c"].keys() // iterator 0, 1, 2 ["a", "b", "c"].values() // iterator "a", "b", "c" Object.assign(Point, { origin: new Point(0,0) })
二進制和八進制字面量babel 通過 polyfill 提供部分支持
ES6添加了二進制和八進制數值的字面量定義方法:
0b111110111 === 503 // true 0o767 === 503 // true
Promisebabel 只支持字面量形式,不支持 Number("0o767")形式
Promise為異步編程提供了一種新的方式,Promise把未來將用到的值當做一等對象,Promise在很多前端庫中已經有所支持了。這個平時用得最多了,還沒使用的推薦試試。
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); }) } var p = timeout(1000).then(() => { return timeout(2000); }).then(() => { throw new Error("hmm"); }).catch(err => { return Promise.all([timeout(100), timeout(200)]); })
Reflect API實際使用時需引入polyfill
Reflect對象與Proxy對象一樣,也是 ES6 為了操作對象而提供的新 API,作用如下:
將Object對象的一些明顯屬于語言內部的方法(比如Object.defineProperty),放到Reflect對象上;
修改某些Object方法的返回結果,讓其變得更合理;
讓Object操作都變成函數行為,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數行為。
Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法;
var O = {a: 1}; Object.defineProperty(O, "b", {value: 2}); O[Symbol("c")] = 3; Reflect.ownKeys(O); // ["a", "b", Symbol(c)] function C(a, b){ this.c = a + b; } var instance = Reflect.construct(C, [20, 22]); instance.c; // 42
Tail Calls實際使用時需引入polyfill
尾部調用被保證不能無限拓展棧,這讓有無限制輸入時的遞歸算法更加安全。
function factorial(n, acc = 1) { "use strict"; if (n <= 1) return acc; return factorial(n - 1, n * acc); } // 堆棧越來越常用,在ES6中其使用更加安全了 factorial(100000)說明
上文對ES6的新特性都做了簡單的描述,但是關于Reflect API和Proxies,由于本人對他們的理解還不夠透徹,說得可能有些不清不楚。希望閱讀本文讓你有收獲,有任何疑問,大家也可以一起討論。
有用的鏈接ECMAScript 6 Features
Learn ES2015
ECMAScript 6 入門
You don"t know js
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83598.html
摘要:構造函數通常首字母大寫,用于區分普通函數。這種關系常被稱為原型鏈,它解釋了為何一個對象會擁有定義在其他對象中的屬性和方法。中所有的對象,都有一個屬性,指向實例對象的構造函數原型由于是個非標準屬性,因此只有和兩個瀏覽器支持,標準方法是。 從這篇文章開始,復習 MDN 中級教程 的內容了,在初級教程中,我和大家分享了一些比較簡單基礎的知識點,并放在我的 【Cute-JavaScript】系...
摘要:字符串拓展在我們判斷字符串是否包含另一個字符串時,之前,我們只有方法,之后我們又多了三種方法返回布爾值,表示是否找到參數字符串。返回布爾值,表示參數字符串是否在原字符串的頭部。 本文是 重溫基礎 系列文章的第八篇。今日感受:人在異鄉,也不能忘記湯圓。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】1.語法和數據類型 【重溫基礎】2.流程控制和...
摘要:迭代器和生成器將迭代的概念直接帶入核心語言,并提供一種機制來自定義循環的行為。本文主要會介紹中新增的迭代器和生成器。屬性本身是函數,是當前數據結構默認的迭代器生成函數。 本文是 重溫基礎 系列文章的第十三篇。今日感受:每次自我年終總結,都會有各種情緒和收獲。 系列目錄: 【復習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎】1.語法和數據類型 【重溫基礎】2.流...
摘要:前端日報精選大前端公共知識梳理這些知識你都掌握了嗎以及在項目中的實踐深入貫徹閉包思想,全面理解閉包形成過程重溫核心概念和基本用法前端學習筆記自定義元素教程阮一峰的網絡日志中文譯回調是什么鬼掘金譯年,一個開發者的好習慣知乎專 2017-06-23 前端日報 精選 大前端公共知識梳理:這些知識你都掌握了嗎?Immutable.js 以及在 react+redux 項目中的實踐深入貫徹閉包思...
閱讀 3101·2021-10-13 09:40
閱讀 3954·2021-09-22 15:51
閱讀 1501·2021-09-22 15:48
閱讀 1068·2021-09-06 15:00
閱讀 1793·2019-08-30 15:43
閱讀 2363·2019-08-29 18:35
閱讀 1674·2019-08-29 16:18
閱讀 3618·2019-08-29 12:49