03-1-JavaScript常用语法
删除数组尾部元素
一个简单方法就是改变数组的length值:
const arr = [11, 22, 33, 44, 55, 66];
// truncanting
arr.length = 3;
console.log(arr); //=> [11, 22, 33]
// clearing
arr.length = 0;
console.log(arr); //=> []
console.log(arr[2]); //=> undefined
格式化 JSON 代码
JSON.stringify除了可以将一个对象字符化,还可以格式化输出 JSON 对象
const obj = {
foo: { bar: [11, 22, 33, 44], baz: { bing: true, boom: 'Hello' } }
};
// The third parameter is the number of spaces used to
// beautify the JSON output.
JSON.stringify(obj, null, 4);
// =>"{
// => "foo": {
// => "bar": [
// => 11,
// => 22,
// => 33,
// => 44
// => ],
// => "baz": {
// => "bing": true,
// => "boom": "Hello"
// => }
// => }
// =>}"
从数组中移除重复元素
通过使用集合语法和 Spread 操作,可以很容易将重复的元素移除:
const removeDuplicateItems = arr => [...new Set(arr)];
removeDuplicateItems([42, 'foo', 42, 'foo', true, true]);
//=> [42, "foo", true]
平铺多维数组
使用 Spread 操作平铺嵌套多维数组:
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = [].concat(...arr); //=> [11, 22, 33, 44, 55, 66]
不过上面的方法仅适用于二维数组,但是通过递归,就可以平铺任意维度的嵌套数组了:
function flattenArray(arr) {
const flattened = [].concat(...arr);
return flattened.some(item => Array.isArray(item)) ?
flattenArray(flattened) : flattened;
}
const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
const flatArr = flattenArray(arr);
//=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
注意事项
做数值计算时,注意 JS 数值类型的精度
在 JS 里,所有的 number 原始值都是一个双精度浮点数,对应 Java 的 double 类型,对应标准 IEEE754。小心它的精度问题。
做整数处理时,注意数值的大小
JS 最大可存储的安全整数(不存在精度问题)为 9007199254740991 (16位,Number.MAXSAFEINTEGER ),注意比 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浏览器)
所以如果你非要用 parseInt:
parseInt
使用规则一:请传入第二个参数
回到 parseInt 本身的含义。顾名思义这个函数是在parse,被parse的一定是个字符串。如果第一个参数不是字符串,那么会首先被转换为字符串。
问: parseInt(0.0000000008)
=?
答:
-
String(0.0000000008) => '8e-10'
-
parseInt('8e-10') => 8
自己打开调试器去试
parseInt使用规则二:永远不要使用parseInt给小数取整
建议对于数值转换一概使用强制转换函数 Number,如果你JS用6了可以使用 +
(正号)。 如果需要对某个数字取整,建议使用 Math.trunc。如果你能确定数值在 32 位以内,可以使用 x|0
或 ~~x
等方式
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 的构造函数比较特殊。
-
如果
year
是 0 ~ 99 之间,year 默认加 1900。比如 1 代表公元 1901 年,99 代表公元 1999 年,100 代表公元 100 年。(你问 -1 是几?公元前 1 年。。。) -
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()
补:慎用 ||
填充默认值
这反而是 JS 老鸟更容易犯的错误。给用户传入的对象填充默认值是很常见的行为,他们总是随手就写:
-
config.prop1 = config.prop1 || 233;
-
config.prop2 = config.prop2 || 'balabala';
expr1||expr2
的意思是:如果expr1能转换成true则返回expr1,否则返回expr2
expr1||expr2<=>Boolean(expr1)?expr1:expr2
哪些值不能转换为 true 呢?
-
null
-
undefined
-
NaN
-
0 !!!
-
空字符串('') !!!
如果用户指定了传入参数的值为 0 或者是空字符串的配置项,它的值就会被强制替换为默认值,然而实际上只有 undefined
应该被认为是用户没有指定其值(语义上可以这样理解: null
表示 用户让你给他把这个位置空着
;而 undefined
表示 用户没发表意见
)
所以就应该是这样:
-
config.prop1 = config.prop1 !== undefined ? config.prop1 : 233;
-
config.prop2 = config.prop2 !== undefined ? config.prop2 : 'balabala';
很长。。。你可以搞个全局的函数简化这一操作,或者考虑使用 lodash 的 defaults 方法
调试技巧
用表格显示对象
有时, 有一组复杂的对象要查看。可以通过console.log
查看并滚动浏览,亦或者使用console.table
展开,更容易看到正在处理的内容!
var animals = [ { animal:
'Horse', name: 'Henry',
age: 43 }, { animal:
'Dog', name: 'Fred',
age: 13 }, { animal:
'Cat', name: 'Frodo',
age: 18 } ]; console.table(animals);