学习JavaScript应该尽早知道的几个技巧

来源:互联网 发布:零基础学java pdf 编辑:程序博客网 时间:2024/06/08 06:43

Shortcut Notations 简洁写法

One of the things I love most about JavaScript now is shortcut notations to generate objects and arrays. So, in the past when we wanted to create an object, we wrote:

在过去,如果你想创建一个对象,你需要这样:

1var car = new Object();
2car.colour = 'red';
3car.wheels = 4;
4car.hubcaps = 'spinning';
5car.age = 4;

The same can be achieved with:

下面的写法能够达到同样的效果:

1var car = {
2  colour:'red',
3  wheels:4,
4  hubcaps:'spinning',
5  age:4
6}

Much shorter, and you don’t need to repeat the name of the object. Right now, car is fine, but what happens when you use invalidUserInSession? The main gotcha in this notation is IE. Never ever leave a trailing comma before the closing curly brace or you’ll be in trouble.

这样就简单多了,你不需要反复使用这个对象的名称。这样 car 就定义好了,也许你会遇到 invalidUserInSession 的问题,这只有你在使用IE时会碰到,只要记住一点,不要右大括号前面写分号,你就不会有麻烦。

The other handy shortcut notation is for arrays. The old school way of defining arrays was this:

传统的定义数组的方法是这样:

1var moviesThatNeedBetterWriters = new Array(
2  'Transformers','Transformers2','Avatar','Indiana Jones 4'
3);

The shorter version of this is:

简写版是这样:

1var moviesThatNeedBetterWriters = [
2  'Transformers','Transformers2','Avatar','Indiana Jones 4'
3];

The other thing about arrays is that there is no such thing as an associative array. You will find a lot of code examples that define the above car example like so:

对于数组,这里有个问题,其实没有什么图组功能。但你会经常发现有人这样定义上面的 car ,就像这样:

1var car = new Array();
2car['colour'] = 'red';
3car['wheels'] = 4;
4car['hubcaps'] = 'spinning';
5car['age'] = 4;

This is not Sparta; this is madness—don’t bother with this. “Associative arrays” is a confusing name for objects.

数组不是万能的;这样写不对,会让人困惑。图组实际上是对象的功能,人们混淆了这两个概念。

Another very cool shortcut notation is the ternary notation for conditions. So, instead of the following…

另外一个非常酷的简写方法是使用与三元条件符号。你不必写成下面的样子:

