Bubblings Blog

常见javascript hack解释

一些javascript技巧被资深的程序员广泛使用。然而对于初学者来说,有的技巧的含义并不是那么的显而易见。这些技巧往往使用的并不是语言的直接含义,而是利用一些特性或者副作用来达到目的。下面我会对一些常用的技巧做出解释。

你应该明白,这些技巧中的很多都属于hack,不应该在日常开发中使用。这篇文章的目的是解释这些hack是如何工作的,并不是推荐去使用它们。

使用!!将值转换为布尔型

在javascript中,所有的东西都能被转换成“真”或“假”。也就是说,当你把一个对象放进if语句的条件中,执行时,要么走条件为真的分支,要么就走条件为假的分支。

0, false, "", null, undefined, NaN为假,其他对象都为真。有时候想将一个对象转换成布尔值,你可以使用!!

另外,如果不是if (x == "test")这种形式,你可以简单的写成if (x),当x为false时,程序将直接运行其它块。

使用+str将字符串转换成数字

在javascript中,当+作为一元运算符使用时会返回一个数值或NaN。有时当你想知道一个变量是否是数值(number)时,你可以这样来写代码x === +x(见underscorejs源码)。

这种方法意图并不明显,通常,你应该使用parseFloat或者parseInt(x, 10)来将一个变量转换成数值型(number)。

使用||来提供默认值

在javascript中,||是一个短路求值的例子,也常用在其他语言当中。||操作符会首页判断左边表达式的值,如果为false,则继续判断右边。在任何情况下它都会返回第一个值不为false的结果,参考下面例子:

function setAge(age) {
    this.age = age || 10;
}

setAge();

我们没有传入age的值,因此age || 10会返回10,这是给变量设置默认值的非常好的一个方式。事实上这段代码等价于:

var x;
if (age) {
   this.age = age;
} else {
   this.age = 10;
}

很明显,前者代码更简洁,这就是为什么到处都在使用它的原因。

就我自己而言,我经常使用这种方式,因为足够的简洁明了。需要注意的是,这种方式你没法设置age0,因为0false,因此下面的方法应该是一个更好的方案(但是代码稍微有些冗长):

this.age = (typeof age !== "undefined") ? age : 10;

使用void 0来替代undefined

关键字void后跟着一个参数,并总会返回undefined。为什么不直接使用undefined?因为在一些浏览器中,undefined仅仅被视作一个变量,并且可以被重新赋值,而前者可以给我们更高的信赖。虽然你可以看到这种写法在很多代码或库中被使用,但是我并不建议经常使用它,因为所有遵循EC5的浏览器已经禁止重写undefined了。

封装模式(function() {...})()

当你想要封装代码时,你可以用一个匿名函数包裹代码,然后立刻执行这个匿名函数。在javascript中,有两种作用域:全局作用域和局部(函数)作用域。你写的代码包括变量和函数声明,不论在任何位置,都会进入全局作用域。通常,你可以将代码封装在匿名函数的内部,而只向全局作用域暴露调用接口的方法名。这个模式使用起来也非常方便,请看以下代码:

(function() {
    function div(a, b) {
        return a / b;
    }

    function divBy5(x) {
        return div(x, 5);
    }

    window.divBy5 = divBy5;
})()

div // => undefined
divBy5(10); // => 2

本文中列出的这些hack中,这条确实是无害的,你应该在代码中使用它以防止代码的内部逻辑暴露在全局作用域中。

总之,我想提醒的是,你写的任何代码都应该本着让其他人读起来简单易懂为原则。

一些文章中列出的问题,在ES6标准中已经通过优雅的方式进行了解决。例如,在未来你不需要使用age = age || 10这种模式了,ES6提供的一种更好的方式来设置默认值:

function(age = 10) {
   ...
}

另一个例子是(function() {...})()这种模式,当现代浏览器支持ES6 modules之后,你可以使用模块化来代替。

译自:

JavaScript hacks explained