Mistake 1 - You’re Using Global Variables
If you’re just getting started with JavaScript, you probably think it’s a great thing that all variables are global. Actually, if you’re just getting started, you might not know what that means. Global variables are variables that are accessible from anywhere in your JavaScript, even in different files loaded on the same page. Sounds great, doesn’t it? Any variable you might every want to change is always accessible.
Actually, no.
The reason this is a bad idea is because it’s easy to overwrite values unintentionally. Say you’ve got a web store, and you’re using JavaScript to display the price of all the items in the shopping cart (of course, you’ll recalculate this on the server side; this just enhances the user experience). Here’s some code you might have:
- var total = 0, // total price
- tax = 0.05; // 5%
Now, let’s say that you’re also using some code you found online to display some tweets on the page … or to make a sharp little gallery for your products. They might have some code like this:
- var total = 15; // number of tweets pulled from twitter
Or,
- var tax = function () { /* ... */ }; // Trigger Animation eXperience function
Now, you’re in trouble: two important variables have been overwritten, and you might not even realize it; your code will be producing errors, and you’ll pay dearly in time and hair to get things working again.
So what’s the solution? In a word, encapsulation; but there are many ways to do this. Firstly, you could just write all your code within a self-invoking, anonymous function:
- (function () {
- var total = 0, tax = 0.05;
- // other code
- }());
This way, absolutely no code outside the function can get to the values you have inside the function. This works for “personal” code, but it’s not so great for functionality that you want to distribute. For example, if we wanted to create a shopping cart totaller that others could use, using the module pattern would be great:
- var cartTotaler = (function () {
- var total = 0; tax = 0.05;
- // other code
- return {
- addItem : function (item) { },
- removeItem : function (item) { },
- calculateTitle : function () { }
- };
- }());
One more thing about global variable: note that if you don’t use the var
keyword when creating a variable, the JavaScript engine will create a global variable by default. So:
- (function () {
- tax = 0.05;
- }());
- var totalPrice = 100 + (100 * tax); // 105
The variable tax
is available outside the function because it’s not declared with the var
keyword. Watch out for this.
Mistake 2 - You’re Not Using Semicolons
Every statement in JavaScript must end with a semicolon. It’s that simple. The issue here is that is you don’t put it in, the compiler will: this is called semicolon insertion. So the question is, if the compiler will insert them for you, why waste your time?
Well, in some places, it’s absolutely necessary; for example, you must put semicolons between the statements in a for-loop’s condition, or you’ll get a syntax error. But what about at the end of lines?
The JavaScript community is really divided on this. I’ve read very well-respected professionals on both sides of the debate. Here’s my argument: whenever you’re relying on the JavaScript compiler to change your code (even in what seems like a small way), you’re in dangerous waters.
For example, look at this simple function:
- function returnPerson (name) {
- return
- {
- name : name
- };
- }
This looks like it should return a cute little object … but in reality, the JavaScript compiler assumes you meant to put a semicolon after return
, and will therefore return nothing; that object will get ignored. Your solution would be to do this:
- return {
- name : name
- };
I’m going to go with “diligently insert semicolons”; honestly, it becomes habit pretty quickly. Also, as a web developer, you’ll probably use other languages (like PHP) where semicolons are required. Why switch back and forth when you don’t have to?
Editor’s Note- Another way to look at it: unless you’re aware of every single situation where they can successfully be omitted, don’t risk it.
Mistake 3 - You’re Using ==
If you left your computer right now and walked until you met any random JavaScript developer (that might take a while), and asked him/her to give you one common JavaScript mistake, this is probably what he/she would say: “using double-equals instead of triple-equals.” What’s this mean?
Try this:
- if (1 == 1) {
- console.log("it's true!");
- }
You’d expect that to work, right? Well, now try this:
- if (1 == '1') {
- console.log("it's true!");
- }
Yes, you got “it’s true!” output to the console … and yes, that’s a bad thing. What’s going on here is that the==
equality operator is coercing the values: this means it’s actually changing them to try to make the two values more similar. In this case, it’s converting the string “1” to the number 1 … so that our if-statement condition passes.
The solution here is to use ===
; this doesn’t perform any type coercion, so the values are what you expect them to be. Of course, this all goes for the !=
and !==
operators as well.
Now, for your amusement, here’s a few of the incredible inconsistencies that you’ll get if you use double-equals:
- '' == '0' // false
- '0' == '' // true
- false == '0' // true
- ' \t\r\n ' == 0 // true
Mistake 4 - You’re using Type Wrapper Objects
JavaScript kindly (um?) gives us some type wrappers for easy (um?) creation of primitive types:
- new Number(10);
- new String("hello");
- new Boolean(true);
- new Object();
- new Array("one", "two", "three");
First off, this is just super inconvenient. All these things can be done with many fewer keystrokes:
- 10;
- "hello";
- true;
- {};
- ["one", "two", "three"];
But, wait, there’s more: these two things aren’t exactly equal. Here’s Douglas Crockford on the topic:
For example,
new boolean(false)
produces an object that has avalueOf
method that returns the wrapped value.- JavaScript: The Good Parts, page 114
This means that if you run typeof new Number(10)
or typeof new String("hello")
, you’ll get ‘object’—not what you want. Plus, using wrapper objects can cause behaviour that you’re not expecting if you’re used to primitive values.
So why does JavaScript provide these objects? It’s because they’re used internally. Primitive values don’t actually have methods (because they aren’t object); so, when you call a method on a primitive object (such as "hello".replace("ello", "i")
), JavaScript creates a wrapper object for that string, does what you want, and then discards the object.
Leave the typed wrappers up to JavaScript and use the primitive values.
Note: this should go without saying, but I want to make this clear for the newbies: I’m not saying you shouldn’t use constructor functions and new
(although some do recommend that). This advice specifically applies to primitive value types—numbers, strings, and booleans—arrays, and blank objects.
Mistake 5 - You’re not Property-Checking when Using For-In
We’re all familiar with iterating over arrays; however, you’ll probably find yourself wanting to iterate over the properties of an object. (Digression: the items in an array are actually just numbered properties in an object.) If you’ve done this before, you’ve used a for-in loop:
- var prop, obj = { name: "Joe", job: "Coder", age: 25 };
- for (var prop in obj) {
- console.log(prop + ": " + obj[prop]);
- }
If you run the above code, you should see this output:
- name: Joe
- job: Coder
- age: 25
However, browsers will include properties and methods from further up the prototype chain. Most of the time you won’t want to see these when enumerating properties. You should be using the hasOwnProperties
to filter out properties that aren’t actually on the object:
- 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
Sometimes, you’ll want to let the properties through, but filter out any methods. You can do that by usingtypeof
:
- for (var prop in d) {
- if (typeof d[prop] !== 'function') {
- console.log( prop + ": " + d[prop] );
- }
- }
Either way, always be sure to clarify your for-in statements to avoid unwanted results.
Mistake 6 - You’re Using with
or eval
Thankfully, most sources for learning JavaScript today don’t teach you about with
or eval
. But if you’re using some older material—or using a less-than-reputable source (because sometimes good material is hard to find on the web)—you might have found with
and eval
and given them a try. Terrible move, web developer.
Let’s start with with
. Two main reasons not to use it:
- It really slows down the execution of your JavaScript.
- It’s not always clear what you’re doing.
Point one stands on its own, so let’s look at the second. Quickly, here’s how with works: You pass an object to a with statement; then, inside the with statement block, you can access properties of the object as variables:
- var person = { name: "Joe", age : 10 };
- with (person) {
- console.log(name); // Joe
- console.log(age); // 10
- }
But, what if we have a variable with the same name as a property of the object we’re with
-ing? Basically, if both a variable and a property with the same name exist, the variable will be used. The other gotcha is that you can’t add a property to the object inside a with statement: if no property or variable exists, it will be made a variable of the scope outside the with statement:
- 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
So, how about eval
? In a nutshell, you can pass a string of code to the function and it will execute the code.
- eval( "Console.log('hello!');" );
Sounds harmless, even powerful, right? Actually, that’s the main problem: it’s too powerful. There’s obviously no reason to just hand it a hard string that you write directly into your code, because 1) why not just write the code? And 2) eval
is slower, just like with
. Therefore, the primary use of eval
is to execute code that you don’t have at runtime. You could be getting this from the server, or taking code directly from the user. Do you really want to give your website users complete control of your code? I hope not. Also, it opens your site up to unnumbered hackers: using eval
is basically a sign that says ”I’m away, and the key is under the mat.” If you love yourself or your users: don’t use it.
Mistake 7 - You’re Not Using a Radix When Using parseInt
JavaScript has a great little helper function called parseInt
that allows you to convert a string that contains a number to a number:
- parseInt("200"); // 200
- parseInt("043"); // 35
Um, what happened there? Shouldn’t that second example be 43? Actually, parseInt
will work with more than just decimal values: so, when parseInt
sees a string that starts with a 0, it assumes that it’s an octal number (base 8). That’s why it’s a mistake not to pass a radix; this tells parseInt
what base the number is in (it always outputs a decimal number).
- parseInt("020", 10); // 20
- parseInt("100", 2); // 4
Mistake 8 - You’re Not Using Braces on if
and while
statements
One of the most obvious beauties of JavaScript is its flexibility. But sometimes, that can come back to fight you. That’s certainly the case with braces on if- and while-statement blocks. These braces are optional if you only have one line of code in the block:
- if (true)
- console.log("inside the if statement");
This is great, because you can even put them on the same line:
- var arr = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"],
- i = arr.length - i;
- while (i) console.log( arr[i--] );
But this isn’t smart for a couple of reasons: firstly, it can become unclear:
- if (true)
- console.log("inside the if-statement.");
- console.log("outside the if-statement.");
See what I mean? That second line isn’t in the if-statement, but it sure looks like it. Braces would make that clear. Also, if you ever want to add a line to the block, you have to remember to add the braces as well. Just adding them in the first place is so much easier. Do it.
Mistake 9 - You’re Adding Elements to the DOM Individually
All right, all right: this isn’t really JavaScript itself. But, in 99 of 100 cases, JavaScript means using the DOM. While there’s a lot of mistakes you can make when working with the DOM, this is a big one.
I fondly remember the day when I inserted my first DOM element via JavaScript. It’s fun to do, and oh-so-useful, but it unfortunately is a strain on the page: inserting a DOM element forces the browser to completely repaint the page, so if you have a whole bunch of elements to add, adding them one by one is a bad idea:
- 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
- }
Here’s what you should do instead: use document fragments. Document fragments are a container to hold DOM elements; then instead of inserting each element individually, you can insert them all at once. The document fragment isn’t a node in itself and there will be nothing to show for it in the DOM: it’s just an invisible net for holding DOM elements before you put them into the DOM. So, here’s how you do it:
- 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);
Faster, quicker, cleaner—what’s not to love?
Mistake 10 - You’re Not Learning JavaScript
Many people don’t take the time to learn JavaScript right.
JavaScript does not equal jQuery. Did I just shock your sock off? If you found yourself guilty of committing several of the mistakes listed above, you probably need to do some serious JavaScript studying. JavaScript is a language that you can use almost without learning it, which means that so many people don’t take the time to learn it right. Don’t be one of those people: there are so many awesome JavaScript tutorials out there that you have no excuse for not learning the language. If all you know is jQuery (or Mootools, or whatever), you’re really putting yourself in a bad spot.
Mistake 11 - You’re Following all the Rules
Rules are made to be broken.
The last and final mistake you’re making is that you’re following all the rules. Yes, even some of the rules I’ve listed here. As with anything, rules are made to be broken. If you’re relatively new to JavaScript, you should probably avoid with fervour all the mistakes I’ve outlined for you today. But the truth is that if you understand why it’s recommended not to do something, that thing becomes a tool that you might use in just the right circumstance. For example, eval
is preached against loudly, but it’s the only tool you can use to parse JSON from a server. Of course, there are many security checks in place when you do it (you should probably use a library). But the point is that you shouldn’t be afraid to knowledgeably use the “bad practices” I’ve listed above if the need arises. It’s the same close-mindedness that causes you to make these mistakes in the first place is the same close-mindedness that keeps you from using them when they are the right tool.
Of course, never make mistake 10 -
Conclusion
If you’re new to JavaScript, hopefully you’re a better JavaScript developer now. If you consider yourself a JavaScript pro (then how are you even still reading this?), what have I missed? Let us all know in the comments!
===========================
javascript是比较容易学的。但是,对于这门语言需要有一些值得注意的地方。本文将指出javascript编程中可能犯过的10个错误
错误1-使用全局变量
如果你刚开始javascript编程,可能会觉得全局变量很好用。事实上,刚开始javascript编程,你可能不知道使用全局变量会带来什么麻烦。在同一个页面中,全局变量可以在任何内嵌的javascript代码段中或是该页面加载的不同的js文件中,都能访问到。这听起来很强大,是吗?这就使得全局变量可以随时随地的被修改赋值。
事实上这样很糟!
这样做会导致变量在意料之外被修改重写。假设有一个网店,需要用javascript计算并显示购物车所有商品的价格总和(当然,服务器端还会进行重新计算,这里只是为了增强用户的体验)。可能会编写代码如下:
现在,还需要用javascript代码在网站上展示一些信息,或则是做一个商品仓库。代码如下:
或则是如下代码:
现在,出现问题了:两个重要的变量被重写,但可能还没被意识到。这样代码运行会出错,会花费很多时间来跟踪和修复该错误。
那该如何解决呢?简言之—“封装”:当然封装有很多方法可以实现。第一种做法是将代码放入一个匿名的自调函数中。代码如下:
这样做,在函数外部是绝对不能访问到函数内部定义的变量。这就形成了个人的代码空间,但是这样就不能公开部分方法或属性。例如,想要创建一个购物车,定义一个总价的变量,作为公共属性,这种情形下可以采用模块式编程。
关于全局变量有一点值得注意,如果不用关键词var来声明创建变量,那么javascript引擎会默认将该变量定义为全局变量。
这里的变量tax在函数外部也是可以被访问的,因为在定义tax的时候没有使用var关键词。
错误2-不加分号
每句javascript语句必须以分号结尾。在编程时如果忘了加分号,这时javascript编解析器会自动加上。那我们在编程的时候是不是就可以完全不用浪费时间去加分号呢?
但是在一些语句中,分号是必不可少的。如for循环语句中的分号是必不可少的,否则会报语法错误。那么语句末尾的分号呢?
Javascript社区已经讨论过该问题。下面是本文的看法:你的代码,只要是被javascript解析器修改过(即便是很小的修改,如添加分号),就可能会出现一些你意料之外的结果。看看下面这段javascript代码:
该方法看起来会返回一个对象,但事实上,javascript解析器会在return后面紧接着添加一个分号,这就导致该函数返回undefined。Return后的对象会被忽略。解决方法很简单,代码如下:
在javascript编程中应严格要求自己添加分号,这个习惯并不难。当然,作为一名web开发人员,你可能也会用到其他的语言(如php),这些语言都是严格要求以分号结尾的。你可以把这些编程语言的习惯带到javascript编程中来。
作者注解:除了你完全肯定可以被忽略的地方,你都不可以忽略添加分号。
错误3-使用==
如果你问一个javascript编程者,在javascript编程中通常会犯什么错误。他/她很可能会说,使用= = =来代替= =。这是什么意思呢?
试试下面的代码:
代码如你所愿的输出了“it’s true!”那再试试下面的代码:
这段代码在控制台中尽然也输出了“it’s true!”,但其实这并不是你所期望的输出。这里的==运算符转换了运算数的类型,从而使得两个运算数相等了。这里if语句中的==运算符使得右边string类型的“1”变成了number型的1。
想要得到你想要的输出,这里应该用= = =运算符来代替= =。===不会强制转换运算数的类型,这样才能如你所期望的。同样地,用!= =运算符来替换!=。下面是用==来做比较,得出的结果令人意外。
错误4-使用数据类型的包装对象
Javascript提供了各个数据类型的包装对象。
首先,它们并不好用。上面的代码可以用更少的代码来实现,如下:
但是这两种方式还是有所不同的。下面是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循环语句,代码如下:
运行上面的代码,输出如下:
但是,浏览器中for-in遍历对象属性和方法时会包括对象原型链上的所有属性和方法。但绝大多数属性是不希望被枚举出来的。可以用hasOwnProperties方法来检测属性是否属于对象。代码如下:
有时,只希望枚举列出对象的的属性,不包括方法。可以用typeof方法,代码如下:
不管怎么样,在用for-in循环时要确保对属性进行检测,以避免得到你意料之外的结果。
错误6-使用with或eval
幸运的是,现在大部分javascript教程都不会教你使用with或eval。但是一些老教程或名气不大的资料时(因为有时候好的资料在网上很难找到),可能会发现有使用with或eval。
下面是两个不用with的主要原因:
1、 它会降低代码性能
2、 不易于代码阅读
第一点是它与生俱来的。第二点,看看下面的代码,这里用with访问person对象的name、age属性。
但是,若果有一个变量和对象其中一个属性同名,那用with会发生什么呢?事实上,这种情况下,访问变量会引用那个变量而不是对象的属性。另一个值得注意的是,在with语句中,如果访问的属性不存在或对象不存在,就不能给对象添加属性,同时会使得作用域链上with作用域后的那个作用域中创建一个变量。
那eval呢?它可以接受一个字符串参数,并且解析执行改字符串。
这听起来没有什么不好,甚至觉得很棒,对吗?但问题就是这太棒了!与其将一连串字符串将给它来解析执行,为什么不直接编写在程序中呢?不该这么做的原因如下:
- 完全可以直接编写在代码中。
- eval解析很慢的,性能跟with差不多。
eval的用法是在非运行时运行环境。可以从服务器端或客户端获取代码。难道真的想你的网站用户来底控制你的代码?这样不就意味着你的网站向无数的黑客敞开了大门。用eval就好比,离开了家,并告诉大家钥匙就是门口垫子下面。如果你爱自己或你的用户,就不要用eval。
错误7-在用parseInt时不用基数
Javascript提供了一个非常有用的方法parseInt,它可以将字符串转换为数值。
结果是不是令人觉得意外?第二句为什么不是43?事实上,parseInt方法不仅仅是只能把字符串当做十进制数来转换。当parseInt的第一个参数是以0开头,它就会把字符串当作是八进制数来转换。这就是不使用基数出现的意料之外结果。第二个参数–基数,会指定parseInt方法把字符串当做什么进制的数来转换。(当然,它的返回值永远是十进制数)
错误8 if和while语句不使用{}
Javascript最明显的特点是语法要求不那么严格。但正是这样的特点,有时会带来麻烦。If和while语句的{}就会引起一些麻烦。{}是根据if条件成立时执行代码语句的条数来用的。
这里看起来没什么问题,因为这里的执行语句只有一句
但是这样做不易于阅读:首先,不用{}代码结构看起来不是那么清晰。
看看上面的代码,第二行console语句是不属于if执行语句的,但是这里它看起来像是if的执行语句。使用{}会使结构更清晰。同时,如果你想在if的执行代码中添加一句,也需要使用{}。习惯使用{}并不是一件难事。
错误9-单个单个地插入dom元素
这并不是javascript自身的问题。99%100的javascript编程都会涉及DOM操作,在对DOM操作上会犯很多错误,但这是最明显的一个。
DOM操作会使浏览器重绘页面,所以如果有一连串的元素一个接一个的插入页面中,这会急剧增加浏览器渲染页面的负担。
Document fragments 是一个DOM元素容器,可以使用它同时添加这些元素到页面中。Document fragment自身不是一个DOM节点,它不会在页面DOM树中显示,并且在把它插入DOM之前它是不可见的。下面是它的用法:
非常快速、简洁!
错误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工程师,如过这里有遗漏的,请在留言板中留言告知大家。