摘要:本文涵蓋了一些新語(yǔ)法可能造成疑惑的地方和一些建議。新接口的迭代器參數(shù)的誤調(diào)用接口及集合類(lèi)構(gòu)造器的參數(shù),可以放入支持迭代器的內(nèi)容,而不局限于數(shù)組兼容。新集合類(lèi)容器的構(gòu)造器集合類(lèi)容器不可以通過(guò)非方式來(lái)構(gòu)造。
本文涵蓋了一些ES6新語(yǔ)法可能造成疑惑的地方和一些建議。
1# 箭頭函數(shù)箭頭函數(shù)看起來(lái)像是匿名函數(shù)表達(dá)式function(){}的簡(jiǎn)寫(xiě),然而它不是。
這個(gè)例子應(yīng)該很容易看出來(lái)會(huì)有怎樣的問(wèn)題:
function Apple(){} Apple.prototype.check = ()=>{ console.log(this instanceof Apple); }; (new Apple()).check() // false
使用apply、call、bind改變箭頭函數(shù)的this指向呢?
var i = 0; var xx = ()=>{ console.log(++i, this) }; var yy = function(){ console.log(++i, this) }; xx(); // 1 window xx.apply([]); // 2 window xx.bind([])(); // 3 window yy(); // 4 window yy.apply([]); // 5 [] yy.bind([])(); // 6 []
顯然apply、call、bind無(wú)法改變箭頭函數(shù)的this指向,箭頭函數(shù)的this確定后無(wú)法更改。
在這些場(chǎng)景中不要使用箭頭函數(shù):
當(dāng)你需要正常使用this binding時(shí),如函數(shù)構(gòu)造器、prototype
當(dāng)你需要?jiǎng)討B(tài)改變this的時(shí)候
針對(duì)工作報(bào)酬和代碼量呈反比的程序猿,在需要用到this binding的場(chǎng)景里,可能比較適合的簡(jiǎn)寫(xiě)形式是在新對(duì)象字面量語(yǔ)法里提供的:
var obj = { hello() { // 少寫(xiě)了一個(gè)function耶! console.log("world") } };2# Promise 2.1# then
//1 fetch(xx, oo).then(handleResultAndReturnsAnPromise(result)); //2 fetch(xx, oo).then(handleResultAndReturnsAnPromise); //3 fetch(xx, oo).then((result) => handleResultAndReturnsAnPromise(result)); //4 fetch(xx, oo).then(function(result) { handleResultAndReturnsAnPromise(result) });
1與2、3、4均不等價(jià):1同步調(diào)用了handleResultAndReturnsAnPromise;而2~4均會(huì)導(dǎo)致handleResultAndReturnsAnPromise在fetch之后完成
2與3/4則是運(yùn)行時(shí)的調(diào)用棧有區(qū)別,3/4額外創(chuàng)建了一個(gè)匿名函數(shù)。
3與4除了this binding的區(qū)別,4的調(diào)用返回值沒(méi)有進(jìn)行返回,這樣將導(dǎo)致promise鏈斷裂。
1中需要注意的是,then(promise)里面?zhèn)饕粋€(gè) Promise 對(duì)象是沒(méi)有什么意義的,它會(huì)被當(dāng)成then(null),在下面推薦的文章中,它被稱(chēng)作“Promise 穿透”
更多的令人混淆的案例,請(qǐng)繼續(xù)閱讀《談?wù)勈褂?promise 時(shí)候的一些反模式》。
2.2# catch在node的一些版本中,采用Promise并忘記給promise鏈增加catch(fn)或then(null, fn),將導(dǎo)致代碼中的異常被吞掉。
這個(gè)問(wèn)題在新的v8中(node 6.6+,chrome最新版)會(huì)導(dǎo)致一個(gè)UnhandledPromiseRejectionWarning,防止開(kāi)發(fā)遺漏。
node -e "Promise.reject()" # UnhandledPromiseRejectionWarning: Unhandled promise rejection2.3# resolve
Promise接口和jQuery實(shí)現(xiàn)的接口不一樣,resolve只接受單參數(shù),then的回調(diào)也只能拿到單參數(shù)。
在Promise規(guī)范中的單參數(shù)鏈?zhǔn)秸{(diào)用場(chǎng)景下,可以利用解構(gòu)、_.spread、訪問(wèn)自由變量等方式來(lái)處理多個(gè)過(guò)程中得到的值:
new Promise(function(resolve, reject){ let something = 1, otherstuff = 2; resolve({something, otherstuff}); }).then(function({something, otherstuff}){ // handle something and otherstuff });
Promise.all([ Promise.resolve(40), Promise.resolve(36) ]).then( _.spread(function(first, second){ // first: 40, second: 36 }) );
let someMiddleResult; fetch() .then(function(fetchResult){ someMiddleResult = fetchResult; }) .then(otherHandleFn) .then(function(otherHandleFnResult){ // use both someMiddleResult and otherHandleFnResult now })2.4# reject / throw
出現(xiàn)reject接口,應(yīng)該是第一次前端有機(jī)會(huì)拿異常處理流程做正常流程(比如*)。不要這樣做。
由于reject(new Error(""))、throw new Error("")都能作為catch的入口,一些不可預(yù)知的錯(cuò)誤被拋出的時(shí)候,這樣的處理方式將會(huì)復(fù)雜化catch內(nèi)的代碼。不要用異常處理邏輯來(lái)做正常處理流程,這個(gè)規(guī)則保證了代碼可讀性與可維護(hù)性。
throw和reject都可以作為catch的入口,它們更加詳細(xì)的區(qū)別如下:
new Promise((resolve, reject) => { setTimeout(function(){ reject(new Error("hello")); }); }).catch(() => console.log("reject")); // reject new Promise((resolve, reject) => { setTimeout(function(){ throw new Error("hello"); }); }).catch(() => console.log("throw")); // Uncaught Error: hello
reject能夠“穿透”回調(diào);而throw限于函數(shù)作用域,無(wú)法“穿透”回調(diào)。
建議:
正常流程請(qǐng)選擇在then的時(shí)候if..else,不要用reject替代
在需要走異常處理流程的時(shí)候封裝Error拋出,可以最大化的化簡(jiǎn)catch回調(diào)里面的處理邏輯,類(lèi)似于e instanceof MyDesignedError
由于回調(diào)函數(shù)里的throw無(wú)法被自動(dòng)捕獲到,如果需要在回調(diào)中reject當(dāng)前 promise,那么我們需要用reject而不是throw
在使用Promise接口的 polyfill 的場(chǎng)景,應(yīng)當(dāng)在reject后加一個(gè)return
3# let & const & var看起來(lái)let和const的組合就像是一個(gè)能完全滅掉var的新特性,但對(duì)舊代碼不能簡(jiǎn)單的正則替換掉var,因?yàn)槲覀兲?xí)慣于濫用它的特性了——主要是聲明提升。
一些情形下會(huì)造成語(yǔ)法錯(cuò)誤:
try { let a = 10; if (a > 2) { throw new Error(); } // ... } catch (err) { console.log(a); // 若為var聲明,不報(bào)錯(cuò) // 若為const、let聲明:Uncaught ReferenceError: a is not defined }
除了try..catch,隱式造就的塊級(jí)作用域在for和if..else中也將造成問(wèn)題:
if(false) { let my = "bad"; } else { console.log(my); // ReferenceError: my is not defined }
解決方案倒是很簡(jiǎn)單,將作用域內(nèi)的let放在更靠外層的位置即可。
var、let和const的區(qū)別如下(部分參考自stackoverflow*):
作用域:let和const將創(chuàng)造一個(gè)塊級(jí)作用域,在作用域之外此變量不可見(jiàn),作用域外訪問(wèn)將導(dǎo)致SyntaxError;var遵循函數(shù)級(jí)作用域
全局影響:全局作用域下的var使用等同于設(shè)置window/global之上的內(nèi)容,但let和const不會(huì)
提升行為:var聲明有提升到當(dāng)前函數(shù)作用域頂部的特性,但const和let沒(méi)有,在聲明前訪問(wèn)變量將導(dǎo)致SyntaxError
重新賦值:對(duì)const變量所做的重新賦值將導(dǎo)致TypeError,而var和let不會(huì)
重新聲明:var聲明的變量使用var再次聲明不會(huì)出現(xiàn)SyntaxError,但const、let聲明的變量不能被重新聲明,也不能覆蓋掉之前任何形式的聲明:
var vVar = 1; const vConst = 2; let vLet = 3; var vVar = 4; // success let vVar = 5; // SyntaxError const vVar = 6; // SyntaxError var vConst = 7; // SyntaxError let vConst = 8; // SyntaxError const vConst = 9; // SyntaxError var vLet = 10; // SyntaxError let vLet = 11; // SyntaxError const vLet = 12; // SyntaxError4# 邊界
本篇章集結(jié) ES6 給予的不同邊界條件,部分編譯自 You don"t know JS
4.1# 函數(shù)默認(rèn)參數(shù)值function before(a) { var a = a || 1; console.log(a); } function after(a = 1) { console.log(a); } before(NaN) // 1 after(NaN) // NaN
新的寫(xiě)法的fallback邏輯只針對(duì)undefined有效。
4.2# Object.assignObject.assign將賦予所有的可枚舉值,但不包含從原型鏈繼承來(lái)的值:
let arr = [1, 2, 3], obj = {}; Object.assign(obj, arr); obj[1] // 2 obj.length // undefined Object.getOwnPropertyDescriptors(arr).length.enumerable // false
此外:Object.assign僅僅進(jìn)行淺拷貝:
var orig = { a: [1, 2, 3] }, nObj = {}; Object.assign(nObj, orig); orig.a.push(4); nObj.a // [1, 2, 3, 4]4.3# NaN
Number.isNaN和全局空間中的isNaN的區(qū)別在于不存在隱式轉(zhuǎn)換:
isNaN("number") // true Number.isNaN("number") // false
Object.is除了區(qū)分正負(fù)零這個(gè)非常小眾的邊界,這個(gè)接口相對(duì)===更大的意義是判斷NaN:
Object.is(NaN, NaN); // true NaN === NaN; // false
Object.is(+0, -0); // false +0 === -0; // true
同樣的,arr.includes(xx)比arr.lastIndexOf(xx) > -1好的地方也包括對(duì)于NaN的處理:
[1, 2, NaN].includes(NaN); // true4.4# Number
isFinite和Number.isFinite的區(qū)別也是后者不存在隱式轉(zhuǎn)換:
isFinite("42"); // true Number.isFinite("42"); // false
Number.isInteger表示一個(gè)數(shù)是不是小數(shù),和x === Math.floor(x)的區(qū)別在于對(duì)Infinity的處理
Number.isInteger(Infinity); // false Infinity === Math.floor(Infinity); // true
Number.isSafeInteger表示傳入的數(shù)值有沒(méi)有精度損失,它比較的是數(shù)字是否在Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER之間:
Number.isSafeInteger(Math.pow(2, 53) - 1); // true Number.isSafeInteger(Math.pow(2, 53)); // false
我曾整理過(guò)Number的數(shù)軸(*),也寫(xiě)過(guò)JavaScript中的一些數(shù)字內(nèi)存模型的demo,其中有一部分值沒(méi)有直接的量來(lái)表示,但現(xiàn)在有了。
從負(fù)無(wú)窮往正無(wú)窮來(lái)看,是這樣的:
Number.NEGATIVE_INFINITY 負(fù)無(wú)窮
-Number.MAX_VALUE 能表示的最小數(shù)字,更小被視為負(fù)無(wú)窮,等于-(2^53-1)*(2^971)
Number.MIN_SAFE_INTEGER(新) 沒(méi)有精度誤差的最小數(shù),等于-(2^53-1)
0 正負(fù)零
Number.EPSILON(新) IEEE 754規(guī)范下的精度位允許的最小差異值,等于2^-52
Number.MIN_VALUE 能表示的最小正整數(shù),這是一個(gè)IEEE 754規(guī)范下的反規(guī)格化值,等于2^-1074
Number.MAX_SAFE_INTEGER(新) 沒(méi)有精度誤差的最大數(shù),,等于2^53-1
Number.MAX_VALUE 能表示的最大數(shù)字,更大被視為正無(wú)窮,等于(2^53-1)*(2^971)
Number.INFINITY 正無(wú)窮
比較令人混淆的是Number.EPSILON和Number.MIN_VALUE,前者為精度位允許的最小差異值,考慮的是浮點(diǎn)數(shù)的精度位;而后者考慮的是利用到浮點(diǎn)數(shù)的所有位置能夠表示的最小正數(shù)值。
5# 怪奇錯(cuò)誤展本節(jié)收集了一些奇奇怪怪的錯(cuò)誤提示,正常寫(xiě)出的代碼不會(huì)導(dǎo)致它們,沒(méi)有興趣可以略過(guò)。
5.1# 新接口的迭代器參數(shù)Array.from(1, 2, 3) // Array.of(1,2,3)的誤調(diào)用 // 2 is not a function
Array.from、Promise.all接口及集合類(lèi)構(gòu)造器的參數(shù),可以放入支持迭代器的內(nèi)容,而不局限于數(shù)組(node 0.12+兼容)。這里其實(shí)嘗試去調(diào)用了參數(shù)的迭代器Symbol.iterator。
5.2# 新集合類(lèi)容器的構(gòu)造器Array(); // [] Set(); // Uncaught TypeError: Constructor Set requires "new"
集合類(lèi)容器Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Set不可以通過(guò)非new方式來(lái)構(gòu)造。
5.3# Tagged Templatevar x = 30 `abcdefg` // Uncaught TypeError: 30 is not a function
模版語(yǔ)法可能是ES6最為顯然的語(yǔ)法,但它的擴(kuò)展形式Tagged Template在極端場(chǎng)景可能造成一個(gè)奇怪的報(bào)錯(cuò),算是對(duì)不寫(xiě)分號(hào)黨造成的又一個(gè)暴擊*。
6# 欺負(fù)新來(lái)的本篇章集結(jié)一些被濫用的特性。
6.1解構(gòu)特性很棒,它可以在promise這樣的單參數(shù)鏈?zhǔn)秸{(diào)用場(chǎng)景或是正則匹配場(chǎng)景中大方光芒,更為經(jīng)典的是python風(fēng)格的[y, x] = [x, y]。
但如果一個(gè)人鐵了心要瘋狂解構(gòu),新來(lái)維護(hù)這份代碼的人就要默默流下痛苦的眼淚了:
// 新人:是什么阻止了你用 a2 = [o1[a], o1[b], o1[c]] …… var o1 = { a: 1, b: 2, c: 3 }, a2 = []; ( { a: a2[0], b: a2[1], c: a2[2] } = o1 );
// 老人:看得爽嗎 var { a: { b: [ c, d ], e: { f } }, g } = obj;
// 主管:寫(xiě)到一半這個(gè)程序猿已經(jīng)被打死了 var x = 200, y = 300, z = 100; var o1 = { x: { y: 42 }, z: { y: z } }; ( { y: x = { y: y } } = o1 ); ( { z: y = { y: z } } = o1 ); ( { x: z = { y: x } } = o1 );
一個(gè)可以嘗試的保持代碼可讀性的方法,是盡量保證解構(gòu)的層次低。
6.2新對(duì)象字面量也很不錯(cuò),新的rest操作符也很實(shí)用,但是如果你們把它們混在一起……下面進(jìn)一段代碼賞析(*):
export const sharePostStatus = createReducer( {}, { [ PUBLICIZE_SHARE ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: { requesting: true, } } } ), [ PUBLICIZE_SHARE_SUCCESS ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: { requesting: false, success: true, } } } ), [ PUBLICIZE_SHARE_FAILURE ]: ( state, { siteId, postId, error } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: { requesting: false, success: false, error, } } } ), [ PUBLICIZE_SHARE_DISMISS ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: undefined } } ), } );
盡可能的保持代碼的可讀性,一行只用不超過(guò)2個(gè)ES6特性或許是一個(gè)可操作的方案。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/81332.html
摘要:用來(lái)轉(zhuǎn)換內(nèi)容,內(nèi)部調(diào)用了方法進(jìn)行轉(zhuǎn)換,這里簡(jiǎn)單介紹一下的原理將代碼解析成,對(duì)進(jìn)行轉(zhuǎn)譯,得到新的,新的通過(guò)轉(zhuǎn)換成,核心方法在中的方法,有興趣可以去了解一下。將函數(shù)傳入?yún)?shù)和歸并,得到。通常我們是用不上的,估計(jì)在某些中可能會(huì)使用到。 什么是Loader? 繼上兩篇文章webpack工作原理介紹(上篇、下篇),我們了解到Loader:模塊轉(zhuǎn)換器,也就是將模塊的內(nèi)容按照需求裝換成新內(nèi)容,而且每...
摘要:所以整個(gè)過(guò)程只涉及三個(gè)輸入狀態(tài),中間狀態(tài),輸出狀態(tài)關(guān)鍵是是如何生成,如何應(yīng)用修改,如何生成最終的。至此基本把上的模式解析完畢。結(jié)束實(shí)現(xiàn)還是相當(dāng)巧妙的,以后可以在狀態(tài)管理上使用一下。 開(kāi)始 在函數(shù)式編程中,Immutable這個(gè)特性是相當(dāng)重要的,但是在Javascript中很明顯是沒(méi)辦法從語(yǔ)言層面提供支持,但是還有其他庫(kù)(例如:Immutable.js)可以提供給開(kāi)發(fā)者用上這樣的特性,所...
摘要:所以整個(gè)過(guò)程只涉及三個(gè)輸入狀態(tài),中間狀態(tài),輸出狀態(tài)關(guān)鍵是是如何生成,如何應(yīng)用修改,如何生成最終的。至此基本把上的模式解析完畢。結(jié)束實(shí)現(xiàn)還是相當(dāng)巧妙的,以后可以在狀態(tài)管理上使用一下。 開(kāi)始 在函數(shù)式編程中,Immutable這個(gè)特性是相當(dāng)重要的,但是在Javascript中很明顯是沒(méi)辦法從語(yǔ)言層面提供支持,但是還有其他庫(kù)(例如:Immutable.js)可以提供給開(kāi)發(fā)者用上這樣的特性,所...
摘要:前言正則表達(dá)式時(shí)處理字符串中常用的手法,本文以簡(jiǎn)單的方式,快速展示了中正則相關(guān)的基礎(chǔ)知識(shí)點(diǎn)。文末還提供了幾個(gè)簡(jiǎn)單的正則相關(guān)面試題。接下來(lái)是正則部分,注意后面的并不匹配,也就是比如,實(shí)際匹配的值是和,在和后面加上,就完成了預(yù)期。 前言:正則表達(dá)式時(shí)處理字符串中常用的手法,本文以簡(jiǎn)單的方式,快速展示了JavaScript中正則相關(guān)的基礎(chǔ)知識(shí)點(diǎn)。文末還提供了幾個(gè)簡(jiǎn)單的正則相關(guān)面試題。個(gè)人總結(jié)...
閱讀 1130·2021-11-25 09:43
閱讀 1649·2021-09-13 10:25
閱讀 2614·2021-09-09 11:38
閱讀 3419·2021-09-07 10:14
閱讀 1730·2019-08-30 15:52
閱讀 653·2019-08-30 15:44
閱讀 3590·2019-08-29 13:23
閱讀 1989·2019-08-26 13:33