文章有代码部分,如果有阅读困难,请到原文阅读:https://blog.jing.do/4212

前言:本篇文章是我在查询时偶然间发现的,虽然年代久远但是依旧非常适合入门学习,特此翻译下分享给大家,顺便给大家加了一些备注方便阅读(特意加粗刷存在感)。原文全文和链接在最后

———————-以下是正文———————-

备注:本篇文章是34岁的程序员Ayman Hourieh在2006年发布的。我找到了他网站的链接,但是网站已经不存在了。我用Wayback Machine找到了文章的镜像。为了让更多人看到把他摘录分享出来。我只做了一些链接的修改。

JavaScript是一个功能强大的面向对象语言。表面上看他他和JAVA和C非常相似,但是他却截然不同,其核心在于JavaScript更像一个功能性语言。本篇文章是一些JavaScript的小提示,一部分提供一些类C语言的功能模拟,另一部分是想要提高性能并探讨下脚本语言中一些比较难懂的东西。索引如下:

多用途的Array

队列

二叉树

String Concatenation 对比 Array.join

给对象绑定方法

使用自定义排序

Assertion

静态局部变量

null, undefined, and delete

深层嵌套

使用Firebug

$ 和 $$

console.log(format, obj1, …)

console.trace()

inspect(obj)

多用途的Array

尽管JavaScript在数据结构方面看起来很奇特,他的Array比其他编程语言(如C ++或Java)用途更加广泛。 它通常用作数组或关联数组,后面将演示如何将其用作堆栈,队列和二叉树。 复用Array而不是编写其他的数据结构有两个好处:首先,不用浪费时间去重写已经存在的功能,其次,内置浏览器对JavaScript的运行将更高效。

大家都知道栈是后进先出:最后插入的会被最先移除。array有两个方法来实现栈的功能。他们是push()和pop()。push()用于插入item到array的尾部,而pop()则用于移除并返回最后一个item。以下是代码的实例:

varstack=[];stack.push(2);// 当前栈是 [2]stack.push(5);// 当前栈是 [2, 5]vari=stack.pop();// 当前栈是 [2]alert(i);// 显示 5

队列

队列是先进先出的:现行插入的将会被最先移除。array可以用push()和shift()来实现队列。push()用于插入item到尾部,shift()用于移除第一个item。案例如下:

varqueue=[];queue.push(2);// 现在的队列 [2]queue.push(5);// 现在的队列 [2, 5]vari=queue.shift();// 现在的队列 [5]alert(i);// 显示 2

值得注意的是,array还有一个unshift()的函数。这个函数用于将item放到array的头部。所以栈也可以使用unshift()/shift(),而队列可以用unshift()/pop()。

如果这些函数名称让你迷茫了(译者注:因为unshift()是处理头部,所以相对应的栈需要从头部出去,队列需要换到尾部),你可以给他们取个别名,比如,创建队列的方法名为add和remove:

varqueue=[];queue.add=queue.push;queue.remove=queue.shift;queue.add(1);vari=queue.remove();alert(i);

二叉树

二叉树是在树的节点表示数据。每个节点有一个数据并且有两个子节点(左叉树和右叉树)。在C语言里,这种数据结构通常使用指针来实现。这种实现方法也可以在JavaScript中使用对象和引用来实现。然而,对于一些小的二叉树,有一种更简单便捷的方法,array的第一个item作为树的头。 如果可以使用以下公式计算节点i,则索引左右节点:

leftChild(i)=2i+1rightChild(i)=2i+2

这张图揭示了这个算法: (来自于Wikipedia):

打开网易新闻 查看更多图片

正如你所看到的,这种方法并不是JavaScript的独有之处,但是在处理小的二叉树时非常有用。 例如,您可以编写自己的函数来获取和设置节点的值或子节点,并遍历二叉树,这些方法与做计算或for循环一样简单。但是,这种方法的缺点是随着树的深度增加,浪费的存储空间越来越多。

String Concatenation 对比 Array.join

大家都知道,如果做太多的字符串链接会让性能下降(译者注:不知道的去补课),并且这个非常容易避免。如果你想要用各个字符来组成一个字符串,最差的方法是使用+把所有的字符结合到一起:

