javascript编程容易出现的11个错误

javascript是比较容易学的。但是,对于这门语言需要有一些值得注意的地方。本文将指出javascript编程中可能犯过的10个错误

错误1-使用全局变量

如果你刚开始javascript编程,可能会觉得全局变量很好用。事实上,刚开始javascript编程,你可能不知道使用全局变量会带来什么麻烦。在同一个页面中,全局变量可以在任何内嵌的javascript代码段中或是该页面加载的不同的js文件中,都能访问到。这听起来很强大,是吗?这就使得全局变量可以随时随地的被修改赋值。
事实上这样很糟!
这样做会导致变量在意料之外被修改重写。假设有一个网店,需要用javascript计算并显示购物车所有商品的价格总和(当然,服务器端还会进行重新计算,这里只是为了增强用户的体验)。可能会编写代码如下:

  1. var total = 0// total price
  2. tax = 0.05// 5%

现在,还需要用javascript代码在网站上展示一些信息,或则是做一个商品仓库。代码如下:

  1. var total = 15// number of tweets pulled from twitter

或则是如下代码:

  1. var tax = function () { /* ... */ }// Trigger Animation eXperience function

现在,出现问题了:两个重要的变量被重写,但可能还没被意识到。这样代码运行会出错,会花费很多时间来跟踪和修复该错误。
那该如何解决呢?简言之—“封装”:当然封装有很多方法可以实现。第一种做法是将代码放入一个匿名的自调函数中。代码如下:

  1. (function () {
  2.  var total = 0tax = 0.05;
  3.  
  4.  // other code
  5.  }());

这样做,在函数外部是绝对不能访问到函数内部定义的变量。这就形成了个人的代码空间,但是这样就不能公开部分方法或属性。例如,想要创建一个购物车,定义一个总价的变量,作为公共属性,这种情形下可以采用模块式编程。

  1. var cartTotaler = (function () {
  2.  var total = 0tax = 0.05;
  3.  
  4.  // other code
  5.  
  6.  return {
  7.  addItem : function (item) { },
  8.  removeItem : function (item) { },
  9.  calculateTitle : function () { }
  10.  };
  11.  }());

关于全局变量有一点值得注意,如果不用关键词var来声明创建变量,那么javascript引擎会默认将该变量定义为全局变量。

  1. (function () {
  2.  tax = 0.05;
  3.  }());
  4.  
  5.  var totalPrice = 100 + (100 * tax)// 105

这里的变量tax在函数外部也是可以被访问的,因为在定义tax的时候没有使用var关键词。

错误2-不加分号

每句javascript语句必须以分号结尾。在编程时如果忘了加分号,这时javascript编解析器会自动加上。那我们在编程的时候是不是就可以完全不用浪费时间去加分号呢?
但是在一些语句中,分号是必不可少的。如for循环语句中的分号是必不可少的,否则会报语法错误。那么语句末尾的分号呢?
Javascript社区已经讨论过该问题。下面是本文的看法:你的代码,只要是被javascript解析器修改过(即便是很小的修改,如添加分号),就可能会出现一些你意料之外的结果。看看下面这段javascript代码:

  1. function returnPerson (name) {
  2.  return
  3.  {
  4.  name : name
  5.  };
  6.  }

该方法看起来会返回一个对象,但事实上,javascript解析器会在return后面紧接着添加一个分号,这就导致该函数返回undefined。Return后的对象会被忽略。解决方法很简单,代码如下:

  1. return {
  2.  name : name
  3.  };

在javascript编程中应严格要求自己添加分号,这个习惯并不难。当然,作为一名web开发人员,你可能也会用到其他的语言(如php),这些语言都是严格要求以分号结尾的。你可以把这些编程语言的习惯带到javascript编程中来。
作者注解:除了你完全肯定可以被忽略的地方,你都不可以忽略添加分号。

错误3-使用==

如果你问一个javascript编程者,在javascript编程中通常会犯什么错误。他/她很可能会说,使用= = =来代替= =。这是什么意思呢?
试试下面的代码:

  1. if (1 == 1) {
  2.  console.log("it's true!");
  3.  }

代码如你所愿的输出了“it’s true!”那再试试下面的代码:

  1. if (1 == '1') {
  2.  console.log("it's true!");
  3.  }

这段代码在控制台中尽然也输出了“it’s true!”,但其实这并不是你所期望的输出。这里的==运算符转换了运算数的类型,从而使得两个运算数相等了。这里if语句中的==运算符使得右边string类型的“1”变成了number型的1。
想要得到你想要的输出,这里应该用= = =运算符来代替= =。===不会强制转换运算数的类型,这样才能如你所期望的。同样地,用!= =运算符来替换!=。下面是用==来做比较,得出的结果令人意外。

  1. '' == '0' // false
  2.  '0' == '' // true
  3.  false == '0' // true
  4.  ' \t\r\n ' == 0 // true

错误4-使用数据类型的包装对象

