在新公司的第一個項目是區(qū)塊鏈相關(guān)的管理后臺和交易所,其中就涉及了很多的計算問題。而JavaScript因為存在計算的精度問題,所以直接計算就可能會導(dǎo)致各種各樣的bug,為了解決這個問題,就要使用BigNumber.js這個庫。
成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),思明企業(yè)網(wǎng)站建設(shè),思明品牌網(wǎng)站建設(shè),網(wǎng)站定制,思明網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,思明網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
至于為什么JavaScript會有精度問題呢,可以看 這里 。簡單來說就是因為: JavaScript中所有的數(shù)字(包括整數(shù)和小數(shù))都只有一種類型–Number。它的實現(xiàn)遵循IEEE 754標(biāo)準(zhǔn),使用64位固定長度來表示,也就是標(biāo)準(zhǔn)的double雙精度浮點數(shù)。它的優(yōu)點是可以歸一化處理整數(shù)和小數(shù),節(jié)省儲存空間。而實際計算的時候會轉(zhuǎn)換成二進(jìn)制計算再轉(zhuǎn)成十進(jìn)制。進(jìn)制轉(zhuǎn)換之后會很長,舍去一部分,計算再轉(zhuǎn)回來,就有了精度誤差。
BigNumber.js是一個用于任意精度計算的js庫??梢栽? 官方文檔 ?的console中測試使用。也可以通過npm install bignumber.js --save來安裝。然后?import BigNumber from 'bignumber.js'?來引入使用。他的大概原理是將所有數(shù)字當(dāng)做字符串,重新實現(xiàn)了計算邏輯。缺點是性能比原生的差很多。
現(xiàn)在 TC39 已經(jīng)有一個 Stage 3 的提案 proposal bigint,大數(shù)問題有望徹底解決。在瀏覽器正式支持前,可以使用 Babel 7.0 來實現(xiàn),它的內(nèi)部是自動轉(zhuǎn)換成 big-integer 來計算,要注意的是這樣能保持精度但運算效率會降低。
具體用法可以參考以下資料:
官方文檔
bignumber.js使用記錄
BigNumber 講解
就不再敖述了,下邊隨便寫點常用的方法:
// 轉(zhuǎn)為 bignumberconstx=newBigNumber('123456789.123456789');// 轉(zhuǎn)為 普通數(shù)字x.toNumber()// 格式化(小數(shù)點)x.toFormat()// '123,456,789.123456789'x.toFormat(3)// '123,456,789.123'// 計算x.plus(0.1)// 加法x.minus(0.1)// 減法x.times(0.1)// 乘法x.div(0.1)// 除法x.mod(3)// 取模/取余// 比較大小x.eq(y)// isEqualTo 的簡寫,是否相等x.gt(y)// isGreaterThan 的簡寫,是否大于x.gte(y)// isGreaterThanOrEqualTo 的簡寫,是否大于等于x.lt(y)// isLessThan 的簡寫,是否小于x.lte(y)// isLessThanOrEqualTo 的簡寫,是否小于等于// 取非,改變數(shù)字的正負(fù)號x.negated()
前端的同學(xué)對 Promise 肯定都很熟悉,而 Future 便是 dart 中 Promise ,但方法名稱和使用方式還是有些許的差異的。
下面我們嘗試,利用 Future 封裝出js中我們熟悉的 Promise 。
使用示例
Promise.all , Promise.race , Promise.resolve , Promise.reject
Promise.allSettled 方法接受一組 Promise 實例作為參數(shù),包裝成一個新的 Promise 實例。只有等到所有這些參數(shù)實例都返回結(jié)果,不管是 fulfilled 還是 rejected ,包裝實例才會結(jié)束。該方法由 ES2020 引入
Promise.any() 方法接受一組 Promise 實例作為參數(shù),包裝成一個新的 Promise 實例。只要參數(shù)實例有一個變成 fulfilled 狀態(tài),包裝實例就會變成 fulfilled 狀態(tài);如果所有參數(shù)實例都變成 rejected 狀態(tài),包裝實例就會變成 rejected 狀態(tài)。該方法目前是一個第三階段的 提案 。
Promise.any() 跟 Promise.race() 方法很像,只有一點不同,就是不會因為某個 Promise 變成 rejected 狀態(tài)而結(jié)束。
順便把 延遲函數(shù) 也封裝一下,畢竟毫秒延遲的使用頻率是最高的。
一、JavaScript異步編程的兩個核心難點
異步I/O、事件驅(qū)動使得單線程的JavaScript得以在不阻塞UI的情況下執(zhí)行網(wǎng)絡(luò)、文件訪問功能,且使之在后端實現(xiàn)了較高的性能。然而異步風(fēng)格也引來了一些麻煩,其中比較核心的問題是:
1、函數(shù)嵌套過深
JavaScript的異步調(diào)用基于回調(diào)函數(shù),當(dāng)多個異步事務(wù)多級依賴時,回調(diào)函數(shù)會形成多級的嵌套,代碼變成
金字塔型結(jié)構(gòu)。這不僅使得代碼變難看難懂,更使得調(diào)試、重構(gòu)的過程充滿風(fēng)險。
2、異常處理
回調(diào)嵌套不僅僅是使代碼變得雜亂,也使得錯誤處理更復(fù)雜。這里主要講講異常處理。
二、異常處理
像很多時髦的語言一樣,JavaScript 也允許拋出異常,隨后再用一個try/catch
語句塊捕獲。如果拋出的異常未被捕獲,大多數(shù)JavaScript環(huán)境都會提供一個有用的堆棧軌跡。舉個例子,下面這段代碼由于'{'為無效JSON
對象而拋出異常。
?
12345678
function JSONToObject(jsonStr) { return JSON.parse(jsonStr);}var obj = JSONToObject('{');//SyntaxError: Unexpected end of input//at Object.parse (native)//at JSONToObject (/AsyncJS/stackTrace.js:2:15)//at Object.anonymous (/AsyncJS/stackTrace.js:4:11)
堆棧軌跡不僅告訴我們哪里拋出了錯誤,而且說明了最初出錯的地方:第4 行代碼。遺憾的是,自頂向下地跟蹤異步錯誤起源并不都這么直截了當(dāng)。
異步編程中可能拋出錯誤的情況有兩種:回調(diào)函數(shù)錯誤、異步函數(shù)錯誤。
1、回調(diào)函數(shù)錯誤
如果從異步回調(diào)中拋出錯誤,會發(fā)生什么事?讓我們先來做個測試。
?
1234567
setTimeout(function A() { setTimeout(function B() { setTimeout(function C() { throw new Error('Something terrible has happened!'); }, 0); }, 0);}, 0);
上述應(yīng)用的結(jié)果是一條極其簡短的堆棧軌跡。
?
12
Error: Something terrible has happened!at Timer.C (/AsyncJS/nestedErrors.js:4:13)
等等,A 和B 發(fā)生了什么事?為什么它們沒有出現(xiàn)在堆棧軌跡中?這是因為運行C 的時候,異步函數(shù)的上下文已經(jīng)不存在了,A 和B 并不在內(nèi)存堆棧里。這3
個函數(shù)都是從事件隊列直接運行的。基于同樣的理由,利用try/catch
語句塊并不能捕獲從異步回調(diào)中拋出的錯誤。另外回調(diào)函數(shù)中的return也失去了意義。
?
1234567
try { setTimeout(function() { throw new Error('Catch me if you can!'); }, 0);} catch (e) {console.error(e);}
看到這里的問題了嗎?這里的try/catch 語句塊只捕獲setTimeout函數(shù)自身內(nèi)部發(fā)生的那些錯誤。因為setTimeout
異步地運行其回調(diào),所以即使延時設(shè)置為0,回調(diào)拋出的錯誤也會直接流向應(yīng)用程序。
總的來說,取用異步回調(diào)的函數(shù)即使包裝上try/catch 語句塊,也只是無用之舉。(特例是,該異步函數(shù)確實是在同步地做某些事且容易出錯。例如,Node
的fs.watch(file,callback)就是這樣一個函數(shù),它在目標(biāo)文件不存在時會拋出一個錯誤。)正因為此,Node.js
中的回調(diào)幾乎總是接受一個錯誤作為其首個參數(shù),這樣就允許回調(diào)自己來決定如何處理這個錯誤。
2、異步函數(shù)錯誤
由于異步函數(shù)是立刻返回的,異步事務(wù)中發(fā)生的錯誤是無法通過try-catch來捕捉的,只能采用由調(diào)用方提供錯誤處理回調(diào)的方案來解決。
例如Node中常見的function (err, ...)
{...}回調(diào)函數(shù),就是Node中處理錯誤的約定:即將錯誤作為回調(diào)函數(shù)的第一個實參返回。再比如HTML5中FileReader對象的onerror函數(shù),會被用于處理異步讀取文件過程中的錯誤。
舉個例子,下面這個Node 應(yīng)用嘗試異步地讀取一個文件,還負(fù)責(zé)記錄下任何錯誤(如“文件不存在”)。
?
1234567
var fs = require('fs'); fs.readFile('fhgwgdz.txt', function(err, data) { if (err) { return console.error(err); }; console.log(data.toString('utf8'));});
客戶端JavaScript 庫的一致性要稍微差些,不過最常見的模式是,針對成敗這兩種情形各規(guī)定一個單獨的回調(diào)。jQuery 的Ajax
方法就遵循了這個模式。
?
1234
$.get('/data', { success: successHandler, failure: failureHandler});
不管API 形態(tài)像什么,始終要記住的是,只能在回調(diào)內(nèi)部處理源于回調(diào)的異步錯誤。
三、未捕獲異常的處理
如果是從回調(diào)中拋出異常的,則由那個調(diào)用了回調(diào)的人負(fù)責(zé)捕獲該異常。但如果異常從未被捕獲,又會怎么樣?這時,不同的JavaScript環(huán)境有著不同的游戲規(guī)則……
1. 在瀏覽器環(huán)境中
現(xiàn)代瀏覽器會在開發(fā)人員控制臺顯示那些未捕獲的異常,接著返回事件隊列。要想修改這種行為,可以給window.onerror
附加一個處理器。如果windows.onerror 處理器返回true,則能阻止瀏覽器的默認(rèn)錯誤處理行為。
?
123
window.onerror = function(err) { return true; //徹底忽略所有錯誤};
在成品應(yīng)用中, 會考慮某種JavaScript 錯誤處理服務(wù), 譬如Errorception。Errorception
提供了一個現(xiàn)成的windows.onerror 處理器,它向應(yīng)用服務(wù)器報告所有未捕獲的異常,接著應(yīng)用服務(wù)器發(fā)送消息通知我們。
2. 在Node.js 環(huán)境中
在Node 環(huán)境中,window.onerror 的類似物就是process 對象的uncaughtException 事件。正常情況下,Node
應(yīng)用會因未捕獲的異常而立即退出。但只要至少還有一個uncaughtException 事件處理
器,Node 應(yīng)用就會直接返回事件隊列。
?
123
process.on('uncaughtException', function(err) { console.error(err); //避免了關(guān)停的命運!});
但是,自Node 0.8.4 起,uncaughtException 事件就被廢棄了。據(jù)其文檔所言,對異常處理而言,uncaughtException
是一種非常粗暴的機(jī)制,請勿使用uncaughtException,而應(yīng)使用Domain 對象。
Domain 對象又是什么?你可能會這樣問。Domain 對象是事件化對象,它將throw 轉(zhuǎn)化為'error'事件。下面是一個例子。
?
123456789
var myDomain = require('domain').create();myDomain.run(function() { setTimeout(function() { throw new Error('Listen to me!') }, 50);});myDomain.on('error', function(err) { console.log('Error ignored!');});
源于延時事件的throw 只是簡單地觸發(fā)了Domain 對象的錯誤處理器。
Error ignored!
很奇妙,是不是?Domain 對象讓throw
語句生動了很多。不管在瀏覽器端還是服務(wù)器端,全局的異常處理器都應(yīng)被視作最后一根救命稻草。請僅在調(diào)試時才使用它。
四、幾種解決方案
下面對幾種解決方案的討論主要集中于上面提到的兩個核心問題上,當(dāng)然也會考慮其他方面的因素來評判其優(yōu)缺點。
1、Async.js
首先是Node中非常著名的Async.js,這個庫能夠在Node中展露頭角,恐怕也得歸功于Node統(tǒng)一的錯誤處理約定。
而在前端,一開始并沒有形成這么統(tǒng)一的約定,因此使用Async.js的話可能需要對現(xiàn)有的庫進(jìn)行封裝。
Async.js的其實就是給回調(diào)函數(shù)的幾種常見使用模式加了一層包裝。比如我們需要三個前后依賴的異步操作,采用純回調(diào)函數(shù)寫法如下:
?
12345678910111213141516
asyncOpA(a, b, (err, result) = { if (err) { handleErrorA(err); } asyncOpB(c, result, (err, result) = { if (err) { handleErrorB(err); } asyncOpB(d, result, (err, result) = { if (err) { handlerErrorC(err); } finalOp(result); }); });});
如果我們采用async庫來做:
?
12345678910111213141516171819202122
async.waterfall([ (cb) = { asyncOpA(a, b, (err, result) = { cb(err, c, result); }); }, (c, lastResult, cb) = { asyncOpB(c, lastResult, (err, result) = { cb(err, d, result); }) }, (d, lastResult, cb) = { asyncOpC(d, lastResult, (err, result) = { cb(err, result); }); }], (err, finalResult) = { if (err) { handlerError(err); } finalOp(finalResult);});
可以看到,回調(diào)函數(shù)由原來的橫向發(fā)展轉(zhuǎn)變?yōu)榭v向發(fā)展,同時錯誤被統(tǒng)一傳遞到最后的處理函數(shù)中。
其原理是,將函數(shù)數(shù)組中的后一個函數(shù)包裝后作為前一個函數(shù)的末參數(shù)cb傳入,同時要求:
每一個函數(shù)都應(yīng)當(dāng)執(zhí)行其cb參數(shù);cb的第一個參數(shù)用來傳遞錯誤。我們可以自己寫一個async.waterfall的實現(xiàn):
?
12345678910111213141516171819202122
let async = { waterfall: (methods, finalCb = _emptyFunction) = { if (!_isArray(methods)) { return finalCb(new Error('First argument to waterfall must be an array of functions')); } if (!methods.length) { return finalCb(); } function wrap(n) { if (n === methods.length) { return finalCb; } return function (err, ...args) { if (err) { return finalCb(err); } methods[n](...args, wrap(n + 1)); } } wrap(0)(false); }};
Async.js還有series/parallel/whilst等多種流程控制方法,來實現(xiàn)常見的異步協(xié)作。
Async.js的問題:
在外在上依然沒有擺脫回調(diào)函數(shù),只是將其從橫向發(fā)展變?yōu)榭v向,還是需要程序員熟練異步回調(diào)風(fēng)格。
錯誤處理上仍然沒有利用上try-catch和throw,依賴于“回調(diào)函數(shù)的第一個參數(shù)用來傳遞錯誤”這樣的一個約定。
2、Promise方案
ES6的Promise來源于Promise/A+。使用Promise來進(jìn)行異步流程控制,有幾個需要注意的問題,
把前面提到的功能用Promise來實現(xiàn),需要先包裝異步函數(shù),使之能返回一個Promise:
?
12345678910
function toPromiseStyle(fn) { return (...args) = { return new Promise((resolve, reject) = { fn(...args, (err, result) = { if (err) reject(err); resolve(result); }) }); };}
這個函數(shù)可以把符合下述規(guī)則的異步函數(shù)轉(zhuǎn)換為返回Promise的函數(shù):
回調(diào)函數(shù)的第一個參數(shù)用于傳遞錯誤,第二個參數(shù)用于傳遞正常的結(jié)果。接著就可以進(jìn)行操作了:
?
123456789101112131415
let [opA, opB, opC] = [asyncOpA, asyncOpB, asyncOpC].map((fn) = toPromiseStyle(fn)); opA(a, b) .then((res) = { return opB(c, res); }) .then((res) = { return opC(d, res); }) .then((res) = { return finalOp(res); }) .catch((err) = { handleError(err); });
通過Promise,原來明顯的異步回調(diào)函數(shù)風(fēng)格顯得更像同步編程風(fēng)格,我們只需要使用then方法將結(jié)果傳遞下去即可,同時return也有了相應(yīng)的意義:
在每一個then的onFullfilled函數(shù)(以及onRejected)里的return,都會為下一個then的onFullfilled函數(shù)(以及onRejected)的參數(shù)設(shè)定好值。
如此一來,return、try-catch/throw都可以使用了,但catch是以方法的形式出現(xiàn),還是不盡如人意。
3、Generator方案
ES6引入的Generator可以理解為可在運行中轉(zhuǎn)移控制權(quán)給其他代碼,并在需要的時候返回繼續(xù)執(zhí)行的函數(shù)。利用Generator可以實現(xiàn)協(xié)程的功能。
將Generator與Promise結(jié)合,可以進(jìn)一步將異步代碼轉(zhuǎn)化為同步風(fēng)格:
?
1234567891011
function* getResult() { let res, a, b, c, d; try { res = yield opA(a, b); res = yield opB(c, res); res = yield opC(d); return res; } catch (err) { return handleError(err); }}
然而我們還需要一個可以自動運行Generator的函數(shù):
?
123456789101112131415161718192021222324252627282930
function spawn(genF, ...args) { return new Promise((resolve, reject) = { let gen = genF(...args); function next(fn) { try { let r = fn(); if (r.done) { resolve(r.value); } Promise.resolve(r.value) .then((v) = { next(() = { return gen.next(v); }); }).catch((err) = { next(() = { return gen.throw(err); }) }); } catch (err) { reject(err); } } next(() = { return gen.next(undefined); }); });}
用這個函數(shù)來調(diào)用Generator即可:
?
1234567
spawn(getResult) .then((res) = { finalOp(res); }) .catch((err) = { handleFinalOpError(err); });
可見try-catch和return實際上已經(jīng)以其原本面貌回到了代碼中,在代碼形式上也已經(jīng)看不到異步風(fēng)格的痕跡。
類似的功能有co/task.js等庫實現(xiàn)。
4、ES7的async/await
ES7中將會引入async function和await關(guān)鍵字,利用這個功能,我們可以輕松寫出同步風(fēng)格的代碼,
同時依然可以利用原有的異步I/O機(jī)制。
采用async function,我們可以將之前的代碼寫成這樣:
?
12345678910111213
async function getResult() { let res, a, b, c, d; try { res = await opA(a, b); res = await opB(c, res); res = await opC(d); return res; } catch (err) { return handleError(err); }} getResult();
和Generator Promise方案看起來沒有太大區(qū)別,只是關(guān)鍵字換了換。
實際上async
function就是對Generator方案的一個官方認(rèn)可,將之作為語言內(nèi)置功能。
async function的缺點:
await只能在async function內(nèi)部使用,因此一旦你寫了幾個async function,或者使用了依賴于async
function的庫,那你很可能會需要更多的async function。
目前處于提案階段的async
function還沒有得到任何瀏覽器或Node.JS/io.js的支持。Babel轉(zhuǎn)碼器也需要打開實驗選項,并且對于不支持Generator的瀏覽器來說,還需要引進(jìn)一層厚厚的regenerator
runtime,想在前端生產(chǎn)環(huán)境得到應(yīng)用還需要時間。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
ECMA規(guī)范最終由TC39敲定。TC39由包括瀏覽器廠商在內(nèi)的各方組成,他們開會推動JavaScript提案沿著一條嚴(yán)格的發(fā)展道路前進(jìn)。 從提案到入選ECMA規(guī)范主要有以下幾個階段:
ES6的特性比較多,在 ES5 發(fā)布近 6 年(2009-11 至 2015-6)之后才將其標(biāo)準(zhǔn)化。兩個發(fā)布版本之間時間跨度很大,所以ES6中的特性比較多。 在這里列舉幾個常用的:
1.類(class)
對熟悉Java,object-c,c#等純面向?qū)ο笳Z言的開發(fā)者來說,都會對class有一種特殊的情懷。ES6 引入了class(類),讓JavaScript的面向?qū)ο缶幊套兊酶雍唵魏鸵子诶斫狻?/p>
2.模塊化(Module)
ES5不支持原生的模塊化,在ES6中模塊作為重要的組成部分被添加進(jìn)來。模塊的功能主要由 export 和 import 組成。每一個模塊都有自己單獨的作用域,模塊之間的相互調(diào)用關(guān)系是通過 export 來規(guī)定模塊對外暴露的接口,通過import來引用其它模塊提供的接口。同時還為模塊創(chuàng)造了命名空間,防止函數(shù)的命名沖突。
導(dǎo)出(export)
ES6允許在一個模塊中使用export來導(dǎo)出多個變量或函數(shù)。
導(dǎo)出變量
ES6將一個文件視為一個模塊,上面的模塊通過 export 向外輸出了一個變量。一個模塊也可以同時往外面輸出多個變量。
導(dǎo)出函數(shù)
導(dǎo)入(import)
定義好模塊的輸出以后就可以在另外一個模塊通過import引用。
3.箭頭(Arrow)函數(shù)
這是ES6中最令人激動的特性之一。=不只是關(guān)鍵字function的簡寫,它還帶來了其它好處。箭頭函數(shù)與包圍它的代碼共享同一個this,能幫你很好的解決this的指向問題。有經(jīng)驗的JavaScript開發(fā)者都熟悉諸如var self = this;或var that = this這種引用外圍this的模式。但借助=,就不需要這種模式了。
箭頭函數(shù)的結(jié)構(gòu)
箭頭函數(shù)的箭頭=之前是一個空括號、單個的參數(shù)名、或用括號括起的多個參數(shù)名,而箭頭之后可以是一個表達(dá)式(作為函數(shù)的返回值),或者是用花括號括起的函數(shù)體(需要自行通過return來返回值,否則返回的是undefined)。
卸載監(jiān)聽器時的陷阱
除上述的做法外,我們還可以這樣做:
4.函數(shù)參數(shù)默認(rèn)值
ES6支持在定義函數(shù)的時候為其設(shè)置默認(rèn)值:
這樣寫一般沒問題,但當(dāng)參數(shù)的布爾值為false時,就會有問題了。比如,我們這樣調(diào)用foo函數(shù):
foo(0, "")
因為0的布爾值為false,這樣height的取值將是50。同理color的取值為‘red’。
所以說,函數(shù)參數(shù)默認(rèn)值不僅能是代碼變得更加簡潔而且能規(guī)避一些問題。
5.模板字符串
ES6支持模板字符串,使得字符串的拼接更加的簡潔、直觀。
在ES6中通過${}就可以完成字符串的拼接,只需要將變量放在大括號之中。
6.解構(gòu)賦值
解構(gòu)賦值語法是JavaScript的一種表達(dá)式,可以方便的從數(shù)組或者對象中快速提取值賦給定義的變量。
獲取數(shù)組中的值
從數(shù)組中獲取值并賦值到變量中,變量的順序與數(shù)組中對象順序?qū)?yīng)。
如果沒有從數(shù)組中的獲取到值,你可以為變量設(shè)置一個默認(rèn)值。
通過解構(gòu)賦值可以方便的交換兩個變量的值。
獲取對象中的值
7.延展操作符(Spread operator)
延展操作符...可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時, 將數(shù)組表達(dá)式或者string在語法層面展開;還可以在構(gòu)造對象時, 將對象表達(dá)式按key-value的方式展開。
語法
應(yīng)用場景
沒有展開語法的時候,只能組合使用 push,splice,concat 等方法,來將已有數(shù)組元素變成新數(shù)組的一部分。有了展開語法, 構(gòu)造新數(shù)組會變得更簡單、更優(yōu)雅:
和參數(shù)列表的展開類似, ... 在構(gòu)造字?jǐn)?shù)組時, 可以在任意位置多次使用。
展開語法和 Object.assign() 行為一致, 執(zhí)行的都是淺拷貝(只遍歷一層)。
在ECMAScript 2018中延展操作符增加了對對象的支持
8.對象屬性簡寫
在ES6中允許我們在設(shè)置一個對象的屬性的時候不指定屬性名。
對象中必須包含屬性和值,顯得非常冗余。
對象中直接寫變量,非常簡潔。
9.Promise
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案callback更加的優(yōu)雅。它最早由社區(qū)提出和實現(xiàn)的,ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對象。
嵌套兩個setTimeout回調(diào)函數(shù):
上面的的代碼使用兩個then來進(jìn)行異步編程串行化,避免了回調(diào)地獄:
10.支持let與const
在之前JS是沒有塊級作用域的,const與let填補(bǔ)了這方便的空白,const與let都是塊級作用域。
ES2016添加了兩個小的特性來說明標(biāo)準(zhǔn)化過程:
1.Array.prototype.includes()
includes() 函數(shù)用來判斷一個數(shù)組是否包含一個指定的值,如果包含則返回 true,否則返回false。
includes 函數(shù)與 indexOf 函數(shù)很相似,下面兩個表達(dá)式是等價的:
接下來我們來判斷數(shù)字中是否包含某個元素:
使用indexOf()驗證數(shù)組中是否存在某個元素,這時需要根據(jù)返回值是否為-1來判斷:
使用includes()驗證數(shù)組中是否存在某個元素,這樣更加直觀簡單:
2.指數(shù)操作符
在ES7中引入了指數(shù)運算符**,**具有與Math.pow(..)等效的計算結(jié)果。
使用自定義的遞歸函數(shù)calculateExponent或者M(jìn)ath.pow()進(jìn)行指數(shù)運算:
使用指數(shù)運算符**,就像+、-等操作符一樣:
1.async/await
ES2018引入異步迭代器(asynchronous iterators),這就像常規(guī)迭代器,除了next()方法返回一個Promise。因此await可以和for...of循環(huán)一起使用,以串行的方式運行異步操作。例如:
2.Object.values()
Object.values()是一個與Object.keys()類似的新函數(shù),但返回的是Object自身屬性的所有值,不包括繼承的值。
假設(shè)我們要遍歷如下對象obj的所有值:
從上述代碼中可以看出Object.values()為我們省去了遍歷key,并根據(jù)這些key獲取value的步驟。
3.Object.entries()
Object.entries()函數(shù)返回一個給定對象自身可枚舉屬性的鍵值對的數(shù)組。
接下來我們來遍歷上文中的obj對象的所有屬性的key和value:
4.String padding
在ES8中String新增了兩個實例函數(shù)String.prototype.padStart和String.prototype.padEnd,允許將空字符串或其他字符串添加到原始字符串的開頭或結(jié)尾。
5.函數(shù)參數(shù)列表結(jié)尾允許逗號
主要作用是方便使用git進(jìn)行多人協(xié)作開發(fā)時修改同一個函數(shù)減少不必要的行變更。
6.Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors()函數(shù)用來獲取一個對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。
返回obj對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。
7.SharedArrayBuffer對象
SharedArrayBuffer 對象用來表示一個通用的,固定長度的原始二進(jìn)制數(shù)據(jù)緩沖區(qū),類似于 ArrayBuffer 對象,它們都可以用來在共享內(nèi)存(shared memory)上創(chuàng)建視圖。與 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分離。
8.Atomics對象
Atomics 對象提供了一組靜態(tài)方法用來對 SharedArrayBuffer 對象進(jìn)行原子操作。
這些原子操作屬于 Atomics 模塊。與一般的全局對象不同,Atomics 不是構(gòu)造函數(shù),因此不能使用 new 操作符調(diào)用,也不能將其當(dāng)作函數(shù)直接調(diào)用。Atomics 的所有屬性和方法都是靜態(tài)的(與 Math 對象一樣)。
多個共享內(nèi)存的線程能夠同時讀寫同一位置上的數(shù)據(jù)。原子操作會確保正在讀或?qū)懙臄?shù)據(jù)的值是符合預(yù)期的,即下一個原子操作一定會在上一個原子操作結(jié)束后才會開始,其操作過程不會中斷。
wait() 和 wake() 方法采用的是 Linux 上的 futexes 模型(fast user-space mutex,快速用戶空間互斥量),可以讓進(jìn)程一直等待直到某個特定的條件為真,主要用于實現(xiàn)阻塞。
1.異步迭代
在async/await的某些時刻,你可能嘗試在同步循環(huán)中調(diào)用異步函數(shù)。例如:
這段代碼不會正常運行,下面這段同樣也不會:
這段代碼中,循環(huán)本身依舊保持同步,并在在內(nèi)部異步函數(shù)之前全部調(diào)用完成。
ES2018引入異步迭代器(asynchronous iterators),這就像常規(guī)迭代器,除了next()方法返回一個Promise。因此await可以和for...of循環(huán)一起使用,以串行的方式運行異步操作。例如:
2.Promise.finally()
一個Promise調(diào)用鏈要么成功到達(dá)最后一個.then(),要么失敗觸發(fā).catch()。在某些情況下,你想要在無論Promise運行成功還是失敗,運行相同的代碼,例如清除,刪除對話,關(guān)閉數(shù)據(jù)庫連接等。
.finally()允許你指定最終的邏輯:
3.Rest/Spread 屬性
ES2015引入了Rest參數(shù)和擴(kuò)展運算符。三個點(...)僅用于數(shù)組。Rest參數(shù)語法允許我們將一個不定數(shù)量的參數(shù)表示為一個數(shù)組。
展開操作符以相反的方式工作,將數(shù)組轉(zhuǎn)換成可傳遞給函數(shù)的單獨參數(shù)。例如Math.max()返回給定數(shù)字中的最大值:
ES2018為對象解構(gòu)提供了和數(shù)組一樣的Rest參數(shù)()和展開操作符,一個簡單的例子:
或者你可以使用它給函數(shù)傳遞參數(shù):
擴(kuò)展運算符可以在其他對象內(nèi)使用,例如:
可以使用擴(kuò)展運算符拷貝一個對象,像是這樣obj2 = {...obj1},但是 這只是一個對象的淺拷貝 。另外,如果一個對象A的屬性是對象B,那么在克隆后的對象cloneB中,該屬性指向?qū)ο驜。
4.正則表達(dá)式命名捕獲組
JavaScript正則表達(dá)式可以返回一個匹配的對象——一個包含匹配字符串的類數(shù)組,例如:以YYYY-MM-DD的格式解析日期:
這樣的代碼很難讀懂,并且改變正則表達(dá)式的結(jié)構(gòu)有可能改變匹配對象的索引。
ES2018允許命名捕獲組使用符號?name,在打開捕獲括號(后立即命名,示例如下:
任何匹配失敗的命名組都將返回undefined。
命名捕獲也可以使用在replace()方法中。例如將日期轉(zhuǎn)換為美國的 MM-DD-YYYY 格式:
5.正則表達(dá)式反向斷言
目前JavaScript在正則表達(dá)式中支持先行斷言(lookahead)。這意味著匹配會發(fā)生,但不會有任何捕獲,并且斷言沒有包含在整個匹配字段中。例如從價格中捕獲貨幣符號:
ES2018引入以相同方式工作但是匹配前面的反向斷言(lookbehind),這樣我就可以忽略貨幣符號,單純的捕獲價格的數(shù)字:
以上是 肯定反向斷言 ,非數(shù)字\D必須存在。同樣的,還存在 否定反向斷言 ,表示一個值必須不存在,例如:
6.正則表達(dá)式dotAll模式
正則表達(dá)式中點.匹配除回車外的任何單字符,標(biāo)記s改變這種行為,允許行終止符的出現(xiàn),例如:
7.正則表達(dá)式 Unicode 轉(zhuǎn)義
到目前為止,在正則表達(dá)式中本地訪問 Unicode 字符屬性是不被允許的。ES2018添加了 Unicode 屬性轉(zhuǎn)義——形式為\p{...}和\P{...},在正則表達(dá)式中使用標(biāo)記 u (unicode) 設(shè)置,在\p塊兒內(nèi),可以以鍵值對的方式設(shè)置需要匹配的屬性而非具體內(nèi)容。例如:
此特性可以避免使用特定 Unicode 區(qū)間來進(jìn)行內(nèi)容類型判斷,提升可讀性和可維護(hù)性。
8.非轉(zhuǎn)義序列的模板字符串
之前,\\u開始一個 unicode 轉(zhuǎn)義,\\x開始一個十六進(jìn)制轉(zhuǎn)義,\后跟一個數(shù)字開始一個八進(jìn)制轉(zhuǎn)義。這使得創(chuàng)建特定的字符串變得不可能,例如Windows文件路徑 C:\\uuu\\xxx\111。更多細(xì)節(jié)參考模板字符串。
1.行分隔符(U + 2028)和段分隔符(U + 2029)符號現(xiàn)在允許在字符串文字中,與JSON匹配
以前,這些符號在字符串文字中被視為行終止符,因此使用它們會導(dǎo)致SyntaxError異常。
2.更加友好的 JSON.stringify
如果輸入 Unicode 格式但是超出范圍的字符,在原先JSON.stringify返回格式錯誤的Unicode字符串?,F(xiàn)在實現(xiàn)了一個改變JSON.stringify的第3階段提案,因此它為其輸出轉(zhuǎn)義序列,使其成為有效Unicode(并以UTF-8表示)
3.新增了Array的flat()方法和flatMap()方法
flat()和flatMap()本質(zhì)上就是是歸納(reduce) 與 合并(concat)的操作。
Array.prototype.flat()
flat() 方法會按照一個可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個新數(shù)組返回。
Array.prototype.flatMap()
flatMap() 方法首先使用映射函數(shù)映射每個元素,然后將結(jié)果壓縮成一個新數(shù)組。它與 map 和 深度值1的 flat 幾乎相同,但 flatMap 通常在合并成一種方法的效率稍微高一些。 這里我們拿map方法與flatMap方法做一個比較。
4.新增了String的trimStart()方法和trimEnd()方法
5.Object.fromEntries()
Object.entries()方法的作用是返回一個給定對象自身可枚舉屬性的鍵值對數(shù)組,其排列與使用 for...in 循環(huán)遍歷該對象時返回的順序一致(區(qū)別在于 for-in 循環(huán)也枚舉原型鏈中的屬性)。
而Object.fromEntries() 則是 Object.entries() 的反轉(zhuǎn)。
Object.fromEntries() 函數(shù)傳入一個鍵值對的列表,并返回一個帶有這些鍵值對的新對象。這個迭代參數(shù)應(yīng)該是一個能夠?qū)崿F(xiàn)@iterator方法的的對象,返回一個迭代器對象。它生成一個具有兩個元素的類似數(shù)組的對象,第一個元素是將用作屬性鍵的值,第二個元素是與該屬性鍵關(guān)聯(lián)的值。
6.Symbol.prototype.description
通過工廠函數(shù)Symbol()創(chuàng)建符號時,您可以選擇通過參數(shù)提供字符串作為描述:
以前,訪問描述的唯一方法是將符號轉(zhuǎn)換為字符串:
現(xiàn)在引入了getter Symbol.prototype.description以直接訪問描述:
7.String.prototype.matchAll
matchAll() 方法返回一個包含所有匹配正則表達(dá)式及分組捕獲結(jié)果的迭代器。 在 matchAll 出現(xiàn)之前,通過在循環(huán)中調(diào)用regexp.exec來獲取所有匹配項信息(regexp需使用/g標(biāo)志:
如果使用matchAll ,就可以不必使用while循環(huán)加exec方式(且正則表達(dá)式需使用/g標(biāo)志)。使用matchAll 會得到一個迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便實現(xiàn)功能:
matchAll可以更好的用于分組
8.Function.prototype.toString()現(xiàn)在返回精確字符,包括空格和注釋
9.修改 catch 綁定
在 ES10 之前,我們必須通過語法為 catch 子句綁定異常變量,無論是否有必要。很多時候 catch 塊是多余的。 ES10 提案使我們能夠簡單的把變量省略掉。
不算大的改動。
之前是
現(xiàn)在是
10.新的基本數(shù)據(jù)類型BigInt
現(xiàn)在的基本數(shù)據(jù)類型(值類型)不止5種(ES6之后是六種)了哦!加上BigInt一共有七種基本數(shù)據(jù)類型,分別是: String、Number、Boolean、Null、Undefined、Symbol、BigInt
JavaScript是 ECMAScript 標(biāo)準(zhǔn)的各種實現(xiàn)的最常用稱呼。這個術(shù)語并不局限于某個特定版本的 ECMAScript 規(guī)范,并且可能被用于任何不同程度的任意版本的 ECMAScript 的實現(xiàn)。被考慮加入未來版本 ECMAScript 標(biāo)準(zhǔn)的特性與語法提案,他們需要經(jīng)歷五個階段:Strawman(稻草人),Proposal(提議),Draft(草案),Candidate(候選)以及 Finished (完成)。
當(dāng)前文章:javascript提案的簡單介紹
地址分享:http://chinadenli.net/article22/dsiegcc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、、網(wǎng)站排名、網(wǎng)站設(shè)計公司、搜索引擎優(yōu)化、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)