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