1). 做用域是塊級做用域(在ES6以前,js只存在函數做用域以及全局做用域)javascript
if(1){ let a=1; console.log(a) }
2). 不存在變量聲明提早;css
console.log(b); //ReferenceError: b is not defined let b=2;
3). 不能重複定義html
let a=1; let a=2; console.log(a);//Identifier 'a' has already been declared
4). 存在暫時性死區:能夠這樣來理解前端
var a=1; if(1){ console.log(a); let a=2; }
① 在一個塊級做用域中,變量惟一存在,一旦在塊級做用域中用let聲明瞭一個變量,那麼這個變量就惟一屬於這個塊級做用域,不受外部變量的影響;vue
② 不管在塊中的任何地方聲明瞭一個變量,那麼在這個塊級做用域中,任何使用這個名字的變量都是指這個變量,不管外部是否有其餘同名的全局變量;java
③ 暫時性死區的本質就是,只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。node
④ 暫時性死區的意義:讓咱們標準化代碼。將全部的變量的聲明放在做用域的最開始。webpack
2. constes6
const通常用來聲明常量,且聲明的常量是不容許改變的,只讀屬性,所以就要在聲明的同時賦值。const與let同樣,都是塊級做用域,存在暫時性死區,不存在變量聲明提早,不容許重複定義web
const A=1;//從新給常量A賦值會報錯 A=3;// Uncaught TypeError: Assignment to constant variable. //錯誤:賦值給常量
//解構賦值,兩邊格式要一致 let [a,b,c] = [1,2,3];let [a,[b,c]] = [1,[2,3]]; //交互數據 let a = 10; let b = 20; [a,b] = [b,a];
用class關鍵字定義對象類型,用extends關鍵字實現繼承
const private2 = Symbol('I am symbol value') class A { a1 = '1' // ES7 實例屬性,須要new實例來訪問, ES6規定class沒有靜態屬性,只有靜態方法因此只能在constructor中定義屬性 static a2 = '2' // ES7的靜態屬性,直接 A.a2 訪問,不須要new實例 getA1() { return this.a1 // this指向new實例 } static getA2() { return ‘2’ // 靜態方法 } constructor(name) { //必定要有構造方法,若是沒有默認生成空構造方法 this.a3 = '3' // 這裏定義實例屬性 this.name = name } // 私有方法寫法 publicMethod() { private1() // 私有方法1,能夠寫在class體外 private2() // 利用Symbol值來定義 } [private2]() { // 這裏是私有方法 } } const private1 = function() { // 這裏也是私有方法,但別export出去} // 最後export class export default A // 經過extends繼承 class B extends A{ constructor() { // 必定要在構造函數的第一句調用super super() // 這是調用父類的構造方法 this.b1 = '11' this.b2 = super.a1 // super直接調用時指向父類構造方法,範圍屬性時,指向父類實例,或調用父類靜態方法 } }
Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javascript異步編程解決方案之一。
const request = url => { return new Promise((resolve, reject) => { $.get(url, data => { resolve(data) }); }) }; // 請求data1 request(url).then(data1 => { return request(data1.url); }).then(data2 => { return request(data2.url); }).then(data3 => { console.log(data3); }).catch(err => throw new Error(err));
const promise = new Promise((resolve, reject) => { // 異步處理 // 處理結束後、調用resolve 或 reject });
promise的三種狀態
①promise 對象初始化狀態爲 pending
②當調用resolve(成功),會由pending => fulfilled
③當調用reject(失敗),會由pending => rejected
注意:promsie狀態 只能由 pending => fulfilled/rejected, 一旦修改就不能再變
1)then方法註冊 當resolve(成功)/reject(失敗)的回調函數,then方法是異步執行的
// onFulfilled 是用來接收promise成功的值 // onRejected 是用來接收promise失敗的緣由 promise.then(onFulfilled, onRejected);
2)resolve(成功) onFulfilled會被調用
const promise = new Promise((resolve, reject) => { resolve('fulfilled'); // 狀態由 pending => fulfilled }); promise.then(result => { // onFulfilled console.log(result); // 'fulfilled' }, reason => { // onRejected 不會被調用 })
3)reject(失敗) onRejected會被調用
const promise = new Promise((resolve, reject) => { reject('rejected'); // 狀態由 pending => rejected }); promise.then(result => { // onFulfilled 不會被調用 }, reason => { // onRejected console.log(rejected); // 'rejected' })
4)promise.catch
在鏈式寫法中能夠捕獲前面then中發送的異常
promise.catch(onRejected) 至關於 promise.then(null, onRrejected); // 注意 // onRejected 不能捕獲當前onFulfilled中的異常 promise.then(onFulfilled, onRrejected); // 能夠寫成: promise.then(onFulfilled) .catch(onRrejected);
promise.then方法每次調用 都返回一個新的promise對象 因此能夠鏈式寫法
function taskA() { console.log("Task A"); } function taskB() { console.log("Task B"); } function onRejected(error) { console.log("Catch Error: A or B", error); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) // 捕獲前面then方法中的異常
1)Promise.resolve 返回一個fulfilled狀態的promise對象
Promise.resolve('hello').then(function(value){ console.log(value); }); Promise.resolve('hello'); // 至關於 const promise = new Promise(resolve => { resolve('hello'); });
2)Promise.reject 返回一個rejected狀態的promise對象
Promise.reject(24); new Promise((resolve, reject) => { reject(24); });
3)Promise.all 接收一個promise對象數組爲參數
只有所有爲resolve纔會調用 一般會用來處理 多個並行異步操做
const p1 = new Promise((resolve, reject) => { resolve(1); }); const p2 = new Promise((resolve, reject) => { resolve(2); }); const p3 = new Promise((resolve, reject) => { reject(3); }); Promise.all([p1, p2, p3]).then(data => { console.log(data); // [1, 2, 3] 結果順序和promise實例數組順序是一致的 }, err => { console.log(err); });
4)Promise.race 接收一個promise對象數組爲參數
Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行後面的處理
function timerPromisefy(delay) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); Promise.race([ timerPromisefy(10), timerPromisefy(20), timerPromisefy(30) ]).then(function (values) { console.log(values); // 10 });
/** * 摘自https://www.cnblogs.com/minigrasshopper/p/9141307.html * Promise類實現原理 * 構造函數傳入一個function,有兩個參數,resolve:成功回調; reject:失敗回調 * state: 狀態存儲 [PENDING-進行中 RESOLVED-成功 REJECTED-失敗] * doneList: 成功處理函數列表 * failList: 失敗處理函數列表 * done: 註冊成功處理函數 * fail: 註冊失敗處理函數 * then: 同時註冊成功和失敗處理函數 * always: 一個處理函數註冊到成功和失敗 * resolve: 更新state爲:RESOLVED,而且執行成功處理隊列 * reject: 更新state爲:REJECTED,而且執行失敗處理隊列 **/ class PromiseNew { constructor(fn) { this.state = 'PENDING'; this.doneList = []; this.failList = []; fn(this.resolve.bind(this), this.reject.bind(this)); } // 註冊成功處理函數 done(handle) { if (typeof handle === 'function') { this.doneList.push(handle); } else { throw new Error('缺乏回調函數'); } return this; } // 註冊失敗處理函數 fail(handle) { if (typeof handle === 'function') { this.failList.push(handle); } else { throw new Error('缺乏回調函數'); } return this; } // 同時註冊成功和失敗處理函數 then(success, fail) { this.done(success || function () { }).fail(fail || function () { }); return this; } // 一個處理函數註冊到成功和失敗 always(handle) { this.done(handle || function () { }).fail(handle || function () { }); return this; } // 更新state爲:RESOLVED,而且執行成功處理隊列 resolve() { this.state = 'RESOLVED'; let args = Array.prototype.slice.call(arguments); setTimeout(function () { this.doneList.forEach((item, key, arr) => { item.apply(null, args); arr.shift(); }); }.bind(this), 200); } // 更新state爲:REJECTED,而且執行失敗處理隊列 reject() { this.state = 'REJECTED'; let args = Array.prototype.slice.call(arguments); setTimeout(function () { this.failList.forEach((item, key, arr) => { item.apply(null, args); arr.shift(); }); }.bind(this), 200); } } // 下面一波騷操做 new PromiseNew((resolve, reject) => { resolve('hello world'); // reject('you are err'); }).done((res) => { console.log(res); }).fail((res) => { console.log(res); })
使用Generator
能夠很方便的幫助咱們創建一個處理Promise
的解釋器;
async
/await
這樣的語法,可讓咱們以接近編寫同步代碼的方式來編寫異步代碼(無需使用.then()
或者回調函數)
Generator
是一個函數,能夠在函數內部經過yield
返回一個值(此時,Generator
函數的執行會暫定,直到下次觸發.next()
) 建立一個Generator
函數的方法是在function
關鍵字後添加*
標識。
在調用一個Generator
函數後,並不會當即執行其中的代碼,函數會返回一個Generator
對象,經過調用對象的next
函數,能夠得到yield
/return
的返回值。 不管是觸發了yield
仍是return
,next()
函數總會返回一個帶有value
和done
屬性的對象。 value
爲返回值,done
則是一個Boolean
對象,用來標識Generator
是否還能繼續提供返回值。 P.S. Generator
函數的執行時惰性的,yield
後的代碼只在觸發next
時纔會執行。
function * oddGenerator () { yield 1 yield 3 return 5 } let iterator = oddGenerator() let first = iterator.next() // { value: 1, done: false } let second = iterator.next() // { value: 3, done: false } let third = iterator.next() // { value: 5, done: true }
function getRandom () { return new Promise(resolve => { setTimeout(_ => resolve(Math.random() * 10 | 0), 1000) }) } async function main () { let num1 = await getRandom() let num2 = await getRandom() return num1 + num2 } console.log(`got data: ${await main()}`)
Async函數始終返回一個Promise
async function throwError () { throw new Error() } async function returnNumber () { return 1 } console.log(returnNumber() instanceof Promise) // true console.log(throwError() instanceof Promise) // true
Await是按照順序執行的,並不能並行執行,JavaScript
是單線程的,這就意味着await
一隻能一次處理一個,若是你有多個Promise
須要處理,則就意味着,你要等到前一個Promise
處理完成才能進行下一個的處理,這就意味着,若是咱們同時發送大量的請求,這樣處理就會很是慢。
function delay () { return new Promise(resolve => setTimeout(resolve, 1000)) } let tasks = [1, 2, 3, 4] //要4s才能執行完 async function runner (tasks) { for (let task of tasks) { await delay() } } //優化,縮短執行時間 async function runner (tasks) { tasks = tasks.map(delay) await Promise.all(tasks) } console.time('runner') await runner(tasks) console.timeEnd('runner')
Generator
與async function
都是返回一個特定類型的對象:
Generator
: 一個相似{ value: XXX, done: true }
這樣結構的Object
Async
: 始終返回一個Promise
,使用await
或者.then()
來獲取返回值Generator
是屬於生成器,一種特殊的迭代器,用來解決異步回調問題感受有些遊手好閒了。。 而async
則是爲了更簡潔的使用Promise
而提出的語法,相比Generator + co
這種的實現方式,更爲專一,生來就是爲了處理異步編程。
// 兩個參數: (x, y) => x * x + y * y // 無參數: () => 3.14 // 可變參數: (x, y, ...rest) => { var i, sum = x + y; for (i=0; i<rest.length; i++) { sum += rest[i]; } return sum; }
var obj = { age: 1, say: function() { setTimeout(function() { console.log(this, this.age); // window undefined }, 0); }, } var obj1 = { age: 1, say: function() { setTimeout(() => { console.log(this, this.age); // obj1 1 }, 0); } };
這裏能夠看出箭頭函數中訪問的this其實是其父級做用域中的this,箭頭函數自己的this是不存在的,這樣就至關於箭頭函數的this是在聲明的時候就肯定了(即this
老是指向詞法做用域,也就是外層調用者handler),這個特性是頗有用的,因此,用call()
或者apply()
調用箭頭函數時,沒法對this
進行綁定,即傳入的第一個參數被忽略。
var handler = { id: '111', doSomething: function(e) { console.log(e); }, init: function() { document.addEventListener('click', (event) => { // 這裏綁定事件,函數this就能夠訪問到handler的方法doSomething this.doSomething(event); }, false); } } handler.init();
var Person = (name) => { // Uncaught TypeError: Person is not a constructor this.name = name; } var person = new Person('Jack');
var foo = (val) => { console.log(arguments); // Uncaught ReferenceError: arguments is not defined }; foo(); //這個特性也很好測試,可是實在要使用arguments對象要怎麼辦呢?咱們可使用es6的另外一個新特性rest參數,完美替代 var foo = (...args) => { console.log(args); // [1, 2, 3] }; foo(1, 2, 3);
JavaScript的默認對象表示方式{}
能夠視爲其餘語言中的Map
或Dictionary
的數據結構,即一組鍵值對。可是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其餘數據類型做爲鍵也是很是合理的。爲了解決這個問題,最新的ES6規範引入了新的數據類型Map。
Map的遍歷:
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]); m.get('Michael'); // 95 //初始化Map須要一個二維數組,或者直接初始化一個空Map。Map具備如下方法: var m = new Map(); // 空Map m.set('Adam', 67); // 添加新的key-value m.set('Bob', 59); m.has('Adam'); // 是否存在key 'Adam': true m.get('Adam'); // 67 m.delete('Adam'); // 刪除key 'Adam' m.get('Adam'); // undefined //因爲一個key只能對應一個value,因此,屢次對一個key放入value,後面的值會把前面的值沖掉 //遍歷Map var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); m.forEach(function (value, key, map) { console.log(value); });
Set
和Map
相似,也是一組key
的集合,但不存儲value
。因爲key
不能重複,因此,在Set
中,沒有重複的key。
//要建立一個Set,須要提供一個Array做爲輸入,或者直接建立一個空Set: var s1 = new Set(); // 空Set var s2 = new Set([1, 2, 3]); // 含1, 2, 3 //重複元素在Set中自動被過濾: var s = new Set([1, 2, 3, 3, '3']); s; // Set {1, 2, 3, "3"} //經過add(key)方法能夠添加元素到Set中,能夠重複添加,但不會有效果: s.add(4); s; // Set {1, 2, 3, 4} s.add(4); s; // 仍然是 Set {1, 2, 3, 4} //經過delete(key)方法能夠刪除元素: var s = new Set([1, 2, 3]); s; // Set {1, 2, 3} s.delete(3); s; // Set {1, 2} //遍歷Set var s = new Set(['A', 'B', 'C']); s.forEach(function (element, sameElement, set) { console.log(element); });
數組去重
//1.for循環嵌套,利用splice去重 function newArr(arr){ for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++) if(arr[i]==arr[j]){ //若是第一個等於第二個,splice方法刪除第二個 arr.splice(j,1); j--; } } } return arr; } var arr = [1,1,2,5,6,3,5,5,6,8,9,8]; console.log(newArr(arr)) //2.建新數組,利用indexOf去重 function newArr(array){ //一個新的數組 var arrs = []; //遍歷當前數組 for(var i = 0; i < array.length; i++){ //若是臨時數組裏沒有當前數組的當前值,則把當前值push到新數組裏面 if (arrs.indexOf(array[i]) == -1){ arrs.push(array[i]) }; } return arrs; } var arr = [1,1,2,5,5,6,8,9,8]; console.log(newArr(arr)) //3.ES6中利用Set去重 function newArr(arr){ return Array.from(new Set(arr)) } var arr = [1,1,2,9,6,9,6,3,1,4,5]; console.log(newArr(arr))
如今的Chrome瀏覽器已經支持ES6了,可是有些低版本的瀏覽器仍是不支持ES6的語法,這就須要咱們把ES6的語法自動的轉變成ES5的語法。Webpack是有自動編譯轉換能力的,除了Webpack自動編譯,還可使用用Babel來完成。
{ "name": "es6", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
能夠根據本身的須要進行修改,好比咱們修改name的值爲es6。
{ "presets":[ "es2015" ], "plugins":[] }
這個文件咱們創建完成後,如今能夠在終端輸入的轉換命令了,此次ES6成功轉化爲ES5的語法。 babel dist/index.js -o src/index.js
{ "name": "es6", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "babel src/index.js -o dist/index.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.24.1", "babel-preset-es2015": "^6.24.1" } }
修改好後,之後咱們就可使用 npm run build 來進行轉換了。
webpack的loaders是一塊很重要的組成部分。咱們都知道webpack是用於資源打包的,裏面的全部資源都是「模塊」,內部實現了對模塊資源進行加載的機制。可是Webpack自己只能處理 js模塊,若是要處理其餘類型的文件,就須要使用 loader 進行轉換。
Loader 能夠理解爲是模塊和資源的轉換器,它自己是一個函數,接受源文件做爲參數,返回轉換的結果,例如可使用loader加載器能夠快速編譯預處理器(less,sass,coffeeScript)。 Loader 能夠在require()引用模塊的時候添加,也能夠在 webpack 全局配置中進行綁定,還能夠經過命令行的方式使用。
loader的特性是:
ES6以前已經出現了js模塊加載的方案,最主要的是CommonJS和AMD規範。commonjs主要應用於服務器,實現同步加載,如nodejs。AMD規範應用於瀏覽器,如requirejs,爲異步加載。同時還有CMD規範,爲同步加載方案如seaJS。
ES6在語言規格的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代現有的CommonJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。
ES6模塊主要有兩個功能:export和import
export用於對外輸出本模塊(一個文件能夠理解爲一個模塊)變量的接口
import用於在一個模塊中加載另外一個含有export接口的模塊。
CommonJS模塊的重要特性是加載時執行,即腳本代碼在require的時候,就會所有執行。一旦出現某個模塊被「循環加載」就只輸出已經執行的部分,尚未執行的部分是不輸出的。
ES6模塊是動態引用,若是使用import從一個模塊加載變量,那些變量不會緩存,而是成爲一個指向被加載模塊的引用,須要開發者本身保證,真正取值的時候可以取到值
impor/export(都是語法糖啦)最終都是編譯爲require/exports來執行的
CommonJS規範規定,每一個模塊內部,module變量表明當前模塊,這個變量是一個對象,他的exports屬性是對外的接口,加載某個模塊,實際上是加載該模塊module.exports屬性。export命令規定的是對外的接口,必須與模塊內部的變量創建一一對應的關係
AMD是RequireJS在推廣過程當中對模塊定義的規範化產出,它是一個概念,RequireJS是對這個概念的實現,就比如JavaScript語言是對ECMAScript規範的實現。AMD是一個組織,RequireJS是在這個組織下自定義的一套腳本語言
CMD---是SeaJS在推廣過程當中對模塊定義的規範化產出,是一個同步模塊定義,是SeaJS的一個標準,SeaJS是CMD概念的一個實現,SeaJS是淘寶團隊提供的一個模塊開發的js框架
CommonJS規範---是經過module.exports定義的,在前端瀏覽器裏面並不支持module.exports,經過node.js後端使用的。Nodejs端是使用CommonJS規範的,前端瀏覽器通常使用AMD、CMD、ES6等定義模塊化開發的
bable.js
babel是一個轉譯器,感受相對於編譯器compiler,叫轉譯器transpiler更準確,由於它只是把同種語言的高版本規則翻譯成低版本規則,而不像編譯器那樣,輸出的是另外一種更低級的語言代碼。
可是和編譯器相似,babel的轉譯過程也分爲三個階段:parsing、transforming、generating,以ES6代碼轉譯爲ES5代碼爲例,babel轉譯的具體過程以下:
ES6代碼輸入 ==》 babylon進行解析 ==》 獲得AST
==》 plugin用babel-traverse對AST樹進行遍歷轉譯 ==》 獲得新的AST樹
==》 用babel-generator經過AST樹生成ES5代碼
此外,還要注意很重要的一點就是,babel只是轉譯新標準引入的語法,好比ES6的箭頭函數轉譯成ES5的函數;而新標準引入的新的原生對象,部分原生對象新增的原型方法,新增的API等(如Proxy、Set等),這些babel是不會轉譯的。須要用戶自行引入polyfill來解決
//摘自博客:https://blog.csdn.net/qq_42149830/article/details/88295747 function _defineProperties(target,prop){ prop.forEach(ele => { //可能會傳入多個屬性 Object.defineProperty(target,ele.key,{ value:ele.value, writable:true, configurable:true, }) });//設置所設置的屬性是否可寫,可枚舉 } function _createClass(_constructor,_prototypeProperties,_staticProperties){ //這裏傳入的三個參數分別是構造函數,原型上的屬性,靜態屬性 if(_prototypeProperties){ //設置公有屬性 _defineProperties(_constructor.prototype,_prototypeProperties) } if(_staticProperties){ //設置靜態屬性 _defineProperties(_constructor,_staticProperties) } } function _classCallCheck(_this,_constructor){ if(!(_this instanceof _constructor)){ //判斷是不是經過new(建立實例)來調用_constructor throw "TypeError: Class constructor AirPlane cannot be invoked without 'new'" } } var FatherPlane=(function(){ function FatherPlane(name,color){ _classCallCheck(this,FatherPlane) this.name=name||'liu'; this.color=color||'red' } _createClass(FatherPlane,[ { key:'fly', value:function(){ console.log('fly') } } ],[ { key:'static', value:function(){ console.log('static') } } ]) return FatherPlane; })() var airplane=new FatherPlane()