JavaScript诞生的历史背景
从JavaScript诞生的(互联网)远古时代说起。
1994年,网景公司(Netscape)发布了Navigator浏览器0.9版。这是历史上第一个比较成熟的网络浏览器,轰动一时。但是,这个版本的浏览器只能用来浏览,不具备在本地与用户互动的能力。比如,如果网页上有一栏"用户名"要求填写,用户点提交,浏览器无法判断是否真的填写了,只有发送请求让服务器端判断。如果没有填写,服务器端就返回错误,要求用户重新填写,这太浪费时间和服务器资源了,效率低下。
为了解决这个问题,网景公司急需一种网页脚本语言,使得浏览器可以与网页在本地实现互动。网页脚本语言到底是用哪家的比较强?网景公司当时有两个选择:一个是采用现有的语言,比如Perl、Python、Tcl、Scheme等等,允许它们直接嵌入网页;另一个是发明一种全新的语言。
权衡利弊后网景决定开发一门新的脚本语言,而这个开发工作的重担就交给了工程师 Brendan Eich。他觉得,这种语言没必要设计得很复杂,只要能够完成一些简单操作就够了,比如判断用户有没有填写表单。所以,JavaScript从诞就这样诞生了。
下面是 Brendan Eich 大神年轻时的照片:
1994年正是面向对象编程(object-oriented programming)最兴盛的时期,C++是当时最流行的语言,Sun公司也正在大肆造势,为即将在第二年推出的Java语言铺路。Brendan 无疑受到了影响,他创造的Javascript语言里面所有的数据类型都是对象(object),这一点与Java非常相似。
其实从名字上来说,也能看出JavaScript在向当时大火的Java来靠拢,用今天网红时代的话讲,就叫蹭热度。当然,Javascript蹭Java热度是两家公司合作的商业行为,是商量好的。JavaScript其实就是网景和Sun两家公司一起携手推向市场的,这种语言被命名为"Java+script"也为Java语言推广造势。
Brendan Eich被指定为这种的设计师。
这里就要说一段Javascript故事的插曲,十分有意思。
Javascript之父Brendan,他本人其实最早对Java一点兴趣也没有。公司将这种网页脚本语言的编写任务派给他的时候,定位是一种"简化版Java语言"。他因个人兴趣缺缺,为了应付公司安排的任务,就只用10天时间就把Javascript设计出来了。可以说,Javascript诞生之初,只是门近似玩具的语言。
可想而知,由于设计时间太短,语言的一些细节考虑得不够严谨,导致后来很长一段时间,Javascript写出来的程序混乱不堪。如果Brendan预见到,未来这种语言会成为互联网第一大语言,全世界有几百万学习者,他会不会在初始版本设计上多花些功夫呢?历史上的很多事情,阴差阳错,无心插柳柳成荫,实在是令人感叹。
JavaScript基本设计思路
总体来说,JavaScript的设计思路大概是这样的:
-
借鉴C语言的基本语法;
-
借鉴Java语言的数据类型和内存管理;
-
借鉴Scheme语言,将函数提升到"第一等公民"(first class)的地位;
-
借鉴Self语言,使用基于原型(prototype)的继承机制。
所以,Javascript语言实际上是两种语言风格的混合产物:
(简化的)函数式编程 +(简化的)面向对象编程。
这种杂糅是由Brendan Eich与网景公司共同决定的。
JavaScript的设计缺陷
缺陷产生的种种因素
不难想象,如此草率不经意下被设计出来的JavaScript语言,在使用中会给程序猿带来很多问题,这些问题是语言诞生之初固有的,不严谨之下,设计上的缺陷。
一方面,设计时间仓促;另一方面,Javascript同时结合了函数式编程和面向对象编程的特点,这很可能是史无前例,它没有先例可供参考。
还有一方面因素加重了它的设计缺陷。1995年5月,设计方案定稿;12月,向市场推出,风靡全球,立刻被广泛接受,全世界的用户都在使用。一年半之后,网景公司申请Javascript的国际标准通过,1997年6月,第一个国际标准ECMA-262正式颁布。这门语言的发展速度如同坐上了火箭,从诞生到爆火,时间太短了,急于定标准抢占商业利益,也让这门语言的设计缺陷还没有充分暴露就固化成了标准。
可供参比的是,C语言问世将近20年之后,国际标准才颁布。
Javascript的10个典型设计缺陷
-
不适合开发大型程序 Javascript没有名称空间(namespace),很难模块化;没有如何将代码分布在多个文件的规范;允许同名函数的重复定义,后面的定义可以覆盖前面的定义,很不利于模块化加载。
-
非常小的标准库
Javascript提供的标准函数库非常小,只能完成一些基本操作,很多基本功能都不具备。
- null和undefined的意义不太清晰
null属于对象(object)的一种,意思是该对象为空;undefined则是一种数据类型,表示未定义。两者容易混淆,尤其是对于初学者来说,但两者本质完全不同。
- 全局变量难以控制
Javascript的全局变量,在所有模块中都是可见的;任何一个函数内部都可以生成全局变量,这大大加剧了程序维护的复杂性。
- 自动插入行尾分号 Javascript的所有语句,都必须以分号结尾。但是,如果你忘记加分号,解释器并不报错,而是为你自动加上分号。
看起来,这似乎很人性化,智能修正笔误。但是往往,这一不严谨的特性会导致一些难以发现的错误。以及还有你并不想加分号的时候,它会自动给你添上,导致语义错误。执行后想debug进行排除错误就会很麻烦。
- 加号运算符
+号作为运算符,有两个含义,在数值运算中可以表示数字与数字的和,在字符串之间可以表示字符与字符的连接。如果一个操作项是字符,另一个操作项是数字,则数字自动转化为字符。这样的设计,不必要地加剧了运算的复杂性,完全可以另行设置一个专门用于字符连接的运算符。
- NaN
NaN是一种数字,它表示超出了解释器的取值极限。它有一些很奇怪的特性:
NaN === NaN; //false
NaN !== NaN; //true
alert( 1 + NaN ); // NaN
与其设计NaN,不如解释器直接报错,反而有利于简化程序,排除故障。
- 数组和对象的区分
由于Javascript的数组也属于对象(object),所以要区分一个对象到底是不是数组,相当麻烦。
9.== 和 ===的区别
==用来判断两个值是否相等。当两个值类型不同时,会发生自动转换,得到的结果非常不符合直觉。在其他一些语言中,==就代表严格相等的比较。而JS中使用"=="的结果经常让你迷茫,看一个例子就有体会:
"" == "0" // false
0 == "" // true
因此,推荐任何时候都使用"==="(精确判断)比较符。
- 基本类型的包装对象
Javascript有三种基本数据类型:字符串、数字和布尔值。它们都有相应的建构函数,可以生成字符串对象、数字对象和布尔值对象。比如:
new Boolean(false);
new Number(1234);
new String("Hello");
实际上没必要,用处不大,却容易带来与基本数据类型之间的混淆。
JavaScript展望
如上文所说,Javascript有先天设计缺陷,数量不少,问题多多,那么它是不是一种很糟糕的语言?它的前途如何,值不值得作为一门吃饭的手艺来学习?
答案是Javascript的发展前景并不糟糕,虽然缺陷不少,但它的编程能力也很强大,浏览器上运行的脚本除了它还并没有更好的选择。
随着ECMAScript标准的不断制定与完善,如果遵守良好的编程规范,尽量避免使用不严谨的语法,能避开大部分因为语法松散带来的问题;Javascript的基础标准库不能满足使用需求,但加上第三方庞大函数库的帮助和支持,常用函数库得到极大的扩展。JavaScript的这些设计缺陷大部分可以回避,JavaScript语言发展的前途光明。
声明,本文主要摘录节选自阮一峰大神博客。 Javascript诞生记 - 阮一峰的网络日志