JavaScript 中的一些最佳实践

前言

这篇文章会把一些在平时开发中遇到的关于 JavaScript 中的一些最佳实践整理出来,有常见的,也有不常见的,不过这只是相对而言,因为每个人的能力以及经历不尽相同,有的人觉得这不是一个什么罕见的问题,我平时也遇到不少,而有人的却会觉得这个我从来都没见过。但这都不要紧,我们一步一个脚,终将会成为那个他。

JavaScript 中的一些最佳实践

下面我们就开始,由于这是整理,可能会让你觉得比较零散,但这不是你要关注的,你所需要花时间的地方是把里面对自己有用的实践吸收消化掉。

1、判断一个变量是否为 null

const n = null;
console.log(!n && typeof n === "object"); // true

 null 为假值,并且它是 typeof 后唯一一个返回 "object" 的基本类型。

2、类数组转换成数组

func();
function func() {
    let arr = Array.prototype.slice(arguments); // 转换成数组
    arr.push("我是数组,可以用 push() 方法啦");
    console.log(arr);
}

函数里的 arguments 是为常见,除了这个 arguments 是数组之外,一些 DOM 操作返回来的元素列表也是类数组。 

这里有一点需要注意,如果是要操作函数中的 arguments ,我们有两种方法来让它使用数组方法:

先通过 Array.prototype.slice() 把它转化为一个新数组,然后就可以像操作数组一样操作它了;如果你不想把它转化为数组,你可以通过形如 Array.prototype.slice.call(arguments) 的方法来让 arguments 间接地使用数组方法。

对于 DOM 类数组我们就只有一种方法,先把它转化成一个新的数组,然后就可以直接使用数组方法操作它了。

而到了 ES6 ,我们可以使用 Array.form() 方法把类数组转换成真正的数组

let arr = Array.from(arrayLike)

3、判断一个变量是否为 NaN

const n = NaN;
console.log(n !== NaN); // true

NaN 是唯一一个不等于自身的值,也就是说只要不等于自身那这个值就是 NaN,在程序中出现 NaN 值的情况常有发生。

我们不妨把它封装成一个函数,需要时直接调用:

function isRealNan(n){
    return n !== n;
}

ES6 之前提供的 isNaN() 方法不是很靠谱,比如像下面这样:

isNaN({}); // true
isNaN("abc"); // true
isNaN(undefined); // true

不过到了 ES6 ,isNaN() 挂到了 Number 对象上,并且 Number.isNaN 变得安全可靠

Number.isNaN({}); // false
Number.isNaN("abc"); // false
Number.isNaN(undefined); // false
Number.isNaN(NaN); // true

虽然被检测的都不是 NaN 但会被强制转换为 NaN,所以我们直接使用前端那种判断方法就可以了。

4、类型判断

const n = NaN;
Object.prototype.toString.call(n); // [object Number]

其它的变量类型判断也一样,只需要把变量值给 Object.prototype.toString.call() 方法就可以,它会给我们返回形如:[object xxx] 的字符串,通过它我们就可以很轻松的知道变量的类型了。

5、动态定义正则

const n = "yunkus";
const m = "http:yunkus.com";
let reg = new RegExp(n, "g"); // 通过构造函数实现
console.log(n.match(reg));

6、默认值设置

function func(a) {
    a = a || Array.prototype;
    console.log(a);
}

这样设置默认值的一个好处就是无需创建多余的对象,但如果你使用这种方式作为默认值,那么你就得要非常地小心,谨记只有在不修改这个默认值的情况下才可以这样用。像这样可以作为默认值的还有 Function.prototype 和 RegExp.prototype 。

7、强制转换成 false 的值

undefined、null、false、""、+0、-0 和 NaN。如果这些值用于条件判断会自动转化成 false 。

8、JSON.stringify() 方法

使用这个方法是必需注意,如果待转换的对象中属性值为 undefined、函数、symbol 的都被忽略,即转换后的对象中不含有这些键值。对于 JSON.stringify() 方法来说还有一个可选参数,可以通过它来过滤掉  json 数据中的某些属性或者转换某些值的。

9、时间格式获取时间戳(数字类型)

var date = new Date(); // Sat Sep 01 2018 10:39:51 GMT+0800 (中国标准时间)
// 使用+来实现转换
var date = +new Date(); // 1535769591723
// 还可以直接使用 new Date();
var date = (new Date()).getTime(); // 1535769591724
// 不过我们还可以通过 ES5 中的 Date.now() 来直接获取 number 类型
var date = Date.now(); // 1535769591724

如果依次打印出来,那么你会发现通过 + 操作符会多消耗一些时间,比如本例,即使只是一毫秒,但有更好的实现(后面两种方法)为什么不用呢?

10、通过 indexOf() 方法判断是否存在

最好的方式,是像下面这样:

const str = "yunkus.com";
const index = str.indexOf("com");
if (~index) {
    console.log("找到了")
} else {
    console.log("没找到");
}

为什么它可以?答案是巧合,在数字类型中只有 0 和 NaN 表示假值,而 indexOf() 返回结果为找到则返回元素下标,找不到则返回 -1,说来也巧 ~ 恰好会把 -1 转为 0。所以上面的判断就顺理成章了。值得注意的是 ~ 遇上 NaN,会返回 -1。

ES6 中,我们可以使用 includes() 方法:

const str = 'abcdefg'
str.includes('ef')  // true

找到返回 true 找不到返回 false,不过需要注意的如果参数为空字符串,则总是返回 true 。

11、奇葩的 == 

console.log([] == 0);  // true
console.log([0] == 0);  // true
console.log(["1"] == 1);  // true
console.log(["\n"] == 0);  // true
console.log("" == 0);  // true

原因是左边的值最终会被 Number() 方法强制转换成对应的数字

12、复制数组

slice()方法,通过数组的 slice() 方法对数组进行复制,不过只是浅拷贝。

const arr = ["A","b","c"];
const arrCopy = arr.slice();

ES6 中的拓展运算符 ... 

const arr = ['A','B','C']
const arr2 = [...arr]
console.log(arr2) // ['A','B','C']

ES6 中的 Array.from() 方法

const arr3 = Array.from(arr)

13、一些继承的来的方法

这里所有的继承来的方法,比如:我们创建了一个对象,那么这个对象就会天生的继承 Object 对象上的一些方法.而有时候我们可能会遇到这样的问题:A 在创建这个对象时,给对象添加一个方法,而 Object 对象上正好也有一个同名的方法,那么此时问题就来了,当 B 想在 A 创建的对象上调用 Object 的方法,而实际上这些调用其实只会调用 A 给对象添加的那个方法.那如果 B 确实想调用 Object 上的同名方法那该怎么实现呢?我们可以通过 call 来实现.下面我们举个例子:

var hasOwn = Object.prototype.hasOwnProperty;
// 或者
var hasOwn = {}.hasOwnProperty;

然后我们可以你下面这样使用它

hasOwn.call(obj,"name");

14、判断是否全等

一般的我们使用 == 或者 === ,但这两个会带来一些问题,比如 == 会作类型转换,而 === 对于 NaN 与自身、-0 和 0 的判断却无能为力。在 ES6 的时代里,我们可以用 Object.is() 方法来完美地完成这些比较,所就是说以后你可以用 Object.is() 方法替代 == 和 === 了。并且对于 NaN 这种也处理得妥妥的。

Object.is(true, true)   // true
Object.is(1, 1)         // true
Object.is(0, -0)        // false
Object.is(NaN, NaN)     // true