Javascript提供了各个数据类型的包装对象。

  1. new Number(10);
  2.  new String("hello");
  3.  new Boolean(true);
  4.  new Object();
  5.  new Array("one""two""three");

首先,它们并不好用。上面的代码可以用更少的代码来实现,如下:

  1. 10;
  2.  "hello";
  3.  true;
  4.  {};
  5.  ["one""two""three"];

但是这两种方式还是有所不同的。下面是douglas crockford的观点:
例如用new Boolean(false)创建一个对象,该对象有一个方法valueOf,调用该方法会返回构造器的值。
这意味着,如果运行typeof new Number(10)或者是typeof new String(‘hello’),将返回‘object’,而不是’number’或’string’.另外,用数据类型的包装还会引发一些意料之外的结果。
那么为什么javascript要提供数据类型的包装对象呢?这是因为javascript解析器内部会调用。简单的数据类型是没有方法的(因为它们不是对象),所以当调用简单类型的数据的方法时(如’hello’.replace(‘ello’, ‘i’)),javascript会调用String包装对象来创建一个临时的string对象,然后调用该对象的方法,完成调用后会删除这个临时对象。
所以不要用数据类型的包装对象来创建简单类型的数据。
注意:本来不用这么说明的,但本文还是想明确的告诉初学者:不是说不使用它们和new(尽管有些是推荐使用的),这里需要特别指出的是,这个建议特别针对这些数据类型,如:number、string、Boolean、array和空对象。

错误5-在使用for-in时不对属性检查

我们都很熟悉遍历数组,但是你可能还希望能遍历对象的属性。(题外话:array事实上是属性名为数字的对象)。这是可以用for-in循环语句,代码如下:

  1. var propobj = { name"Joe"job"Coder"age25 };
  2.  
  3.  for (var prop in obj) {
  4.  console.log(prop + "" + obj[prop]);
  5.  }

运行上面的代码,输出如下:

  1. nameJoe
  2.  jobCoder
  3.  age25

但是,浏览器中for-in遍历对象属性和方法时会包括对象原型链上的所有属性和方法。但绝大多数属性是不希望被枚举出来的。可以用hasOwnProperties方法来检测属性是否属于对象。代码如下:

  1. Function Dog (name) {
  2.  this.name = name;
  3.  }
  4.  Dog.prototype.legs = 4;
  5.  Dog.prototype.speak = function () {
  6.  return "woof!";
  7.  };
  8.  
  9.  var d = new Dog("Bowser");
  10.  
  11.  for (var prop in d) {
  12.  console.log( prop + "" + d[prop] );
  13.  }
  14.  
  15.  console.log("=====");
  16.  
  17.  for (var prop in d) {
  18.  if (d.hasOwnProperty(prop)) {
  19.  console.log( prop + "" + d[prop] );
  20.  }
  21.  }
  22.  
  23.  // Output
  24.  
  25.  // name: Bowser
  26.  // legs: 4
  27.  // speak: function () {
  28.  return "woof!";
  29.  // }
  30.  // =====
  31.  // name: Bowser

有时,只希望枚举列出对象的的属性,不包括方法。可以用typeof方法,代码如下:

  1. for (var prop in d) {
  2.  if (typeof d[prop] !== 'function') {
  3.  console.log( prop + "" + d[prop] );
  4.  }
  5.  }

不管怎么样,在用for-in循环时要确保对属性进行检测,以避免得到你意料之外的结果。

错误6-使用with或eval

幸运的是,现在大部分javascript教程都不会教你使用with或eval。但是一些老教程或名气不大的资料时(因为有时候好的资料在网上很难找到),可能会发现有使用with或eval。
下面是两个不用with的主要原因:
1、 它会降低代码性能
2、 不易于代码阅读

第一点是它与生俱来的。第二点,看看下面的代码,这里用with访问person对象的name、age属性。

  1. var person = { name"Joe"age : 10 };
  2.  
  3.  with (person) {
  4.  console.log(name)// Joe
  5.  console.log(age)// 10
  6.  }

但是,若果有一个变量和对象其中一个属性同名,那用with会发生什么呢?事实上,这种情况下,访问变量会引用那个变量而不是对象的属性。另一个值得注意的是,在with语句中,如果访问的属性不存在或对象不存在,就不能给对象添加属性,同时会使得作用域链上with作用域后的那个作用域中创建一个变量。

  1. var person = { name"Joe"age : 10 },
  2.  name = "Billy";
  3.  
  4.  with (person) {
  5.  console.log(name)// Billy
  6.  job = "Designer";
  7.  }
  8.  
  9.  console.log(person.job)// undefined;
  10.  console.log(job)// Designer

那eval呢?它可以接受一个字符串参数,并且解析执行改字符串。

这听起来没有什么不好,甚至觉得很棒,对吗?但问题就是这太棒了!与其将一连串字符串将给它来解析执行,为什么不直接编写在程序中呢?不该这么做的原因如下:

  1. 完全可以直接编写在代码中。
  2. eval解析很慢的,性能跟with差不多。

