了解和熟悉这些陷阱,并在开发时规避它们,可以给我们省去很多麻烦。
作为二元运算符时,+
既是数学运算的加法,也是字符串的拼接。另外,它还可以作为一元符号,表示正数。
看看下面的代码:
// 1 console.log( 1 + 2 ); // 3 console.log( "3" + "4" ); // "34" // 2 console.log( 1 + "3" ); // "13" console.log( "3" + 1 ); // "31" // 3 console.log( 1 + null ); console.log( 1 + undefined ); console.log( 1 + NaN ); // 4 console.log( "3" + null ); console.log( "3" + undefined ); console.log( "3" + NaN ); // 5 console.log( 1 + {} ); console.log( 1 + [] ); // 6 console.log( "3" + {} ); console.log( "3" + [] );
在 JavaScript 中,是如何决定一段代码中的 +
是数学运算还是字符串拼接呢?答案请看下面这段逻辑:
a + b: pa = ToPrimitive(a) pb = ToPrimitive(b) if (pa is string || pb is string) return concat(ToString(pa), ToString(pb)) else return add(ToNumber(pa), ToNumber(pb))
收集 +
两端的操作数的原始值。
需要注意的是,JavaScript 的原始值类型包括 number, string, boolean, undefined, 而 null 也是一种特殊的原始值。另一方面,对于非原始值类型(即复合类型,也即 object )的变量,其原始值被认为是字符串。
按这个逻辑,之前的测试结果就容易理解了。当然,像上面那样使用加号是不被推荐的,为了避免混淆,利用上面的加号逻辑,我们通常可以这样使用加号:
// 确保数字相加 a = +b + (+c); // 确保变量 d 为字符串 d = "" + d;
使用 with
语句,可以将一个语句块的上下文绑定为一个指定对象。
with (document) { write("foo"); getElemntById("bar").innerHTML = "foobar"; alert("Hello world!"); } // 等同于以下代码 document.write("foo"); document.getElemntById("bar").innerHTML = "foobar"; window.alert("Hello world!");
但是咱们不推荐使用 with ,事实上,ECMAScript 5 中引入的严格模式也禁止使用 with :
在语句结束时,你不必手动输入分号,换行即可。
function foo() { var bar = "value" return bar } // `{}` 包围的语句块的最后一个语句的分号也可省略 function bar() { return "foo" }
开发者们每写一行代码,就可以少敲打一次键盘,这看起来很人性化。但过于依赖分号自动插入,会带来一些潜在问题。
function foo() { return { bar: 1 } } function bar() { var a, b, c, d, e a = b + c (d + e).toString() }
foo()
将返回什么?bar()
又将怎么运行?事实上,前者将返回 undefined
,而后者的后两行代码将被理解为 a = b + c(d + e).toString()
。
JavaScript 的分号自动插入的规则并不那么清晰可辨,老实地多敲几次键盘,可以避免那些让你摸不着头绪的bug在某一天突然出现。
看看下面这段代码,我们将得到什么结果?
var foo = 1; function bar() { // 这个条件成立吗? if (! foo) { var foo = 10; } alert(foo); } bar();
那么这段代码呢?
var a = 1; function b() { a = 10; return; function a() {} } b(); alert(a);
第1个例子,也许你会觉得是 “1″ ,因为 ! 1
为假,if
里的代码不会执行。而第2个例子,可能你认为应该是 “10″ 。
事实上,结果相反,我们将分别得到 “10″ 和 “1″ 。
在 JavaScript 中,变量、函数的声明会被提升到当前函数主体的顶部,而不管这个声明语句是否出现在了不可到达的地方。
上面的两段其实等同于:
var foo = 1; function bar() { var foo; if (! foo) { foo = 10; } alert(foo); } bar(); var a = 1; function b() { function a() {} a = 10; return; } b(); alert(a);
需要注意的是,只有变量或函数的声明被提升了,而赋值语句并没有。
eval
是 JavaScript 的动态特性之一,在运行时, eval 可以将给定的字符串当作代码语句执行:
<script> var func = <?php echo json_encode($user_send['func']); ?>; eval(func + "()"); function sayHello() {} function sayGoodbye() {} </script>
在代码中用一组字符串与变量拼出另一串代码来运行,这看起来吊爆了。
但请在使用 eval 之前考虑下它将带来的潜在问题: