避免隐式类型转换

在JavaScript对变量进行比较时会有一些隐式的数据类型转换。比如诸如false == 0"" == 0之类的比较都返回true

为了避免隐式类型转换对程序造成干扰,推荐使用===!==运算符,它们除了比较值还会比较类型:

  1. var zero = 0;
  2. if (zero === false) {
  3. // 不会执行,因为zero是0,不是false
  4. }
  5. // 反模式
  6. if (zero == false) {
  7. // 代码块会执行…
  8. }

有一种观点认为当==够用的时候就不必使用===。比如,当你知道typeof的返回值是一个字符串,就不必使用全等运算符。但JSLint却要求使用全等运算符,这无疑会提高代码风格的一致性,并减少了阅读代码时的思考量(“这里使用==是故意的还是无意的?”)。

避免使用eval()

当你想使用eval()的时候,不要忘了那句话“eval() is evil”(eval()是魔鬼)。这个函数的参数是一个字符串,它会将传入的字符串作为JavaScript代码执行。如果用来解决问题的代码是事先知道的(在运行之前),则没有理由使用eval()。如果需要在运行时动态生成并执行代码,那一般都会有更好的方式达到同样的目的,而非一定要使用eval()。例如,访问动态属性时可以使用方括号:

  1. // 反模式
  2. var property = "name";
  3. alert(eval("obj." + property));
  4. // 更好的方式
  5. var property = "name";
  6. alert(obj[property]);

eval()还有安全隐患,因为你有可能会运行一些被干扰过的代码(比如一段来自于网络的代码)。这是一种在处理Ajax请求所返回的JSON数据时比较常见的反模式。这种情况下最好使用浏览器的内置方法来解析JSON数据,以确保代码的安全性和数据的合法性。如果浏览器不支持JSON.parse(),你可以使用JSON.org所提供的库。

值得一提的是,多数情况下,给setInterval()setTimeout()Function()构造函数传入字符串的情形和eval()类似,这种用法也是应当避免的,因为这些情形中JavaScript最终还是会执行传入的字符串参数:

  1. // 反模式
  2. setTimeout("myFunc()", 1000);
  3. setTimeout("myFunc(1, 2, 3)", 1000);
  4. // 更好的方式
  5. setTimeout(myFunc, 1000);
  6. setTimeout(function () {
  7. myFunc(1, 2, 3);
  8. }, 1000);

new Function()的用法和eval()非常类似,应当特别注意。这种构造函数的方式很强大,但经常会被误用。如果你不得不使用eval(),你可以尝试用new Function()来代替。这有一个潜在的好处,在new Function()中运行的代码会在一个局部函数作用域内执行,因此源码中所有用var定义的变量不会自动变成全局变量。还有一种方法可以避免eval()中定义的变量被转换为全局变量,即是将eval()包装在一个即时函数内(详细内容请参见第四章)。

看一下这个例子,这里只有un成为全局变量污染了全局命名空间:

  1. console.log(typeof un);// "undefined"
  2. console.log(typeof deux); // "undefined"
  3. console.log(typeof trois); // "undefined"
  4. var jsstring = "var un = 1; console.log(un);";
  5. eval(jsstring); // 打印出 "1"
  6. jsstring = "var deux = 2; console.log(deux);";
  7. new Function(jsstring)(); // 打印出 "2"
  8. jsstring = "var trois = 3; console.log(trois);";
  9. (function () {
  10. eval(jsstring);
  11. }()); // 打印出 "3"
  12. console.log(typeof un); // "number"
  13. console.log(typeof deux); // "undefined"
  14. console.log(typeof trois); // "undefined"

eval()Function()构造函数还有一个区别,就是eval()可以修改作用域链,而Function更像是一个沙箱。不管在什么地方执行Function(),它都只能看到全局作用域。因此它不会太严重的污染局部变量。在下面的示例代码中,eval()可以访问并修改其作用域之外的变量,而Function()则不能(注意,使用Function()new Function()是完全一样的)。

  1. (function () {
  2. var local = 1;
  3. eval("local = 3; console.log(local)"); // 打印出 3
  4. console.log(local); // 打印出 3
  5. }());
  6. (function () {
  7. var local = 1;
  8. Function("console.log(typeof local);")(); // 打印出 undefined
  9. }());