給初學者:JavaScript 的常見注意點

上篇說了一些 JS 中數組操做的常見誤區,此次來總結一下初學者常見的其餘易錯點。html

寫當即執行函數時前置 void

當即執行函數(IIFE)在 JS 很是經常使用,做用就是構造一個函數級的變量做用域。常見的寫法以下:數據庫

(function () {
  // code
})();

這樣寫可能會被 JS 理解成爲一個函數調用segmentfault

var a = 1
(function () { // Uncaught TypeError: 1 is not a function
})()

從今天改變習慣,這樣寫:後端

void function () {
  // code
}();

有些人喜歡以 ! 打頭,我的習慣問題。數組

standardjs 規範日益流行的今天,忽略行尾分號成爲了主流(可是筆者不喜歡),更要改變這個習慣瀏覽器

注:standardjs 自己禁止行首括號(https://standardjs.com/readme...安全

檢查一個變量是否爲對象以前,首先判斷其值是否爲 null

雖然不肯認可,JS 標準說:函數

typeof null === 'object' // true

毋庸置疑的,null 不具有做爲對象類型的基本特徵,是原始類型。這是一個廣爲人知的 JS 的 bug,,它從 JS 誕生開始就存在,從未、並且永遠不會被修復spa

咱們沒必要去探究它的黑歷史,可是咱們寫代碼時判斷一個變量的類型時,首先須要判斷它是否爲 null調試

if (someVal !== null && typeof someVal === 'object') {
  // someVal 是一個對象
}

作數值計算時,注意 JS 數值類型的精度

在 JS 裏,全部的 number 原始值都是一個雙精度浮點數,對應 Java 的 double 類型,對應標準 IEEE754。當心它的精度問題。

作整數處理時,注意數值的大小

JS 最大可存儲的安全整數(不存在精度問題)爲 9007199254740991 (16位,Number.MAX_SAFE_INTEGER ),注意比 Java 的 long 類型最大整數 9223372036854775807 (19位) 小几個數量級,因此有時 JS 的 number 類型是不能精確存儲 Java 的整數的(固然一般狀況下不是問題)。

問題一般出在先後端數據傳輸上。數據庫中的主鍵一般是一個自增加的長整型數,有可能會超出 JS 的安全整數範圍,這時請考慮使用字符串傳輸。

作小數計算時,注意浮點數的精度問題

例如:0.1+0.2 => 0.30000000000000004,0.4-0.3 => 0.10000000000000003

將小數轉化爲字符串時,永遠記得使用 toFixed 取小數點後若干位數字:

(0.1 + 0.2).toFixed(2) === '0.30'

比較小數相等時,切記不要直接使用 ===,而要使用相減取絕對值的方式(表示兩數相差在必定範圍內即認爲他們相等)。

0.1+0.2 === 0.3 // false
Math.abs(0.1+0.2 - 0.3) <= 1e-10 // true

NaN !== NaN

NaN 之因此 NB,由於它有一個獨一無二的特性。對!獨一無二!那就是:

NaN === NaN // false
var a = NaN; a === a // false

NaN 不等於它本身。你可使用這個特性判斷一個變量是否爲 NaN,一個變量若是不等於它本身,這個變量必定是 NaN

還有一個方式是使用 Number.isNaN。注意若是不已知這個變量的類型是數字時,不要使用 isNaN 作判斷,由於 isNaN 有個很詭異的特性:它會先將待判斷的變量轉換爲數值類型。

isNaN('abc') // true
isNaN('123') // false
isNaN('') // false
isNaN([]) // false
isNaN({}) // true

永遠不要寫 someVal === NaN

正確使用 parseInt

首先parseInt接受兩個參數,第一個參數爲待parse的字符串(若是不是字符串則會首先轉換爲字符串);第二個參數爲使用的進制數。

若是不傳第二個參數,則進制由第一個參數決定。什麼意思呢?好比以 0x 開頭的字符串,會被解析爲16進制數。

咱們知道以數字 0 開頭的數字爲8進制數(非嚴格模式),好比 011 === 9,0 自己也是8進制數。那麼問題來了, parseInt('011') = ?

答案是看瀏覽器。目前絕大多數瀏覽器都會做爲10進制數解析,結果爲11。可是還有一些老舊的瀏覽器以8進制數解析(例如IE8和一批老Android瀏覽器)

clipboard.png

因此若是你非要用 parseInt:

parseInt 使用規則一:請傳入第二個參數

回到 parseInt 自己的含義。顧名思義這個函數是在parse,被parse的必定是個字符串。若是第一個參數不是字符串,那麼會首先被轉換爲字符串。

問:parseInt(0.0000000008) =?

答:

  1. String(0.0000000008) => '8e-10'
  2. parseInt('8e-10') => 8

本身打開調試器去試

parseInt使用規則二:永遠不要使用parseInt給小數取整

建議對於數值轉換一律使用強制轉換函數 Number,若是你JS用6了可使用 +(正號)。
若是須要對某個數字取整,建議使用 Math.trunc。若是你能肯定數值在 32 位之內,可使用 x | 0~~x 等方式

clipboard.png

parseInt的用處在於轉換一些CSS裏帶單位的值:parseInt('10px', 10) => 10。但這裏建議使用parseFloat,能夠解析小數又沒有進制問題。

除了用於比較 null 或 undefined,永遠不要使用非嚴格相等 ==

毫不要簡單的把非嚴格相等 == 理解爲二者表示的數字同樣,它有一套很是複雜的轉換規則:它會先將 %% 轉換爲 @@,而後把 !! 轉換爲 **,若是 %%?? 類型,還會 xx 一把……

看不懂對吧,我相信你就算看懂了也記不住的。否則請問:

'true' == true // => false
'true' == false // => false
[] == {} // => false
[] == [] // => false

關於非嚴格相等,你只須要記住這個規則:

null == null // => true
undefined == undefined  // => true
null == undefined // => true
undefined == null // => true
x == null // => false (x 非 null 或 undefined)
x == undefined // => false (x 非 null 或 undefined)

簡言之:

x == null // 或 x == undefined

是最簡單的判斷 x 爲 null 或 undefined 的方式,相對應的

x != null // 或 x != undefined

是最簡單的判斷 x 非 null 和 undefined 的方式。這就是 == 存在的惟一意義。

日期處理

new Date(year, month, day) 注意其參數的數值範圍

因爲可能的歷史傳承緣由,JS 內置對象 Date 的構造函數比較特殊。

  1. 若是 year 是 0 ~ 99 之間,year 默認加 1900。好比 1 表明公元 1901 年,99 表明公元 1999 年,100 表明公元 100 年。(你問 -1 是幾?公元前 1 年。。。)
  2. month 從 0 開始算。0 表明一月,1 表明二月,以此類推。12 表明下一年的一月(自動進位)

第一點不知道也沒什麼,畢竟通常不會操做公元 99 年以前的時間。但第二點就很容易出錯,切記它是以 0 開始的數字。

這樣獲得的日期對象是本地時間(採用客戶端時區)

new Date(dateString) 注意瀏覽器時區問題以及瀏覽器兼容性

時常有後端接口返回一個日期字符串的狀況:

new Date('2018-01-01') // => "2018/1/1 08:00:00" 新版瀏覽器,IE 11
new Date('2018-01-01') // => "2018/1/1 00:00:00" 某些舊版安卓
new Date('2018-01-01') // => "Invalid Date" IE 8(這個忽略。。。)

能夠看到,瀏覽器基本都是把日期字符串當作 UTC 時間處理的。而

new Date('2018/01/01') // => "2018/1/1 00:00:00" 包括 IE 8 在內全部瀏覽器

因此對於日期字符串,請注意字符串中是使用橫槓仍是斜槓。對於橫槓能夠考慮將 - 替換成 /,或者補全完整的帶時區的 ISO8601 字符串。考慮到負數時區的問題,不推薦將小時數清零的作法。

PS:將日期對象取當天 0 點爲 date.setHours(0, 0, 0, 0)
PS2:取當前時間的 Unix 時間戳能夠 Date.now()

clipboard.png

補:慎用 || 填充默認值

這反而是 JS 老鳥更容易犯的錯誤。給用戶傳入的對象填充默認值是很常見的行爲,他們老是隨手就寫:

config.prop1 = config.prop1 || 233;
config.prop2 = config.prop2 || 'balabala';

expr1 || expr2意思是:若是expr1能轉換成true則返回expr1,不然返回expr2

expr1 || expr2 <=> Boolean(expr1) ? expr1 : expr2

哪些值不能轉換爲 true 呢?

  1. null
  2. undefined
  3. NaN
  4. 0 !!!
  5. 空字符串('') !!!

若是用戶指定了傳入參數的值爲 0 或者是空字符串的配置項,它的值就會被強制替換爲默認值,然而實際上只有 undefined 應該被認爲是用戶沒有指定其值(語義上能夠這樣理解:null 表示 用戶讓你給他把這個位置空着;而 undefined 表示 用戶沒發表意見

因此就應該是這樣:

config.prop1 = config.prop1 !== undefined ? config.prop1 : 233;
config.prop2 = config.prop2 !== undefined ? config.prop2 : 'balabala';

很長。。。你能夠搞個全局的函數簡化這一操做,或者考慮使用 lodash 的 defaults 方法

歡迎補充

相關文章
相關標籤/搜索