首页 > 代码库 > 浅析Javascript

浅析Javascript

  Javascript是一门脚本语言,一般用于Web前端开发。可以说,从出生到现在它就不是高大上的象征,一开始人们使用它只是为了解决一些页面数据校验之类的简单任务,慢慢的才在业界的努力下一点点升级。它灵活的语法,基于prototype的面向对象实现一度被人嗤之以鼻,甚至国内很多一线的Web开发者都不是特别重视,没有给予足够的时间及精力去深入学习它。不过随着NodeJS的兴起、开源社区的支持、面向个人用户的互联网产品的短平快开发特点,以及Web2.0对用户体验的重视下,Javascript终于媳妇熬成婆,在2014年4月的语言排行榜中上升到第9,算是交了一份不错的答案了。Javascript虽然有不合理之处,但语言的设计整体上非常有趣,如果读者有兴趣,可以花多点时间来慢慢品味这一门动态、弱类型、解释性语言。接下来我们来大致了解一下Javascript的一些特点。

Prototype-Based

  Javascript(以下简称JS)是据称是一门完全面向对象的语言(笔者认为这是一个理解误区,实际上它所宣传的“完全”是依靠动态类型转换实现的),与一般面向对象语言不同的是,在JS中你不能,也不需要定义一个类,因为JS中并没有类的概念(类跟对象是不一样的),JS的面向对象特性是基于一种被称为“Prototype-based”的编程范型实现的。

  原型继承是一种面向对象的实现模式(另一种是广为人知的Class-based Language,如C#、Java),它的核心是每一个对象都会有一个一般被称为prototype的属性,这个属性是对象继承的基础。假设有对象a,对象b的prototype链接到对象a中,那么对象a的所有属性,以及属性的动态变化都会影响到b(注意是影响,不是赋值)。我们来看看Javascript中的例子。

<!--[endif]-->

  相信不需要过多解释,大家就能明白代码包含的意思(先忽略第三行吧,这是一种定义类的方式),注意,在给a.name 赋值后,b.name 确实是同时被修改了,但另一种情况,先给子对象b的属性赋值后,再修改a对象对应的属性(即本例中的age),b是不会受影响的。这种机制的原理与Javascript的原型链规则有关,以后我们再慢慢聊聊。

First-class Function

  JS中,函数是一等对象。一等对象是一个编程语言术语,指语言中可被当做参数、返回值进行传递,并可给变量赋值的对象,比如C#中的对象、基本类型(可能有人会觉得C#中的函数可以利用委托进行传递,所以它是一级对象,实际上在C#的IL层面,委托也是一种对象,也是Class的实例)。JS中函数是一等对象(这个概念很重要),所以你可以对它做能对其他对象做的任何事情,例如:

 

  你可以把函数赋值给变量,可以正常调用它,可以调用它的属性(上例中的length),可以给它添加属性(上例中的age),可以把它赋给另一个变量(上例中的func2),可以给承载函数的变量赋另一个类型的值,与其他对象别无二致,遵循同一套语法,同一套操作。如果继续上面的例子就可以看到下图,证明函数是 Function 的一个实例。

 

Dynamic Programming Language

  动态是JS里面一个有人欢喜有人忧的特点。语言的动静特性,包含两个含义,一是结构层面的,即语言中的对象结构是否允许动态变化(简单的理解就是对象属性的动态添加删除,内存层面则是存储在堆中的对象可以动态扩展内存);二是功能层面的,即语言中的功能(一般表现为函数)是否允许动态变化。JS是动态语言,所以在实际运行中你并不知道某对象的合法性,在大型项目开发中容易出现问题,这也是所有动态语言的通病。相对而言,静态语言则特别严谨,可以在编译阶段发现潜在的许多问题,但实际用起来又比较繁琐,诸多限制。不同技术有不同的适用场景,会在一些环境下解决一些问题,然后在另外一些环境中带来一些问题,单纯讨论动静特性的好坏并无意义(不过,从个人学习角度出发,如果你想在技术这条路走久走远,建议多学习多使用不同编程语言、编程范型,可以看看《七周七语言》这本书。另外,动态语言应该是下一个技术方向,连C#都在支持dynamic关键字了)。

  我们不妨试着从另外一个角度来看待JS的动态这个特性。就现在而言,世界上有很多很多网站(这些域名连接在一起可能可以围绕世界好几圈,12px的字体),这些网站承载着200多个国家几百甚至几千万个公司、个人,以多种方式(博客、论坛、电商、微博。。。)发布的海量信息(信息大爆炸,Big bang!),这种情况下,你觉得Web技术最需要什么?没错,是包容性(从XHTML到HTML5的变化就可见端倪了)。HTML就是一个极具包容性、扩展性、简单的标准,而主流Browser实现的HTML解析器更是进一步提高了这种包容性。对应这一点,如果JS是一种静态语言?如果JS是C#、JAVA(绝对没有嘲笑的意思),你能想像你需要设计一颗继承树来实现一些功能扩展(不是说这种方式不行,Ext JS就是这样设计的,只是用起来不是很灵活)?你能想象你需要用大量的反射、元编程方法来实现一些类型变化?不是说这种严谨的方式不行,但严谨意味着严格,严格意味着轻易就拒绝你,所以严谨的技术(或者其他事物)不容易大范围铺开,不容易吸引大量的人去尝试,不适合Web这种开放的环境。言归正传,我们还是用一个例子来说明动态性吧:

 

  上面的例子很简单,先定义一个对象,然后给它一个本来不存在的属性赋值(改变结构),这样子的操作是合法的,甚至是改变函数对象的结构(上面已经说了,函数是一等对象,可以执行任何语法运行的操作)。上面的例子是针对“对象”,即引用类型的,如果是值类型呢?

 

  诶,undefined?哪是不是JS针对值类型不存在动态性呢?不是的,要理解这一点首先要理解“包装类”的概念,上面已经提到,JS宣传处处皆对象,实际上值类型是通过类型转换实现的,每一个基本类型(string,number,boolean...)都会有对应同名的引用类型(String,Number,Boolean...),基本类型本身是一个存储在栈结构中的值,没有方法表,也就没有方法可言,但在对它进行方法调用时,解释器会创建一个相对应引用对象(相当于对原来的值进行包装),调用方法的操作实际是通过该对象进行的。上例中改变对象结构操作的是针对这个转换后的对象进行的(所以不会报错),但b仍然是原来那个值为“1”的值对象,所以没有发生变化(b.age 是 undefined 的)。需要注意的是,上述的结构变化是没有意义的,因为没有办法引用到那个解释器创建的“包装”对象。如果实在想改变值类型的结构,也是办法的:

 

  上图就是通过改变Number类型(实际上是一个函数)的原型实现的结构扩展,这是一种迂回但有效的做法,不过实际开发中一般不会贸贸然修改解释器中的原生对象。

 

Interpreted Language

  相信大家都知道,至少听过解释性语言。语言是表达思维的工具,需要一个特定的上下文环境才能被理解,编程语言能被计算机理解、执行的上下文环境一般指语言对应的编译器、解释器,可以把它简单理解为一个翻译者。翻译者有两大特点,一是大部分语言一般都有多个翻译者,例如JS的V8、Jhakar、JaegerMonkey,C++的GCC、MSC、TC等等,所以不要把语言与翻译者搅混了,语言是相对独立的;而是翻译者有多种工作方式,可以从头到尾翻译一篇文章,也可以一边听一边翻译,这对应着编译器、解释器的工作机制;三是无论怎样的翻译者,受限于计算机本身的体系结构,它们最终的目标都是机器码(有很多是翻译成中间代码,但这些中间代码最终还是需要被变成二进制才有意义)。

  编译性是指语言编写的代码,在实际运行前,会通过编译器一次性,将所有代码翻译成机器语言,即二进制代码。编译语言的优点有很多很多,比如性能,程序运行的是最直接的二进制代码,不用其它操作;比如代码足够安全,因为二进制(即使是汇编)代码很难看懂,反编译起来一般都比较麻烦。很多高大全的语言,像Pascal、C、C++、C#(会先编译成IL,保证多语言兼容性;运行时编译成二进制代码,注意,是一次性的)等。编译性可以给语言带来许多好处,比如静态类型检查,非常适合商业开发,曾经(现在也是)是许多公司追逐的宠儿(从这个角度也可以看出,封闭、多人协作、软件工程化曾是软件开发的主流思想)。

  解释性则指语言编写的代码会以某种形式(非机器码)存在,在实际运行时,由解释器对逐行代码进行解释、翻译成功二进制代码,交由机器执行。注意,是逐行解释,也就意味在运行时,计算机除了要执行你的代码,还要执行解释器。解释性的优点,主要体现在它移植性比较强(在现今WEB时代,这可是很吃香的),虽然这是通过牺牲效率为代价的(计算机硬件技术的迅速发展,已经足以让这成为不那么重要的因素了)。许多脚本语言,如PHP、Javascript、Basic、Ruby都是解释性的(脚本语言设计的初衷就是简化开发过程编写、编译、链接、运行这个繁琐步骤)。解释性可以为语言带来动态、易扩展等特点,你甚至可以对语言本身进行编程(Ruby的元编程就是一个活生生的例子了),加上它表现良好的移植性,在这个讲究快速迭代的互联网时代,越来越受关注(甚至有一些厂商不惜花重本为原来的编译器带来一些解释性质),可以认为,在不久的将来解释性语言极有可能成为主流语言。

  语言的解释性、编译性并不是泾渭分明的,比如Java,很多人一直闹不清它的性质,其实简单的说,它是先编译(将代码一次性编译成字节码,在这个过程中执行一些静态类型、安全性检查),后解释(JVM加载字节码,并逐行解释成机器码并运行),所以它宣称拥有编译语言的安全性,也具有解释语言的可移植性(就我所知,基本扯淡)。另外一个异类是C#(其他基于.NET平台的语言也是),它也分两个步骤,即先编译(将代码一次性编译成IL,中间语言,用于多语言间的适配,毕竟.NET的初衷就是支持多语言协作),再编译(在实际运行时,将IL编译成机器码,当然,你也可以选择在你的用户使用前执行这个步骤)。顺便说一句,博客园里面的月经贴最近好像有点多,其实学什么都一样,重要的不是工具,而是使用工具的人,C#这么语言真挺好的,很适合拿来学面向对象思想,我用着就挺爽的。

后言

  说了这么多,抛开技术来说说其他的吧。这是我开通博客的处女贴,也是我毕业工作后的第一篇博客,文章内容准备了很久,但行文却是一气呵成,所以可能有很多语法问题,有很多错别字,看在这份上大家拍轻点啊;如果大家觉得是干货,也别客气啊,哈哈。我打算写针对Javascript(在这个基础上,尽力延伸其他内容)写一个系列,内容主要围绕JS及JS在浏览器上的实现,上面简略提到的很多内容都会在适当的时候详细的聊聊,行文风格会跟这篇差不多(如果大家觉得不好,请在留言说明,我一定改),质量则希望是越来越好,总之,有任何问题都请留言让我知道啊。