1var direction;
2if(x < 200){
3  direction = 1;
4else {
5  direction = -1;
6}

… You could write a shorter version using the ternary notation:

你可以使用三元条件符号简化它:

1var direction = x < 200 ? 1 : -1;

The true case of the condition is after the question mark, and the other case follows the colon.

当条件为true 时取问号后面的值,否则取冒号后面的值。

JSON As A Data Format 用 JSON 形式存储数据

Before I discovered JSON to store data, I did all kinds of crazy things to put content in a JavaScript-ready format: arrays, strings with control characters to split, and other abominations. The creation of JSON by Douglas Crockford changed all that. Using JSON, you can store complex data in a format that is native to JavaScript and doesn't need any extra conversion to be used immediately.

Douglas Crockford 发明了JSON 之后,一切全变了。使用JSON,你可以使用JavaScript自有功能把数据存贮成复杂的格式,而且不需要再做其它的额外转换,直接可以访问使用。

JSON is short for "JavaScript Object Notation" and uses both of the shortcuts we covered earlier.

JSON 是 “JavaScript Object Notation” 的缩写,它用到了上面提到的两种简写方法。

So, if I wanted to describe a band, for example, I could do the following:

如果你想描述一个乐队,你可能会像这样写:

01var band = {
02  "name":"The Red Hot Chili Peppers",
03  "members":[
04    {
05      "name":"Anthony Kiedis",
06      "role":"lead vocals"
07    },
08    {
09      "name":"Michael 'Flea' Balzary",
10      "role":"bass guitar, trumpet, backing vocals"
11    },
12    {
13      "name":"Chad Smith",
14      "role":"drums,percussion"
15    },
16    {
17      "name":"John Frusciante",
18      "role":"Lead Guitar"
19    }
20  ],
21  "year":"2009"
22}

You can use JSON directly in JavaScript and, when wrapped in a function call, even as a return format of APIs. This is called JSON-P and is supported by a lot of APIs out there. You can use a data endpoint, returning JSON-P directly in a script node:

你可以在JavaScript里直接使用JSON,可以把它封装在函数里,甚至作为一个API的返回值形式。我们把这称作 JSON-P ,很多的API都使用这种形式。你可以调用一个数据提供源,在script代码里直接返回 JSON-P 数据:

01<div id="delicious"></div><script>
02function delicious(o){
03  var out = '<ul>';
04  for(var i=0;i<o.length;i++){
05    out += '<li><a href="' + o[i].u + '">' +
06           o[i].d + '</a></li>';
07  }
08  out += '</ul>';
09  document.getElementById('delicious').innerHTML = out;
10}
11</script>
12<script src="http://feeds.nowamagic.net/v2/json/codepo8/javascript?count=15&callback=delicious"></script>

This calls the Delicious Web service to get my latest JavaScript bookmarks in JSON format and then displays them as an unordered list.

这是调用 Delicious 网站提供的 Web service 功能,获得JSON格式的最近的无序书签列表。

In essence, JSON is probably the most lightweight way of describing complex data—and it runs in a browser. You can even use it in PHP using the json_decode() function.

基本上,JSON是最轻便的描述复杂数据结构的方法,而且它能在浏览器里运行。你甚至可以在PHP里用 json_decode() 函数来运行它。

Native JavaScript Functions (Math, Array And String) JavaScript的自带函数(Math, Array 和 String)

One thing that amazed me is how much easier my life got once I read up thoroughly on the math and string functions of JavaScript. You can use these to avoid a lot of looping and conditions. For example, when I had the task of finding the largest number in an array of numbers, I used to write a loop, like so:

JavaScript里的math和String函数后,发现它们能极大的简化我的编程劳动。使用它们,你可以省去复杂的循环处理和条件判断。例如,当需要实现一个功能,找出数字数组里最大的一个数时,我过去是这样写出这个循环的,就像下面:

1var numbers = [3,342,23,22,124];
2var max = 0;
3for(var i=0;i<numbers.length;i++){
4  if(numbers[i] > max){
5    max = numbers[i];
6  }
7}
8alert(max);

This can be achieved without a loop:

不用循环也能实现:

1var numbers = [3,342,23,22,124];
2numbers.sort(function(a,b){return b - a});
3alert(numbers[0]);

Notice that you cannot use sort() on a number array because it sorts lexically.

需要注意的是,你不能对一个数字字符数组进行 sort() ,因为这种情况下它只会按照字母顺序进行排序。

Another interesting method is Math.max(). This one returns the largest number from a list of parameters:

再有一个有意思的函数就是 Math.max()。这个函数返回参数里的数字里最大的一个数字:

1Math.max(12,123,3,2,433,4); // returns 433

Because this tests for numbers and returns the largest one, you can use it to test for browser support of certain properties:

因为这个函数能够校验数字,并返回其中最大的一个,所以你可以用它来测试浏览器对某个特性的支持情况:

1var scrollTop= Math.max(
2 doc.documentElement.scrollTop,
3 doc.body.scrollTop
4);

This works around an Internet Explorer problem. You can read out the scrollTop of the current document, but depending on the DOCTYPE of the document, one or the other property is assigned the value. When you use Math.max() you get the right number because only one of the properties returns one; the other will be undefined. You can read more about shortening JavaScript with math functions here.

这个是用来解决IE问题的。你可以获得当前页面的 scrollTop 值,但是根据页面上 DOCTYPE 的不同,上面这两个属性中只有一个会存放这个值,而另外一个属性会是 undefined,所以你可以通过使用 Math.max() 得到这个数。 阅读这篇文章你会得到更多的关于使用数学函数来简化JavaScript的知识。

Other very powerful functions to manipulate strings are split() and join(). Probably the most powerful example of this is writing a function to attach CSS classes to elements.

另外有一对非常有用的操作字符串的函数是 split() 和 join()。我想最有代表性的例子应该是,写一个功能,用来给页面元素附加CSS样式。

The thing is, when you add a class to a DOM element, you want to add it either as the first class or to already existing classes with a space in front of it. When you remove classes, you also need to remove the spaces (which was much more important in the past when some browsers failed to apply classes with trailing spaces).

是这样的,当你给页面元素附加一个CSS class时,要么它是这个元素的第一个CSS class,或者是它已经有了一些class , 需要在已有的class后加上一个空格,然后追加上这个class。而当你要去掉这个class时,你也需要去掉这个class前面的空格(这个在过去非常重要,因为有些老的浏览器不认识后面跟着空格的class)。

So, the original function would be something like:

于是,原始的写法会是这样:

1function addclass(elm,newclass){
2  var c = elm.className;
3  elm.className = (c === '') ? newclass : c+' '+newclass;
4}

You can automate this using the split() and join() methods:

你可以使用 split() 和 join() 函数自动完成这个任务:

1function addclass(elm,newclass){
2  var classes = elm.className.split(' ');
3  classes.push(newclass);
4  elm.className = classes.join(' ');
5}

This automatically ensures that classes are space-separated and that yours gets tacked on at the end.

这会确保所有的class都被空格分隔,而且你要追加的class正好放在最后。

Event Delegation 事件委派

Events make Web apps work. I love events, especially custom events, which make your products extensible without your needing to touch the core code. The main problem (and actually one of its strengths) is that events are removed from the HTML—you apply an event listener to a certain element and then it becomes active. Nothing in the HTML indicates that this is the case though. Take this abstraction issue (which is hard for beginners to wrap their heads around) and the fact that "browsers" such as IE6 have all kind of memory problems and too many events applied to them, and you'll see that not using too many event handlers in a document is wise.

Web应用都是由事件驱动运转的。我喜欢事件处理,尤其喜欢自己定义事件。它能使你的产品可扩展,而不用改动核心代码。有一个很大的问题(也可以说是功能强大的表现),是关于页面上事件的移除问题。你可以对某个元素安装一个事件监听器,事件监听器就开始运转工作。但页面上没有任何指示说明这有个监听器。因为这种不可表现的问题 (这尤其让一些新手头疼) ,以及像IE6这样的”浏览器“在太多的使用事件监听时会出现各种的内存问题,你不得不承认尽量少使用事件编程是个明智的做法。

This is where event delegation comes in. When an event happens on a certain element and on all the elements above it in the DOM hierarchy, you can simplify your event handling by using a single handler on a parent element, rather than using a lot of handlers.

于是 事件委托 就出现了。当页面上某个元素上的事件触发时,而在 DOM 继承关系上,这个元素的所有子元素也能接收到这个事件,这时你可以使用一个在父元素上的事件处理器来处理,而不是使用一堆的各个子元素上的事件监听器来处理。

What do I mean by that? Say you want a list of links, and you want to call a function rather than load the links. The HTML would be:

究竟是什么意思?这样说吧,页面上有很多超链接,你不想直接使用这些链接,想通过一个函数来调用这个链接,HTML代码是这样的:

1<h2>Great Web resources</h2>
2<ul id="resources">
3  <li><a href="http://opera.com/wsc">Opera Web Standards Curriculum</a></li>
4  <li><a href="http://sitepoint.com">Sitepoint</a></li>
5  <li><a href="http://alistapart.com">A List Apart</a></li>
6  <li><a href="http://yuiblog.com">YUI Blog</a></li>
7  <li><a href="http://blameitonthevoices.com">Blame it on the voices</a></li>
8  <li><a href="http://oddlyspecific.com">Oddly specific</a></li>
9</ul>

The normal way to apply event handlers here would be to loop through the links:

常见的做法是通过循环这些链接,将每个链接上附加一个事件处理器:

01// Classic event handling example
02(function(){
03  var resources = document.getElementById('resources');
04  var links = resources.getElementsByTagName('a');
05  var all = links.length;
06  for(var i=0;i<all;i++){
07    // Attach a listener to each link
08    links[i].addEventListener('click',handler,false);
09  };
10  function handler(e){
11    var x = e.target; // Get the link that was clicked
12    alert(x);
13    e.preventDefault();
14  };
15})();

This could also be done with a single event handler:

我们用一个事件处理器也能完成这项任务:

01(function(){
02  var resources = document.getElementById('resources');
03  resources.addEventListener('click',handler,false);
04  function handler(e){
05    var x = e.target; // get the link tha
06    if(x.nodeName.toLowerCase() === 'a'){
07      alert('Event delegation:' + x);
08      e.preventDefault();
09    }
10  };
11})();

Because the click happens on all the elements in the list, all you need to do is compare the nodeName to the right element that you want to react to the event.

因为点击事件就发生在这些页面元素里,你要做的就是比较它们的 nodeName,找出应该回应这个事件的那个元素。

Disclaimer: while both of the event examples above work in browsers, they fail in IE6. For IE6, you need to apply an event model other than the W3C one, and this is why we use libraries for these tricks.

免责声明:上面说的这两个关于事件的例子,在所有浏览器里都能运行,除了IE6,在IE6上你需要使用一个事件模型,而不是简单的W3C的标准实现。这也就是我们推荐使用一些工具包的原因。

The benefits of this approach are more than just being able to use a single event handler. Say, for example, you want to add more links dynamically to this list. With event delegation, there is no need to change anything; with simple event handling, you would have to reassign handlers and re-loop the list.

这种方法的好处并不是仅限于把多个事件处理器缩减为一个。你想想,举个例子,你需要动态的往这个链接表里追加更多的链接。使用事件委托后,你就不需要做其它修改了;否则的话,你需要重新循环这个链接表,重新给每个链接安装事件处理器。

Anonymous Functions And The Module Pattern 匿名函数和模块化

One of the most annoying things about JavaScript is that it has no scope for variables. Any variable, function, array or object you define that is not inside another function is global, which means that other scripts on the same page can access—and will usually override— them.

在JavaScript里最令人懊恼的事情是变量没有使用范围。任何变量,函数,数组,对象,只要不在函数内部,都被认为是全局的,这就是说,这个页面上的其它脚本也可以访问它,而且可以覆盖重写它。

The workaround is to encapsulate your variables in an anonymous function and call that function immediately after you define it. For example, the following definition would result in three global variables and two global functions:

解决办法是,把你的变量放在一个匿名函数内部,定义完之后立即调用它。例如,下面的写法将会产生三个全局变量和两个全局函数:

1var name = 'Chris';
2var age = '34';
3var status = 'single';
4function createMember(){
5  // [...]
6}
7function getMemberDetails(){
8  // [...]
9}

Any other script on the page that has a variable named status could cause trouble. If we wrap all of this in a name such as myApplication, then we work around that issue:

如果这个页面上的其它脚本里也存在一个叫 status 的变量,麻烦就会出现。如果我们把它们封装在一个 myApplication 里,这个问题就迎刃而解了:

01var myApplication = function(){
02  var name = 'Chris';
03  var age = '34';
04  var status = 'single';
05  function createMember(){
06    // [...]
07  }
08  function getMemberDetails(){
09    // [...]
10  }
11}();

This, however, doesn't do anything outside of that function. If this is what you need, then great. You may as well discard the name then:

但是,这样一来,在函数外面就没有什么功能了。如果这是你需要的,那就可以了。你还可以省去函数的名称:

01(function(){
02  var name = 'Chris';
03  var age = '34';
04  var status = 'single';
05  function createMember(){
06    // [...]
07  }
08  function getMemberDetails(){
09    // [...]
10  }
11})();

If you need to make some of the things reachable to the outside, then you need to change this. In order to reach createMember() or getMemberDetails(), you need to return them to the outside world to make them properties of myApplication:

如果你想在函数外面也能使用里面的东西,那就要做些修改。为了能访问 createMember() 或 getMemberDetails(),你需要把它们变成 myApplication的属性,从而把它们暴露于外部的世界:

01var myApplication = function(){
02  var name = 'Chris';
03  var age = '34';
04  var status = 'single';
05  return{
06    createMember:function(){
07      // [...]
08    },
09    getMemberDetails:function(){
10      // [...]
11    }
12  }
13}();
14// myApplication.createMember() and
15// myApplication.getMemberDetails() now works.

This is called a module pattern or singleton. It was mentioned a lot by Douglas Crockford and is used very much in the Yahoo User Interface Library YUI. What ails me about this is that I need to switch syntaxes to make functions or variables available to the outside world. Furthermore, if I want to call one method from another, I have to call it preceded by the myApplication name. So instead, I prefer simply to return pointers to the elements that I want to make public. This even allows me to shorten the names for outside use:

这被称作 module 模式或 singleton。Douglas Crockford 多次谈到过这些,Yahoo User Interface Library YUI 里对此有大量的使用。但这样一来让我感到不便的是,我需要改变句式来使函数和变量能被外界访问。更甚者,调用时我还需要加上myApplication 这个前缀。所以,我不喜欢这样做,我更愿意简单的把需要能被外界访问的元素的指针导出来。这样做后,反倒简化了外界调用的写法:

01var myApplication = function(){
02  var name = 'Chris';
03  var age = '34';
04  var status = 'single';
05  function createMember(){
06    // [...]
07  }
08  function getMemberDetails(){
09    // [...]
10  }
11  return{
12    create:createMember,
13    get:getMemberDetails
14  }
15}();
16//myApplication.get() and myApplication.create() now work.

I've called this "revealing module pattern."

我把这个称作 “revealing module pattern.”

Allowing For Configuration 可配置化

Whenever I've written JavaScript and given it to the world, people have changed it, usually when they wanted it to do things that it couldn't do out of the box—but also often because I made it too hard for people to change things.

一旦我把所写的JavaScript代码发布到这个世界上,就有人想改动它,通常是人们想让它完成一些它本身完成不了的任务—但通常也是我写的程序不够灵活,没有提供用户可自定义的功能。

The workaround is to add configuration objects to your scripts. I've written about JavaScript configuration objects in detail, but here's the gist:

解决办法是给你的脚本增加一个配置项对象。

  • Have an object as part of your whole script called configuration.
  • In it, store all of the things that people will likely change when they use your script:
    • CSS ID and class names;
    • Strings (such as labels) for generated buttons;
    • Values such as "number of images being displayed," "dimensions of map";
    • Location, locale and language settings.
  • Return the object as a public property so that people can override it.
  • 在你的脚本了添加一个叫做 configuration 的对象。
  • 这个对象里面,存放所有人们在使用这个脚本时经常要改动的东西:
    • CSS ID 和类名称;
    • 按钮的名称,标签字等;
    • 诸如”每页显示图片数”的值, “图像的显示的尺寸”的值;
    • 地点,位置,以及语言设置。
  • 将这个对象作为一个公用属性返回给用户,这样用户可以修改覆盖它。

Most of the time you can do this as a last step in the coding process. I've put together an example in "Five things to do to a script before handing it over to the next developer."

通常情况下这是你编程过程中的最后一步要做的事情。我把这些集中表现在了一个例子里: “Five things to do to a script before handing it over to the next developer.”

In essence, you want to make it easy for people to use your code and alter it to their needs. If you do that, you are much less likely to get confusing emails from people who complain about your scripts and refer to changes that someone else actually did.

实际上,你也希望你的代码能够让人们很方面的使用,并且根据他们各自的需要进行一些改动。如果你实现了这个功能,你会少收到一些抱怨你的脚本的人发送给你的让你困惑的邮件,这些信件会告诉你,有人修改了你的脚本,而且很好用。

Interacting With The Back End 与后台交互

One of the main things I learned from all my years with JavaScript is that it is a great language with which to make interactive interfaces, but when it comes to crunching numbers and accessing data sources, it can be daunting.

在这么多年的编程经历中,我所领悟到的一个重要的事情就是,JavaScript是一个很优秀的开发界面交互的语言,但如果用来处理数字或访问数据源,那就有点使不上劲了。

Originally, I learned JavaScript to replace Perl because I was sick of copying things to a cgi-bin folder in order to make it work. Later on, I learned that making a back-end language do the main data churning for me, instead of trying to do all in JavaScript, makes more sense with regard to security and language.

最初,我学习JavaScript,是用来替代Perl的,因为我很讨厌非要把代码拷贝到 cgi-bin 文件夹下才能使Perl运行。后来,我明白了应该使用一种后台工作的语言来处理主要的数据,而不能什么事情都让JavaScript去做。更重要的是我们要考虑安全性和语言特征。

If I access a Web service, I could get JSON-P as the returned format and do a lot of data conversion on the client, but why should I when I have a server that has a richer way of converting data and that can return the data as JSON or HTML… and cache it for me to boot?

如果我访问一个Web service, 我可以获取到JSON-P 格式的数据,在客户端浏览器里我把它做各种各样的数据转换,但当我有了服务器时,我有了更多的方法来转换数据,我可以在Server端生成JSON或HTML格式的数据返回给客户端,以及缓存数据等操作。

So, if you want to use AJAX, learn about HTTP and about writing your own caching and conversion proxy. You will save a lot of time and nerves in the long run.

如果你事先了解了并准备了这些,你会长期收益,省去了很多头疼的时间。

Browser-Specific Code Is A Waste Of Time. Use Libraries! 编写适用各种浏览器的程序是种浪费时间,使用工具包吧!

When I started Web development, the battle between using document.all and using document.layers as the main way to access the document was still raging. I chose document.layers because I liked the idea of any layer being its own document (and I had written more than enough document.write solutions to last a lifetime). The layer model failed, but so did document.all. When Netscape 6 went all out supporting only the W3C DOM model, I loved it, but end users didn't care. End users just saw that this browser didn't show the majority of the Internets correctly (although it did)—the code we produced was what was wrong. We built short-sighted code that supported a state-of-the-art environment, and the funny thing about the state of the art is that it is constantly changing.

在我最初开始搞Web开发时,在访问页面时,究竟是使用 document.all 还是使用 document.layers 的问题上痛苦的挣扎了很久。我选择了 document.layers,因为我喜欢任何层都是自己的document的思想 (而且我写了太多的 document.write 来生成元素)。层模式最终失败了,于是我开始使用 document.all。当Netscape 6 公布只支持 W3C DOM 模型时,我很高兴,但其实用户并不关心这些。用户只是看见这种浏览器不能显示大多数浏览器都能正常显示的东西—这是我们编码的问题。我们编写了短视的代码,只能运行在当前的环境下,而不幸的是,我们的运行环境却在不停的改变。

I've wasted quite some time learning the ins and outs of all of the browsers and working around their issues. Doing this back then secured my career and ensured that I had a great job. But we shouldn't have to go through this trial by fire any longer.

我已经浪费了太多的时间来处理对各种浏览器各种版本兼容的问题。善于处理这类问题提供了我一个好的工作机会。但现在我们不必在忍受这种痛苦了。

Libraries such as YUI, jQuery and Dojo are here to help us with this. They take on the problems of browsers by abstracting the pains of poor implementation, inconsistencies and flat-out bugs, and relieve us of the chore. Unless you want to beta test a certain browser because you're a big fan, don't fix browser issues in your JavaScript solutions, because you are unlikely to ever update the code to remove this fix. All you would be doing is adding to the already massive pile of outdated code on the Web.

一些工具包,例如 YUI, jQuery 以及 Dojo 都能够帮我们处理这类问题。它们通过抽象各种接口实现来处理浏览器的各种问题,像版本不兼容,设计缺陷等,把我们从痛苦中解救出来。除非你要测试某个Beta版的浏览器,千万不要在自己的程序里添加修正浏览器的缺陷的代码,因为你很有可能当浏览器已经修改了这个问题时,你却忘了删除你的代码。

That said, relying solely on libraries for your core skill is short-sighted. Read up on JavaScript, watch some good videos and tutorials on it, and understand the language. (Tip: closures are God's gift to the JavaScript developer.) Libraries will help you build things quickly, but if you assign a lot of events and effects and need to add a class to every HTML element in the document, then you are doing it wrong.

另一方面,完全依赖于工具包也是个短视的行为。工具包可以帮你快速的开发,但如果你不深入理解JavaScript,你也会做错事。

1 0