str='';for(/* each piece */){str+=piece;// bad for performance!}returnstr;

这种方法将使用太多的字符串链接,会让性能枯竭。

在JavaScript中有个更好的办法,就是Array.join(),他可以让所有array内的元素连接成一个字符串:

vartmp=[];for(/* each piece */){tmp.push(piece);}str=tmp.join(' ');// 用空格作为分隔符returnstr;

该方法不会受到额外的字符串对象的影响,通常执行的非常高效

给对象绑定方法

任何使用JavaScript事件的人都可能遇到了一种情况,他们需要将对象的方法分配给事件处理程序。 这里的问题是事件处理程序会被HTML调用,即使它们最初被绑定到另一个对象。 为了解决这个问题,我用一个函数将对象和方法绑定; 它他会运行对象和方法,并返回一个在该对象调用该方法的函数。 我在Prototype中找到了一个窍门,并且写了以下函数来在不包含Prototype的项目中使用它:

functionbind(obj,method){returnfunction(){returnmethod.apply(obj,arguments);}}

如何使用:

varobj={msg:'Name is',buildMessage:function(name){returnthis.msg+' '+name;}}alert(obj.buildMessage('John'));// displays: Name is Johnf=obj.buildMessage;alert(f('Smith'));// displays: undefined Smithg=bind(obj,obj.buildMessage);alert(g('Smith'));// displays: Name is Smith

使用自定义排序

排序是一个常见的工作。 JavaScript提供了一种排序数组的方法。 但是,该方法默认按字母顺序排列 —— 非字符串元素在排序之前被强制转换为字符串,这个使得数字排序会有意想不到的结果:

varlist=[5,10,2,1];list.sort()// list is now: [1, 10, 2, 5]

这个解释很容易: 数字被强制转换成了字符串,所以10编程了’10’而2变成了’2’,那么JavaScript对比两个字符串的时候,先对比第一个字符。如果str1的第一个字符出现在字符集中的第一个字符之前,则str1被认为是“小于”str2。 在我们的情况下,’1’在’2’之前,所以’10’小于’2’。

幸运的是,JavaScript提供一个重写机制,让我们可以自定义如何排序,我们用a和b两个元素最为例子:

如果a

如果a=b,返回0

如果a>b,返回大于0

写这样的程序比较容易:

functioncmp(a,b){returna-b;}

我们现在可以使用这个方法来做排序:

varlist=[5,10,2,1];list.sort(cmp);//listisnow:[1,2,5,10]

Array.sort()牛逼的地方是允许更复杂的排序。 假设你有一个论坛帖子,每个帖子看起来像:

varpost={id:1,author:'...',title:'...',body:'...'}

如果你想用id排序,只需要创建以下函数:

functionpostCmp(a,b){returna.id-b.id;}

可以说,使用浏览器的方法进行排序将比在JavaScript中实现排序函数更有效。 但是,数据应该在服务器端进行排序。所以,除非必要,否则不应该让数据在客户端排序。

Assertion

Assertion是一种常用的调试技术,它用于确保表达式在执行期间计算为真。 如果表达式计算为假,则表示代码中可能出现的错误。 JavaScript缺少一个内置的Assertion功能,但幸运的是,它很容易编写一个。 如果传递的表达式的计算结果为假,以下实现会抛出AssertException类型的异常:

functionAssertException(message){this.message=message;}AssertException.prototype.toString=function(){return'AssertException: '+this.message;}functionassert(exp,message){if(!exp){thrownewAssertException(message);}}

自己抛出异常并不是非常有用,但是当与有用的错误消息或调试工具结合使用时,您可以检测到有问题的部分。

您还可以使用以下代码段检查异常是否为Assertion异常:

try{// ...}catch(e){if(einstanceofAssertException){// ...}}

该函数的使用类似于C或Java:

assert(obj!=null,'Object is null');

如果obj碰巧为null,Firefox将在JavaScript控制台中打印以下消息:

uncaughtexception:AssertException:Objectisnull

Static Local Variables

大家知道,一些语言像C ++,他们有静态变量的概念,用于函数调用。 JavaScript并不支持此技术。 然而,“功能也是对象”让这个功能成为可能。 方法是:将静态变量变为函数的属性。 假设我们要创建一个计数器函数:

functioncount(){if(typeofcount.i=='undefined'){count.i=0;}returncount.i++;}

当第一次调用count时,count.i是未定义的,所以if条件为true,count.i为0。因为我们将变量存储为一个函数属性,它将在函数调用之间保留它的值, 因此它可以被认为是一个静态变量。

这里有个性能提升小技巧:

functioncount(){returncount.i++;}count.i=0;

虽然第一个例子将Count的所有逻辑封装在主体中,但第二个例子更为有效。

null, undefined, and delete

因为JavaScript有undefined和null所以他不同于其他语言。null是一个特别的数值,他表示没有数值。null会被认为一个特别的对象,因为typeof null会返回null。

undefined表示变量没有被定义,或者定义了但没有给数值。以下情况都会显示undefined:

// i is not declared anywhere in codealert(typeofi);vari;alert(typeofi);

虽然undefined和null是两个不同的类型,但是如果使用 == ,会被判定为相等,但是如果是 === 则不等。

JavaScript还有一个删除操作符,”undefines”一个属性,可以将其应用于对象属性和数组成员,使用var声明的变量不能被删除,而是隐式声明(implicitly declared )的变量可以:

varobj={val:'Some string'}alert(obj.val);// displays 'Some string'deleteobj.val;alert(obj.val);// displays 'undefined'

深层嵌套

如果您需要在深层嵌套对象上执行多个操作,最好将其引用到临时变量中,而不是每次对其进行解引用。 例如,假设您想在文本字段上执行一系列操作:

document.forms[0].elements[0]

建议您存储到变量中,并使用此变量而不是以上构造:

varfield=document.forms[0].elements[0];// Use field in loop

每个点都导致一个操作来检索一个属性,在一个循环中,这些操作是相加的,所以最好做一次,将对象存储在变量中并重新使用它。

Using Firebug

Firefox有一个非常棒的扩展,用于调试名为Firebug的JavaScript代码。 它提供一个具有断点和堆栈视图的调试器,以及一个JavaScript控制台。 它还可以监视Ajax请求。 此外,扩展提供了一组JavaScript函数和对象来简化调试。 您可以在Firebug的文档页面中详细研究它们。 这里有一些我觉得有用的:

$ and $$

熟悉Prototype的人马上就认出他们了,

$() 接受一个字符串参数,并返回其ID是传递的字符串的DOM元素。(译者注:Jquery

$('nav')// returns the element whose id is #nav.

$$()返回DOM的数组

$$('div li.menu')// returns an array of li elements that are// located inside a div and has the .menu class

console.log(format, obj1, …)

console对象提供显示log消息的方法,这个将比alert更加好用,console.log()有点像C里面的printf,他会将输入转化为字符串在console中展示:

vari=1;console.log('i value is %d',i);// prints:// i value is 3

console.trace()

此方法打印一个堆栈跟踪调用它。 它不需要输入参数

inspect(obj)

该功能需要一个参数。 它切换到检查选项卡并检查传递的对象。