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

标签: javascript 前端 | 发表时间:2011-09-21 14:13 | 作者:飞绿 ~Wing~
出处:http://www.36ria.com

javascript-11-mistake
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 = 0, tax = 0.05;
  3.  
  4.  // other code
  5.  }());

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

  1. var cartTotaler = (function () {
  2.  var total = 0; tax = 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 prop, obj = { name: "Joe", job: "Coder", age: 25 };
  2.  
  3.  for (var prop in obj) {
  4.  console.log(prop + ": " + obj[prop]);
  5.  }

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

  1. name: Joe
  2.  job: Coder
  3.  age: 25

但是,浏览器中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 = 0; items[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 = 0; items[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》

相关 [javascript 编程 错误] 推荐:

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

- ~Wing~ - ria之家--RIA三部曲:jquery、ext、flex
javascript是比较容易学的. 但是,对于这门语言需要有一些值得注意的地方. 本文将指出javascript编程中可能犯过的10个错误. 如果你刚开始javascript编程,可能会觉得全局变量很好用. 事实上,刚开始javascript编程,你可能不知道使用全局变量会带来什么麻烦. 在同一个页面中,全局变量可以在任何内嵌的javascript代码段中或是该页面加载的不同的js文件中,都能访问到.

javascript 编程规范

- 红茶 - 博客园-Ruby's Louvre
为公司起草的javascript编程规范,参考了网上的许多资料,尤其是google的规范. 现在放出来,希望能抛砖引玉,大家多提宝贵意见. 本规范是针对javascript函数式编程风格与公司严重依赖于jQuery进行编码的现实制定出来. 禁止使用eval,with与caller(ecma262 v5 的use strict要求).

Javascript编程风格

- - 阮一峰的网络日志
Douglas Crockford是Javascript权威, Json格式就是他的发明. 去年11月他有一个演讲( Youtube),谈到了好的Javascript编程风格是什么. 我非常推荐这个演讲,它不仅有助于学习Javascript,而且能让你心情舒畅,因为Crockford讲得很幽默,时不时让听众会心一笑.

Javascript模块化编程(二):AMD规范

- - 阮一峰的网络日志
这个系列的 第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. 先想一想,为什么模块很重要. 因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块. 但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套.

Javascript异步编程的4种方法

- - 阮一峰的网络日志
你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务. 如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推. 这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.

[总结贴] 十个 JavaScript 中易犯的小错误

- - SegmentFault 最新的文章
在今天,JavaScript已经成为了网页编辑的核心. 尤其是过去的几年,互联网见证了在SPA开发、图形处理、交互等方面大量JS库的出现. 如果初次打交道,很多人会觉得js很简单. 确实,对于很多有经验的工程师,或者甚至是初学者而言,实现基本的js功能几乎毫无障碍. 但是JS的真实功能却比很多人想象的要更加多样、复杂.

创业编程七个错误认识

- microlj - cnBeta全文版
多少年来,人们普遍有一种看法,认为软件工程应该和其它种类的工程一样:仔细的设计,精确的规划,然后进行开发 ―― 严格按照设计说明书. 这种开发方式的问题在于:软件,它是“软”的. 任何需要的时候你都可以大幅度的修改你 的软件,人们也都是这么干的. 还有,因为软件可以被拿来对任何事物进行模型造型,你能要求软件开发人员去实现的可能的东西几乎是无穷无尽.

使用 node.js 进行服务器端 JavaScript 编程

- jiaosq - IBM developerWorks 中国 : 文档库
node.js 是一个可以使用 JavaScript 开发服务器端应用的平台. 它依托于 Google V8 JavaScript 引擎,并采用事件 I/O 的架构,可以用来创建高性能服务器. 本文详细介绍了 node.js 的基本知识、模块化的结构、事件驱动的机制以及常用的模块.

面向对象的 JavaScript 编程及其 Scope 处理

- zhibin - IBM developerWorks 中国 : Web development : Articles,Tutorials
在面向对象的 JavaScript 编程中,我们常常会将一些数据结构和操作封装成对象以达到继承和重用的目的. 然而层层封装和继承再加上 JavaScript 中特殊关键字 this 的使用,使得 JavaScript 当前运行 Context 看起来非常的混乱. 很多 developer 为了获取运行时正确的 Context,常常不得已将 function 或者 Object 声明在全局 Global Context 中.

浅谈JavaScript编程语言的编码规范

- 冷月 - 博客园新闻频道
  注:本文转载自 developerworks.   简介: JavaScript 编程语言作为最流行的客户端脚本语言,早已被众多 Web 开发人员所熟悉. 随着 Web2.0 时代的到来和 Ajax 技术的广泛应用,JavaScript 也逐渐吸引着更多的视线. 工作中要求越多的是对 JavaScript 语言的深入学习,灵活运用,和对编码质量的保证.