您的位置:首页 > Web前端 > JavaScript

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

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


错误1-使用全局变量

如果你刚开始javascript编程,可能会觉得全局变量很好用。事实上,刚开始javascript编程,你可能不知道使用全局变量会带来什么麻烦。在同一个页面中,全局变量可以在任何内嵌的javascript代码段中或是该页面加载的不同的js文件中,都能访问到。这听起来很强大,是吗?这就使得全局变量可以随时随地的被修改赋值。

事实上这样很糟!

这样做会导致变量在意料之外被修改重写。假设有一个网店,需要用javascript计算并显示购物车所有商品的价格总和(当然,服务器端还会进行重新计算,这里只是为了增强用户的体验)。可能会编写代码如下:

var total = 0, //
total price

tax = 0.05; // 5%

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

var total = 15; //
number of tweets pulled from twitter

或则是如下代码:

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

现在,出现问题了:两个重要的变量被重写,但可能还没被意识到。这样代码运行会出错,会花费很多时间来跟踪和修复该错误。

那该如何解决呢?简言之—“封装”:当然封装有很多方法可以实现。第一种做法是将代码放入一个匿名的自调函数中。代码如下:

(function () {

var total = 0, tax = 0.05;

// other code

}());

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

var cartTotaler = (function () {

var total = 0; tax = 0.05;

// other code

return {

addItem : function (item) { },

removeItem : function (item) { },

calculateTitle : function () { }

};

}());

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

(function () {

tax = 0.05;

}());

var totalPrice = 100 + (100 * tax); //
105

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


错误2-不加分号

每句javascript语句必须以分号结尾。在编程时如果忘了加分号,这时javascript编解析器会自动加上。那我们在编程的时候是不是就可以完全不用浪费时间去加分号呢?

但是在一些语句中,分号是必不可少的。如for循环语句中的分号是必不可少的,否则会报语法错误。那么语句末尾的分号呢?

Javascript社区已经讨论过该问题。下面是本文的看法:你的代码,只要是被javascript解析器修改过(即便是很小的修改,如添加分号),就可能会出现一些你意料之外的结果。看看下面这段javascript代码:

function returnPerson (name) {

return

{

name : name

};

}

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

return {

name : name

};

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

作者注解:除了你完全肯定可以被忽略的地方,你都不可以忽略添加分号。


错误3-使用==

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

试试下面的代码:

if (1 == 1) {

console.log("it's true!");

}

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

if (1 == '1') {

console.log("it's true!");

}

这段代码在控制台中尽然也输出了“it’s true!”,但其实这并不是你所期望的输出。这里的==运算符转换了运算数的类型,从而使得两个运算数相等了。这里if语句中的==运算符使得右边string类型的“1”变成了number型的1。

想要得到你想要的输出,这里应该用= = =运算符来代替= =。===不会强制转换运算数的类型,这样才能如你所期望的。同样地,用!= =运算符来替换!=。下面是用==来做比较,得出的结果令人意外。

'' == '0' //
false

'0' == '' //
true

false == '0' //
true

' \t\r\n ' == 0 //
true


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

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

new Number(10);

new String("hello");

new Boolean(true);

new Object();

new Array("one", "two", "three");

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

10;

"hello";

true;

{};

["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循环语句,代码如下:

var prop, obj = { name: "Joe", job: "Coder", age: 25 };

for (var prop in obj) {

console.log(prop + ": " + obj[prop]);

}

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

name: Joe

job: Coder

age: 25

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

Function Dog (name) {

this.name = name;

}

Dog.prototype.legs = 4;

Dog.prototype.speak = function () {

return "woof!";

};

var d = new Dog("Bowser");

for (var prop in d) {

console.log( prop + ": " + d[prop] );

}

console.log("=====");

for (var prop in d) {

if (d.hasOwnProperty(prop)) {

console.log( prop + ": " + d[prop] );

}

}

// Output

// name: Bowser

// legs: 4

// speak: function () {

return "woof!";

// }

// =====

// name: Bowser

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

for (var prop in d) {

if (typeof d[prop] !== 'function') {

console.log( prop + ": " + d[prop] );

}

}

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


错误6-使用with或eval

幸运的是,现在大部分javascript教程都不会教你使用with或eval。但是一些老教程或名气不大的资料时(因为有时候好的资料在网上很难找到),可能会发现有使用with或eval。

下面是两个不用with的主要原因:

1、 它会降低代码性能

2、 不易于代码阅读

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

var person = { name: "Joe", age : 10 };

with (person) {

console.log(name); //
Joe

console.log(age); //
10

}

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

var person = { name: "Joe", age : 10 },

name = "Billy";

with (person) {

console.log(name); //
Billy

job = "Designer";

}

console.log(person.job); //
undefined;

console.log(job); //
Designer

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

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

完全可以直接编写在代码中。

eval解析很慢的,性能跟with差不多。

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


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

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

parseInt("200"); //
200

parseInt("043"); //
35

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

parseInt("020", 10); //
20

parseInt("100", 2); //
4


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

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

if (true)

console.log("inside the if statement");

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

var arr = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"],

i = arr.length - i;

while (i) console.log( arr[i--] );

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

if (true)

console.log("inside the if-statement.");

console.log("outside the if-statement.");

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


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

这并不是javascript自身的问题。99%100的javascript编程都会涉及DOM操作,在对DOM操作上会犯很多错误,但这是最明显的一个。

DOM操作会使浏览器重绘页面,所以如果有一连串的元素一个接一个的插入页面中,这会急剧增加浏览器渲染页面的负担。

var list = document.getElementById("list"),

items = ["one", "two", "three", "four"],

el;

for (var i = 0; items[i]; i++) {

el = document.createElement("li");

el.appendChild( document.createTextNode(items[i]) );

list.appendChild(el); //
slow, bad idea

}

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

var list = document.getElementById("list"),

frag = document.createDocumentFragment(),

items = ["one", "two", "three", "four"],

el;

for (var i = 0; items[i]; i++) {

el = document.createElement("li");

el.appendChild( document.createTextNode(items[i]) );

frag.appendChild(el); //
better!

}

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工程师,如过这里有遗漏的,请在留言板中留言告知大家。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: