本篇記錄部分es6相關內容和我的理解。javascript
======================相關文章和開源庫=======================前端
系列文章vue
1.前端知識梳理-HTML,CSS篇
java
2.前端知識梳理-ES5篇
node
3.前端知識梳理-ES6篇
es6
4.前端知識梳理-VUE篇
sql
我的維護的開源組件庫編程
1.bin-ui,一個基於vue的pc端組件庫json
2.樹形組織結構組件
數組
3.bin-admin ,基於bin-ui的後臺管理系統集成方案
4.bin-data ,基於bin-ui和echarts的數據可視化框架
5.其他生態連接
========================================================
var 會變量提高;let 聲明的變量只在它所在的代碼塊有效;
它們是繼 var 以後, 新的變量定義方法。與 let 相比,const 更容易被理解,用於定義常量,即不可變量
沒有塊級做用域。這個問題之因此爲人所熟知,是由於它引起了諸如歷遍監聽事件須要使用閉包解決等問題。
<button>一</button>
<button>二</button>
<button>三</button>
<button>四</button>
<div id="output"></div>
<script>
var buttons = document.querySelectorAll('button')
var output = document.querySelector('#output')
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
output.innerText = buttons[i].innerText
})
}
</script>
複製代碼
從直觀的角度看這段代碼並無語義上的錯誤,可是當咱們點擊任意一個按鈕時,就會報出這樣的錯誤信息:
出現這個錯誤的緣由是由於 buttons[i] 不存在,即爲 undefined。
爲何會出現按鈕不存在結果呢?經過排查,咱們能夠發現,每次咱們點擊按鈕時,事件監聽回調函數中獲得的變量 i 都會等於 buttons.length, 也就是這裏的 4。而 buttons[4] 偏偏不存在,因此致使了錯誤的發生。
再而致使 i 獲得的值都是 buttons.length 的緣由就是由於 JavaScript 中沒有塊級做用域,而使對 i 的變量引用(Reference)一直保持在上一層做用域(循環語句所在層)上, 而當循環結束時 i 則正好是buttons.length。
而在 ES6 中,咱們只需作出一個小小的改動,即可以解決該問題(假設所使用的瀏覽器已經支持所須要的特性):
for (/* var */ let i = 0; i < buttons.length; i++) {
// ...
}
複製代碼
經過把 for 語句中對計數器 i 的定義語句從 var 換成 let,便可。由於 let 語句會使該變量處於一個塊級做用域中, 從而讓事件監聽回調函數中的變量引用獲得保持。咱們不妨看看改進後的代碼通過 babel 的編譯會變成什麼樣子:
// ...
var _loop = function (i) {
buttons[i].addEventListener('click', function () {
output.innerText = buttons[i].innerText
})
}
for (var i = 0; i < buttons.length; i++) {
_loop(i)
}
// ...
複製代碼
繼 let 和 const 以後,箭頭函數就是使用率最高的新特性了。
箭頭函數,顧名思義即是使用箭頭(=>)進行定義的函數,屬於匿名函數(Lambda)一類。
箭頭函數有好幾種使用語法:
// 1.means return `foo + ' world'`
foo => foo + ' world'
// 2.
(foo, bar) => foo + bar
// 3.
foo => {
return foo + ' world'
}
// 4.
(foo, bar) => {
return foo + bar
}
複製代碼
箭頭函數與上下文綁定
事實上,箭頭函數在 ES2015 標準中,並不僅是做爲一種新的語法出現。用於對函數內部的上下文 (this)綁定爲定義函數所在的做用域的上下文。
let obj = {
hello: 'world',
foo() {
let bar = () => {
return this.hello
}
return bar
}
}
window.hello = 'ES6'
window.bar = obj.foo()
window.bar() //=> 'world'
複製代碼
上面代碼中的 obj.foo 等價於:
// ...
foo(){
let bar = (function() {
return this.hello
}).bind(this)
return bar
}
// ...
複製代碼
它的出現可讓很是多的字符串使用變得尤其輕鬆。
模板字符串要求使用 ` 代替本來的單/雙引號來包裹字符串內容。它有兩大特色:
支持變量注入
模板字符串之因此稱之爲「模板」,就是由於它容許咱們在字符串中引用外部變量,而不須要像以往須要不斷地相加、相加、相加……
let name = 'Will Wen Gunn'
let title = 'Founder'
let company = 'LikMoon Creation'
let greet = `Hi, I'm ${name}, I am the ${title} at ${company}` 複製代碼
支持換行
在 Node.js 中,若是咱們沒有支持換行的模板字符串,若須要拼接一條SQL,則頗有多是這樣的:
var sql =
"SELECT * FROM Users " +
"WHERE FirstName='Mike' " +
"LIMIT 5;"
複製代碼
或者是這樣的:
var sql = [
"SELECT * FROM Users",
"WHERE FirstName='Mike'",
"LIMIT 5;"
].join(' ')
複製代碼
不管是上面的哪種,都會讓咱們感到很不爽。但若使用模板字符串,彷彿打開了新世界的大門~
let sql = ` SELECT * FROM Users WHERE FirstName='Mike' LIMIT 5; `
複製代碼
方法屬性省略 function
這個新特性能夠算是比較有用但並非很顯眼的一個。
let obj = {
// before
foo: function() {
return 'foo'
},
// after
bar() {
return 'bar'
}
}
複製代碼
來了來了來了,至關有用的一個特性。
// Matching with object
function search(query) {
// ...
// let users = [ ... ]
// let posts = [ ... ]
// ...
return {
users: users,
posts: posts
}
}
let { users, posts } = search('iwillwen')
// Matching with array
let [ x, y ] = [ 1, 2 ]
// missing one
[ x, ,y ] = [ 1, 2, 3 ]
function g({name: x}) {
console.log(x)
}
g({name: 5})
複製代碼
這個特性有很是高的使用頻率,一個簡單的語法糖解決了從前須要一兩行代碼才能實現的功能。
默認參數值
這個特性在類庫開發中至關有用,好比實現一些可選參數:
import fs from 'fs'
import readline from 'readline'
import path from 'path'
function readLineInFile(filename, callback = noop, complete = noop) {
let rl = readline.createInterface({
input: fs.createReadStream(path.resolve(__dirname, filename))
})
rl.on('line', line => {
//... do something with the current line
callback(line)
})
rl.on('close', complete)
return rl
}
function noop() { return false }
readLineInFile('big_file.txt', line => {
// ...
})
複製代碼
後續參數
咱們知道,函數的 call 和 apply 在使用上的最大差別即是一個在首參數後傳入各個參數,一個是在首參數後傳入一個包含全部參數的數組。 若是咱們在實現某些函數或方法時,也但願實現像 call 同樣的使用方法,在 ES2015 以前,咱們可能須要這樣作:
function fetchSomethings() {
var args = [].slice.apply(arguments)
// ...
}
function doSomeOthers(name) {
var args = [].slice.apply(arguments, 1)
// ...
}
複製代碼
而在 ES2015 中,咱們能夠很簡單的使用 … 語法糖來實現:
function fetchSomethings(...args) {
// ...
}
function doSomeOthers(name, ...args) {
// ...
}
複製代碼
要注意的是,...args
後不可再添加
雖然從語言角度看,arguments
和 ...args
是能夠同時使用 ,但有一個特殊狀況則不可:arguments
在箭頭函數中, 會跟隨上下文綁定到上層,因此在不肯定上下文綁定結果的狀況下,儘量不要再箭頭函數中再使用 arguments
,而使用 ...args
。
雖然 ECMA 委員會和各種編譯器都無強制性要求用 ...args
代替 arguments
,但從實踐經驗看來,...args
確實能夠在絕大部份場景下能夠代替 arguments 使用, 除非你有很特殊的場景須要使用到 arguments.callee
和 arguments.caller
。因此我推薦都使用 ...args
而非 arguments
。
解構傳參
在 ES2015 中,... 語法還有另一個功能:無上下文綁定的 apply。什麼意思?看看代碼你就知道了。
function sum(...args) {
return args.map(Number)
.reduce((a, b) => a + b)
}
console.log(sum(...[1, 2, 3])) //=> 6
複製代碼
ES2015 以前,JavaScript 中有哪些基本的數據結構。
其中又分爲值類型和引用類型,Array 實際上是 Object 的一種子類。
Set 和 WeakSet
在 ES2015 中,ECMA 委員會爲 ECMAScript 增添了集(Set)和「弱」集(WeakSet)。它們都具備元素惟一性,若添加了已存在的元素,會被自動忽略。
let s = new Set()
s.add('hello').add('world').add('hello')
console.log(s.size) //=> 2
console.log(s.has('hello')) //=> true
複製代碼
在實際開發中,咱們有不少須要用到集的場景,如搜索、索引創建等。
WeakSet 在 JavaScript 底層做出調整(在非降級兼容的狀況下),檢查元素的變量引用狀況。若是元素的引用已被所有解除,則該元素就會被刪除, 以節省內存空間。這意味著沒法直接加入數字或者字符串。另外 WeakSet 對元素有嚴格要求,必須是 Object,固然了,你也能夠用 new String('...') 等形式處理元素。
let weaks = new WeakSet()
weaks.add("hello") //=> Error
weaks.add(3.1415) //=> Error
let foo = new String("bar")
let pi = new Number(3.1415)
weaks.add(foo)
weaks.add(pi)
weaks.has(foo) //=> true
foo = null
weaks.has(foo) //=> false
複製代碼
Map 和 WeakMap
從數據結構的角度來講,映射(Map)跟本來的 Object 很是類似,都是 Key/Value 的鍵值對結構。 可是 Object 有一個讓人很是不爽的限制:key 必須是字符串或數字。在通常狀況下,咱們並不會趕上這一限制,但若咱們須要創建一個對象映射表時,這一限制顯得尤其棘手。
而 Map 則解決了這一問題,可使用任何對象做爲其 key,這能夠實現從前不能實現或難以實現的功能,如在項目邏輯層實現數據索引等。
map.set(object, 'hello')
map.set('hello', 'world')
map.has(object) //=> true
map.get(object) //=> hello
複製代碼
而 WeakMap 和 WeakSet 很相似,只不過 WeakMap 的鍵和值都會檢查變量引用,只要其一的引用全被解除,該鍵值對就會被刪除。
let weakm = new WeakMap()
let keyObject = { id: 1 }
let valObject = { score: 100 }
weakm.set(keyObject, valObject)
weakm.get(keyObject) //=> { score: 100 }
keyObject = null
weakm.has(keyObject) //=> false
複製代碼
類,做爲自 JavaScript 誕生以來最大的痛點之一,終於在 ES2015 中獲得了官方的妥協,「實現」了 ECMAScript 中的標準類機制。 爲何是帶有雙引號的呢?由於咱們不難發現這樣一個現象:
// $ node
> class Foo {}
// [Function: Foo]
複製代碼
回想一下在 ES2015 之前的時代中,咱們是怎麼在 JavaScript 中實現類的?
function Foo() {}
var foo = new Foo()
複製代碼
是的,ES6 中的類只是一種語法糖,用於定義原型(Prototype)的。
語法
與大多數人所期待的同樣,ES2015 所帶來的類語法確實與不少 C 語言家族的語法類似。
class Person {
constructor(name, gender, age) {
this.name = name
this.gender = gender
this.age = age
}
isAdult() {
return this.age >= 18
}
}
let me = new Person('iwillwen', 'man', 19)
console.log(me.isAdult()) //=> true
複製代碼
與 JavaScript 中的對象字面量不同的是,類的屬性後不能加逗號,而對象字面量則必需要加逗號。
然而,讓人很不爽的是,ES2015 中對類的定義依然不支持默認屬性的語法:
// 理想型 可是沒實現
class Person {
name: String
gender = 'man'
// .
}複製代碼
繼承
ES2015 的類繼承總算是爲 JavaScript 類繼承之爭拋下了一根定海神針了。
class Animal {
yell() {
console.log('yell')
}
}
class Person extends Animal {
constructor(name, gender, age) {
super() // must call `super` before using `this` if this class has a superclass
this.name = name
this.gender = gender
this.age = age
}
isAdult() {
return this.age >= 18
}
}
class Man extends Person {
constructor(name, age) {
super(name, 'man', age)
}
}
let me = new Man('iwillwen', 19)
console.log(me.isAdult()) //=> true
me.yell()
複製代碼
一樣的,繼承的語法跟許多語言中的很相似,ES2015 中若要是一個類繼承於另一個類而做爲其子類,只須要在子類的名字後面加上 extends {SuperClass}
便可。
ES2015 中的類機制支持 static 類型的方法定義,好比說 Man 是一個類,而我但願爲其定義一個 Man.isMan() 方法以用於類型檢查,咱們能夠這樣作:
class Man {
// ...
static isMan(obj) {
return obj instanceof Man
}
}
let me = new Man()
console.log(Man.isMan(me)) //=> true
複製代碼
就目前來講,ES2015 的類機制依然很雞肋:
1.不支持私有屬性(private)
2.不支持前置屬性定義,但可用 get 語句和 set 語句實現
3.不支持多重繼承
4.沒有相似於協議(Protocl)或接口(Interface)等的概念
在 JavaScript 的發展歷史上,曾出現過多種模塊加載庫,如 RequireJS、SeaJS、FIS 等,而由它們衍生出來的 JavaScript 模塊化標準有 CommonJS、AMD、CMD 和 UMD 等。
其中最爲典型的是 Node.js 所遵循的 CommonJS 和 RequireJS 的 AMD。
基本用法
import name from "module-name"
import * as name from "module-name"
import { member } from "module-name"
import { member as alias } from "module-name"
import { member1 , member2 } from "module-name"
import { member1 , member2 as alias2 , [...] } from "module-name"
import defaultMember, { member [ , [...] ] } from "module-name"
import defaultMember, * as alias from "module-name"
複製代碼
如上所示,ES2015 中有不少種模塊引入方式,咱們能夠根據實際須要選擇一種使用。
全局引入
全局引入是最基本的引入方式,這跟 CommonJS、AMD 等模塊化標準並沒有兩樣,都是把目標模塊的全部暴露的接口引入到一個命名空間中。
import name from 'module-name'
import * as name from 'module-name'
複製代碼
這跟 Node.js 所用的 CommonJS 相似: var name = require('module-name')
局部引入
與 CommonJS 等標準不一樣的是,ES2015 的模塊引入機制支持引入模塊的部份暴露接口,這在大型的組件開發中顯得尤其方便,如 React 的組件引入即是使用了該特性。
import { A, B, C } from 'module-name'
A()
B()
C()複製代碼
接口暴露
ES2015 的接口暴露方式比 CommonJS 等標準都要豐富和健壯,可見 ECMA 委員會對這一部份的重視程度之高。
ES2015 的接口暴露有幾種用法:
暴露單獨接口
// module.js
export function method() { /* ... */ }複製代碼
基本的 export 語句能夠調用屢次,單獨使用可暴露一個對象到該模塊外。
暴露覆蓋模塊
若須要實現像 CommonJS 中的 module.exports = {} 以覆蓋整個模塊的暴露對象,則須要在 export 語句後加上 default。
// module.js
export default {
method1,
method2
}
// main.js
import M from './module'
M.method1()複製代碼
Promise的含義
Promise是異步編程的一種解決方案,比傳統的解決方案(回調函數和事件)更合理更強大。
所謂Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件 (一般是一個異步操做)的結果。從語法上說,Promise是一個對象,從它能夠獲取異步操做的消息。
Promise對象有如下2個特色:
1.對象的狀態不受外界影響。Promise
對象表明一個異步操做,有三種狀態:Pending
(進行中)、 Resolved
(已完成)和Rejected
(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態, 任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」, 表示其餘手段沒法改變。
2.一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise
對象的狀態改變,只有兩種可能: 從Pending
變爲Resolved
;從Pending
變爲Rejected
。只要這兩種狀況發生,狀態就凝固了,不會再變了, 會一直保持這個結果。就算改變已經發生了,你再對Promise
對象田靜回調函數,也會當即獲得這個結果。 這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就能夠把異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外, Promise對象提供了統一的接口,使得控制異步操做更加容易。
基本用法
ES6規定,Promise對象是一個構造函數,用來生成Promise實例
var promise = new Promise(function(resolve,reject){
// ... some code
if(/* 異步操做成功 */){
resolve(value);
}else{
reject(error);
}
});
複製代碼
1 Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數,由JavaScript引擎提供,不是本身部署。
2 resolve
函數的做用,將Promise對象的狀態從「未完成」變成「成功」(即從Pending變爲Resolved),在異步操 做成功時調用,並將異步操做的結果,做爲參數傳遞出去;
3 reject
函數的做用是,在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。
Promise實例生成之後,能夠用then方法分別制定Resolved狀態和Rejected狀態的回調函數:
promise.then(function(value){
// sucess
},function(error){
// failure
});
複製代碼
then方法能夠接受2個回調函數做爲參數,第二個函數是可選的,不必定要提供。這兩個函數都接受Promise對象傳出的值做爲參數。
下面是一個Promise對象的簡單例子:
function timeout(ms){
return new Promise((resolve,reject)=>{
setTimeout(resolve,ms,'done');
});
}
timeout(100).then((value)=>{
console.log(value);
});
複製代碼
上面代碼中,timeout方法返回一個Promise實例,表示一段事件之後纔會發生的結果。過了指定的時間(ms參數)之後,Promise實例的狀態變爲Resolved, 就會觸發then方法綁定的回調函數。
Promise新建後就會當即執行
let promise = new Promise(function(resolve,rejeact){
console.log('Promise');
resolve();
});
promise.then(function(){
console.log('Resolved');
});
console.log('Hi');
// Promise
// Hi
// Resolved
複製代碼
上面代碼中,Promise新建後當即執行,因此首先輸出的是」Promise」,而後then方法指定的回調函數,將在當前腳本全部同步任務執行完纔會執行,因此」Resolved」最後輸出。
下面是異步加載圖片的例子:
function loadImageAsync(url){
return new Promise(function(resolve,reject){
var image = new Image();
image.onload = function(){
resolve(image);
};
image.onerror = function(){
reject(new Error('Could not load image at' + url));
};
image.src = url;
});
}複製代碼
下面是一個用Promise對象實現Ajax操做的例子:
var getJSON = function(url){
var promise = new Promise(function(resolve,reject){
var client = new XMLHttpRequest();
client.open('GET',url);
client.onreadystatechange = handler;
client.responseType = 'json';
client.setRequestHeader('Accept','application/json');
client.send();
function handler(){
if(this.readyState !== 4){
return;
}
if(this.status === 200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
}
});
return promise;
};
getJSON('/posts.jons').then(function(json){
consoloe.log(json);
},function(error){
console.log('出錯了');
});
複製代碼