eval的用法是在非运行时运行环境。可以从服务器端或客户端获取代码。难道真的想你的网站用户来底控制你的代码?这样不就意味着你的网站向无数的黑客敞开了大门。用eval就好比,离开了家,并告诉大家钥匙就是门口垫子下面。如果你爱自己或你的用户,就不要用eval。

错误7-在用parseInt时不用基数

Javascript提供了一个非常有用的方法parseInt,它可以将字符串转换为数值。

  1. parseInt("200")// 200
  2.  parseInt("043")// 35

结果是不是令人觉得意外?第二句为什么不是43?事实上,parseInt方法不仅仅是只能把字符串当做十进制数来转换。当parseInt的第一个参数是以0开头,它就会把字符串当作是八进制数来转换。这就是不使用基数出现的意料之外结果。第二个参数–基数,会指定parseInt方法把字符串当做什么进制的数来转换。(当然,它的返回值永远是十进制数)

  1. parseInt("020"10)// 20
  2.  parseInt("100"2)// 4

错误8 if和while语句不使用{}

Javascript最明显的特点是语法要求不那么严格。但正是这样的特点,有时会带来麻烦。If和while语句的{}就会引起一些麻烦。{}是根据if条件成立时执行代码语句的条数来用的。

  1. if (true)
  2.  console.log("inside the if statement");

这里看起来没什么问题,因为这里的执行语句只有一句

  1. var arr = ["one""two""three""four""five""six""seven""eight""nine""ten"],
  2.  i = arr.length - i;
  3.  
  4.  while (i) console.log( arr[i--] );

但是这样做不易于阅读:首先,不用{}代码结构看起来不是那么清晰。

  1. if (true)
  2.  console.log("inside the if-statement.");
  3.  console.log("outside the if-statement.");

看看上面的代码,第二行console语句是不属于if执行语句的,但是这里它看起来像是if的执行语句。使用{}会使结构更清晰。同时,如果你想在if的执行代码中添加一句,也需要使用{}。习惯使用{}并不是一件难事。

错误9-单个单个地插入dom元素

这并不是javascript自身的问题。99%100的javascript编程都会涉及DOM操作,在对DOM操作上会犯很多错误,但这是最明显的一个。
DOM操作会使浏览器重绘页面,所以如果有一连串的元素一个接一个的插入页面中,这会急剧增加浏览器渲染页面的负担。

  1. var list = document.getElementById("list"),
  2.  items = ["one""two""three""four"],
  3.  el;
  4.  
  5.  for (var i = 0items[i]i++) {
  6.  el = document.createElement("li");
  7.  el.appendChild( document.createTextNode(items[i]) );
  8.  list.appendChild(el)// slow, bad idea
  9.  }

Document fragments 是一个DOM元素容器,可以使用它同时添加这些元素到页面中。Document fragment自身不是一个DOM节点,它不会在页面DOM树中显示,并且在把它插入DOM之前它是不可见的。下面是它的用法:

  1. var list = document.getElementById("list"),
  2.  frag = document.createDocumentFragment(),
  3.  items = ["one""two""three""four"],
  4.  el;
  5.  
  6.  for (var i = 0items[i]i++) {
  7.  el = document.createElement("li");
  8.  el.appendChild( document.createTextNode(items[i]) );
  9.  frag.appendChild(el)// better!
  10.  }
  11.  
  12.  list.appendChild(frag);

非常快速、简洁!

错误10-不懂javascript

许多人不花时间来认真地学习javascript。
Javascript并不等于jquery。这是否吓到你了?如果你会犯以上列出的错误,那么你需要认真地学习javascript。Javascript是一门语言,一门基本上不用学习就可以使用的语言,这就导致许多人不花时间来认真学习。千万不要这么做,已经有太多太多的教程指出这样做的弊端,你没有借口不认真学习javascript。如果你只是了解jquery(或mootools,或别的),那么你学习了解javascript的出发点就已经错了。

错误11-严格遵循以上的规则

“Rules are made to be broken.”(规则是用来被打破的。)

虽然本文列举了以上规则,但像任何事一样,规则是用来被打破的。如果是刚开始学习javascript,你会严于律己,严格遵循以上规则。但是到了真正理解了为什么要遵循以上规则的原因后,你才会知道灵活运用以上规则。例如,eval被反复的说到不能用,但是它却是唯一能解析服务器端返回json字符串的方法。当然这里在运用它时会做很多安全的检测(你可能会用到一个javascript库)。这里想要指明的是,在需要的地方,不应该害怕犯错,大胆的运用它。当然,永远不要犯错误10所指出的问题。

结论:

如果你是javascript新手,希望以上的内容对你javascript编程有所帮助。如果你是一个资深javascript工程师,如过这里有遗漏的,请在留言板中留言告知大家。

本文翻译自《The 11 JavaScript Mistakes you’re Making》

posted @ 2011-10-12 11:40  蝌蚪归来  阅读(373)  评论(0编辑  收藏  举报