自ES6发行以来,许多新的,便捷的原生方法已添加到JavaScript的新标准中。
然而,我在GitHub的代码库中看到了很多旧的代码。这并不意味着它们一定是坏的,但我要介绍的这些功能将帮助你的代码更可读,更漂亮。
Number.isNaN vs. isNaN
NaN 是数字类型。
- typeof NaN === 'number' // true
因此,你无法区分 NaN 和数字。
甚至 Object.prototype.toString.call 都为 NaN 和数字返回 [object Number]。你可能已经知道可以使用 isNaN 方法检查参数是否为NaN。但是从ES6开始,number构造函数已开始将 isNaN 作为其方法。那有什么不同?
- isNaN——检查传递的值是不是数字还是不能转换为数字。
- Number.isNaN——检查传递的值是否不是数字。
下面是这个例子。
- Number.isNaN({});
- // <- false, {} is not NaN
- Number.isNaN('ponyfoo')
- // <- false, 'ponyfoo' is not NaN
- Number.isNaN(NaN)
- // <- true, NaN is NaN
- Number.isNaN('pony'/'foo')
- // <- true, 'pony'/'foo' is NaN, NaN is NaN
- isNaN({});
- // <- true, {} is not a number
- isNaN('ponyfoo')
- // <- true, 'ponyfoo' is not a number
- isNaN(NaN)
- // <- true, NaN is not a number
- isNaN('pony'/'foo')
- // <- true, 'pony'/'foo' is NaN, NaN is not a number
Number.isFinite vs. isFinite
在JavaScript中,诸如1/0之类的计算不会产生错误。相反,它为你提供了 Infinit(无限),这是一个全局属性。
那么,如何检查一个值是否是Infinit呢?你不能,但是你可以用 isFinite 和 Number.isFinite 来检查一个值是否是有限值。
它们的工作原理基本相同,但彼此之间略有不同。
- isFinite——检查传递的值是否有限。如果传递的值的类型不是数字,则将其转换为数字。
- Number.isFinite——检查传递的值是否有限。传递的值不会转换为数字。
示例:
- Number.isFinite(Infinity) // false
- isFinite(Infinity) // false
- Number.isFinite(NaN) // false
- isFinite(NaN) // false
- Number.isFinite(2e64) // true
- isFinite(2e64) // true
- Number.isFinite(undefined) // false
- isFinite(undefined) // false
- Number.isFinite(null) // false
- isFinite(null) // true
- Number.isFinite('0') // false
- isFinite('0') // true
Math.floor vs. Math.trunc
在过去,当你需要把数字取整时你可能会用 Math.floor。但是从现在开始,如果你真正想要的只是整数部分,请尝试使用 Math.trunc。
- Math.floor——返回小于或等于给定数字的最大整数。
- Math.trunc——截断点和右边的数字。
基本上,如果给定数为正,它们将为你返回完全相同的结果。但是,如果给定数为负数,结果将有所不同。
- Math.floor(1.23) // 1
- Math.trunc(1.23) // 1
- Math.floor(-5.3) // -6
- Math.trunc(-5.3) // -5
- Math.floor(-0.1) // -1
- Math.trunc(-0.1) // -0
Array.prototype.indexOf vs. Array.prototype.includes
当你在给定数组中寻找某个值时,如何找到它?我已经看到很多开发人员都使用 Array.prototype.indexOf,如以下示例所示。
- const arr = [1, 2, 3, 4];
- if (arr.indexOf(1) > -1) {
- ...
- }
他们的区别如下:
- Array.prototype.indexOf——返回可以在数组中找到给定元素的第一个索引;如果不存在,则返回 -1
- Array.prototype.includes——检查给定数组是否包含你要查找的特定值,并返回 true / false 作为结果
示例:
- const students = ['Hong', 'James', 'Mark', 'James'];
- students.indexOf('Mark') // 1
- students.includes('James') // true
- students.indexOf('Sam') // -1
- students.includes('Sam') // false
小心!由于Unicode差异,传递的值区分大小写。
String.prototype.repeat vs. 手动循环
在添加这个功能之前,你制作字符串的方式是复制字符串,比如 abcabcabc,然后将其串联成一个空的字符串,无论你想要多少次,都可以将其串联起来。
- var str = 'abc';
- var res = '';
- var copyTimes = 3;
- for (var i = 0; i < copyTimes; i += 1) {
- for (var j = 0; j < str.length; j += 1) {
- res += str[j];
- }
- }
但这太长了,非常混乱,有时很难阅读。为此,可以使用 String.prototype.repeat。你要做的就是传递数字,该数字表示你要复制字符串的次数。
- 'abc'.repeat(3) // "abcabcabc"
- 'hi '.repeat(2) // "hi hi "
- 'empty'.repeat(0) // ""
- 'empty'.repeat(null) // ""
- 'empty'.repeat(undefined) // ""
- 'empty'.repeat(NaN) // ""
- 'error'.repeat(-1) // RangeError
- 'error'.repeat(Infinity) // RangeError
传入的值不能为负数,而且必须小于无穷大,不能是溢出的最大字符串大小。
String.prototype.match vs. String.prototype.includes
要检查字符串中是否包含某些单词,有两种方法:match 和 includes。
- String.prototype.match——采用RegExp类型的参数,可以使用RegExp中所有支持的标志。
- String.prototype.includes——采用两个参数,searchString 作为第一个参数,而 position 作为第二个参数。如果 position 没有通过,将使用默认值 0。
区别在于,includes是区分大小写的,而 match 则不区分大小写。你可以将 i 标志放在RegExp中,以使其不区分大小写。
- const name = 'jane';
- const nameReg = /jane/i;
- const str = 'Jane is a student';
- str.includes(name) // false
- str.match(nameReg)
- // ["Jane", index: 0, input: "Jane is a student", groups: undefined]
String.prototype.concat vs. String.prototype.padStart
当你想在某些字符串的开头附加一些字符串时,padStart 是一种功能强大的方法。
另外,concat 也可以很好地执行这个功能。但主要的区别在于 padStart 重复了从结果字符串的第一个索引到当前字符串的第一个索引的字符串。
下面演示如何使用这个功能:
- const rep = 'abc';
- const str = 'xyz';
这是两个字符串。我想做的是在 xyz 前面添加 rep,但不仅是一次,我希望重复几次。padStart 需要两个参数——新创建的结果字符串的总长度和将被重复的字符串。
- str.padStart(10, rep); // "abcabcaxyz"
这个功能很好用,这个功能用 concat 肯定是很难做到的,concat 也是执行字符串附加的。
padEnd 从位置的结尾开始。
结论
在JavaScript中,有很多有趣而有用的方法并不普遍。但这并不意味着它们没有用处。这一切都取决于你如何在各种情况下使用它们。