警惕Java,迎来JavaScript
2010-10-06 10:23:01 阿炯

最近Oracle控告搜索巨人Google,宣称其Android智能手机操作系统上的Dalvik虚拟机侵犯了Java专利,这很自然让很多的开发人员感到担心。但大多数分析指出,这场诉讼对绝大多数Java开发人员将不会有多少的影响,但有些程序员却惊恐不安,他们已经开始寻找一种可替代的语言了。如果你也是这样,你是否考虑过转向JavaScript

“什么?”,你也许会诧异。任何一个开发人员或多说少都知道Java跟JavaScript几乎没有任何的关系。Netscape最初是想把这个跨浏览器的脚本语言叫做LiveScript,但Sun微系统公司说服他们使用JavaScript这个名字——他们的想法是要把JavaScript当成一种HTML和Sun的全功能的Java之间的桥梁。

但如果Sun漠视JavaScript的存在而认为Java将成为Web上的真正语言,那它就大错特错了。Java最终发现它的用武之地只是作为一种服务器端语言,JavaScript在浏览器里给了它致命打击。如今,像CommonJS和Node.js这样的项目把JavaScript扩展到了更广的地方,使它也能够扮演Java在数据中心上的传统角色。在这样一种神奇的角色转换中,JavaScript正在成为一种多才多艺的,强大的,功能齐全的Web语言,而Java却日益显现沦为昔日COBOL语言的结局。

桥接客户端/服务器端

估计经历了那个年代的人都能回忆起当时以浏览器为平台的Java applet的那种笨拙,丑陋,激不起兴趣的样子,它更多给人带来的是烦恼,而不是真正的用处。即使Sun最近的在富(rich)互联网应用技术的尝试 ——JavaFX,也没有使其在对抗其现有的竞争对手(包括Adobe Flash,微软Silverlight)的进程上前进多少。客户端Java,看起来在出生之日就已被判了死刑。

相似的,服务器端JavaScript(SSJS)也从来没制造出多少火花。早在1996年,Netscape Enterprise Server就支持SSJS,但它一种昂贵且有专利的产品。它很快就被开源的Apache server抢光了所有的市场,SSJS也随着它消失了。然而,在那些日子里,JavaScript的确成为了最适合Web页面的轻量级脚本语言。对比一下那个时期新兴的其它语言,例如Perl和 Python,JavaScript运行缓慢而且语法怪异,只有有限的功能支持。更糟糕的是,每个厂商对其的实现还各不相同,导致开发人员浪费时间写一些修改补丁和相关处理。

至今,JavaScript已经走了一段很长的路。独立的、开源的JavaScript引擎的出现——包括谷歌的V8,Mozilla的 SpiderMonkey,和WebKit的SquirrelFish Extreme——意味着任何人都可以在自己的代码里嵌入一个标准兼容的JavaScript解释器,无需你从头来开发它们。目前这三个项目正处在一场猛烈的性能竞赛中,每种引擎的性能都在稳定的进步。随着这些底层技术高速的成熟发展,JavaScript展现出了一种Java从未实现的态势:打破传统的领域壁垒,贯通服务器端和客户端。客户端的Java从未景气过,但服务器端JavaScript却杀了回来。.

服务器端JavaScript越来越受重视

现代的JavaScript引擎都能够单独的独立运行,这使得它们给SSJS提供了天然的便利。但目前JavaScript还只是主要的作为一种浏览器端的语言,这使得它缺少一些开发人员们期望的在其它环境中的功能特征。例如,客户端的开发人员通常是通过互联网加载单独的.js文件,而服务器端的开发人员需要一种更常用的方式,使代码程序能打成包。同样,JavaScript缺少常见的系统功能标准库,相比较而言,像C和Java这样的更加面向系统的语言在这方面要丰富的多。

CommonJS项目正试图解决这些问题。它的目标就是要创造出一套开放的、标准的API,提供诸如二进制对象处理,并行线程,文件、流、和套接字 I/O,系统日志处理等功能接口。除此之外,它还提议了一套代码和相关命名空间的模块格式标准。虽然这还是个很年轻的项目,但它的终极目标却是要让 JavaScript开发人员在写代码时有一个CommonJS规范,写出的程序在不作任何修改的情况下可以在任何CommonJS兼容的平台上运行——不论底层的JavaScript引擎和操作系统是什么。

然而,更令人兴奋的却是Node.js这个项目,它和CommonJS的初衷很相似,而且实现了一些CommonJS API。可是,它却把SSJS的概念提升到了一个新的高度。它的最重要的一项革新就是实现了针对服务器端开发的面向事件的编程模型。这意味着不仅仅 Nodo.js编程会让客户端的JavaScript开发人员感觉到得心应手——因为事件驱动模型是他们的开发规范,同时对于那些严重依赖于并行操作来支持多个并行用户的Web应用程序也是理想的选择。

如果这听起来像有一只手在向你招唤,请看看Node.js 编程例子。这个完全等效的“Hello,world!”程序是只用了6行JavaScript代码的完全的HTTP服务器端实现。

JavaScript:Web之王?

不要奢望JavaScript能把Java从唯我独尊的宝座上拉下来。CommonJS和Node.js都还有很多的功课要做,一种精确的对这两个项目的描述应该是:试验品。对于更多的改进,例如特定的优化和管理工具等,就像Oracle对JRockit JVM所做的这方面的工作将会使Java成为企业软件开发的一个很有吸引力的平台。然而,JavaScript作为服务器端开发语言所带来的好处显而易见。它能够让Web开发人员用一种单一的语法实现所有的程序,去除了典型Web 应用上的混乱和混淆。JavaScript的运行效率的改进日新月异,这是开发人员最看重的。而JavaScript语言本身也发展成熟,成为一种不错的语言,具有同时支持面向对象和函数的编程风格的特征。

还有另外一点需要注意:JavaScript是完全免费和开放的,它由行业内厂商组成的ECMA标准委员会维护。ECMAScript工作组过去处于停滞状态,但它已经克服了种种困难,使JavaScript继续得到高速的发展。而同时,Java,一边假装开放,有不伦不类的JCP(Java Community Process)负责,一边又受到Oracle潜在的法律诉讼的威胁。如果Oracle自己不能意识的现在的形势,那我相信开发人员们可是清清楚楚的看清楚了。

上文源自:外刊IT评论

JavaScript 的三特点

1、作用域和闭包

作用域指代码当前上下文,控制着变量和函数的可见性和生命周期。最大的作用是隔离变量,不同作用域下同名变量不会冲突。作用域链指如果在当前作用域中没有查到值,就会向上级作用域查询,直到全局作用域,这样一个查找过程所形成的链条就被称之为作用域链。

作用域可以堆叠成层次结构,子作用域可以访问父作用域,反之则不行。作用域具体可细分为四种:全局作用域、模块作用域、函数作用域、块级作用域。

全局作用域:代码在程序的任何地方都能被访问,例如 window 对象。但全局变量会污染全局命名空间,容易引起命名冲突。

模块作用域:早期 js 语法中没有模块的定义,因为最初的脚本小而简单。后来随着脚本越来越复杂,就出现了模块化方案(AMD、CommonJS、UMD、ES6模块等)。通常一个模块就是一个文件或者一段脚本,而这个模块拥有自己独立的作用域。

函数作用域:顾名思义由函数创建的作用域。闭包就是在该作用域下产生,后面我们会单独介绍。

块级作用域:由于 js 变量提升存在变量覆盖、变量污染等设计缺陷,所以 ES6 引入了块级作用域关键字来解决这些问题。典型的案例就是 let 的 for 循环和 var 的 for 循环。

了解完作用域再来谈谈闭包:函数A里包含了函数B,而函数B使用了函数A的变量,那么函数B被称为闭包或者闭包就是能够读取函数A内部变量的函数。可以看出闭包是函数作用域下的产物,闭包会随着外层函数的执行而被同时创建,它是一个函数以及其捆绑的周边环境状态的引用的组合。换而言之,闭包是内层函数对外层函数变量的不释放。闭包的特征:
函数中存在函数;
内部函数可以访问外层函数的作用域;
参数和变量不会被 GC,始终驻留在内存中;
有内存地方才有闭包。

所以使用闭包会消耗内存、不正当使用会造成内存溢出的问题,在退出函数之前,需要将不使用的局部变量全部删除。如果不是某些特定需求,在函数中创建函数是不明智的,闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

2、原型和原型链

有对象的地方就有原型,每个对象都会在其内部初始化一个属性,就是prototype(原型),原型中存储共享的属性和方法。当我们访问一个对象的属性时,js引擎会先看当前对象中是否有这个属性,如果没有的就会查找他的prototype对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象。这么一个寻找的过程就形成了原型链的概念。

理解原型最关键的是理清楚__proto__、prototype、constructor三者的关系,我们先看看几个概念:
__proto__属性在所有对象中都存在,指向其构造函数的prototype对象;prototype对象只存在(构造)函数中,用于存储共享属性和方法;constructor属性只存在于(构造)函数的prototype中,指向(构造)函数本身。
一个对象或者构造函数中的隐式原型__proto__的属性值指向其构造函数的显式原型 prototype属性值,关系表示为:instance.__proto__ === instance.constructor.prototype
除了 Object,所有对象或构造函数的 prototype 均继承自 Object.prototype,原型链的顶层指向 null:Object.prototype.__proto__ === null
Object.prototype 中也有 constructor:Object.prototype.constructor === Object
构造函数创建的对象(Object、Function、Array、普通对象等)都是 Function 的实例,它们的 __proto__ 均指向 Function.prototype。

3、步和单线程

JavaScript 是单线程语言,意味着只有单独的一个调用栈,同一时间只能处理一个任务或一段代码。队列、堆、栈、事件循环构成了 js 的并发模型,事件循环是 JavaScript 的执行机制。为什么js是一门单线程语言呢?最初设计JS是用来在浏览器验证表单以及操控DOM元素,为了避免同一时间对同一个DOM元素进行操作从而导致不可预知的问题,JavaScript从一诞生就是单线程。既然是单线程也就意味着不存在异步,只能自上而下执行,如果代码阻塞只能一直等下去,这样导致很差的用户体验,所以事件循环的出现让 js 拥有异步的能力。

JavaScript 诞生 25 周年纪

1995 年 12 月 4 日,Netscape 和 Sun Microsystems 宣布推出 JavaScript。在尝试制作动态网页时,Netscape 公司的 Brendan Eich 开始研究一种新的脚本语言。该语言的第一个版本在十天之内就被开发出来,并命名“Mocha”,之后更名为 LiveScript,最后改为 JavaScript。

1996 年 11 月,Netscape 开始对 JavaScript 进行标准化并向 ECMA International 提交提议。同年 12 月,W3C 提出 CSS1 规范

1999 年 4 月 23 日,JSDoc 首次发布。10 月 13 日,ECMAScript 3 发布,新版本添加了规则表达式和 try/catch 异常处理,可以更好地处理错误,并且增加一些重要的增强功能。

2006 年 6 月 1 日,JSLint 推出,这是 JavaScript 语法检查器的始祖。

2006 年 1 月 14 日,JavaScript 库 jQuery 发布,它封装 JavaScript 常用的功能代码,提供一种简便的 JavaScript 设计模式,可以更轻松导航和操作 HTML DOM。

2009 年 11 月 8 日,Node.js 出现在公共视野。Node.js 是一个 JavaScript 运行环境,它让 JavaScript 可以开发后端程序,实现几乎其他后端语言实现的所有功能,这意味着 JavaScript 可以与 PHP、Java、Python、.NET、Ruby 等后端语言平起平坐。从那时起,“ 凡是能用 JavaScript 写的应用终将用 JavaScript 来写 ”开始在圈内广为流传。

2012 年 10 月 1 日,TypeScript 公开化。这是 Microsoft 推出的 JavaScript 的超集,最终会被编译为 JavaScript 代码。GitHub 刚刚发布的 2020 年度 Octoverse 报告显示,TypeScript 的受欢迎程度已成功超越 C#、PHP 和 C++,其排名从 2018 年和 2019 年的第七位上升到了今年的第四位。

2018 年 6 月 2 日,Node.js 创作者 Ryan Dahl 公开介绍了 JavaScript 和 TypeScript 的新运行时 Deno。Deno 是专门为了修复 Dahl 认为的 Node.js 的关键弱点而创建的--包括安全问题、使用集中式仓库系统(npm)以及"繁琐的工具"。

2020 年 5 月 13 日,Deno 1.0 发布,其创造者同样是 Node.js 的开发者 Ryan Dahl 。5 月 31 日,JavaScript 跟随 SpaceX Dragon 进入太空。Dragon 2 飞行界面使用 Chromium 和 JavaScript 搭建。


ECMAScript 2022 正式发布

ECMAScript 2022 现已于2022年6月下旬获得 ECMA International 的批准。ECMAScript 是标准化的 JavaScript 语言,于 1997 年发布了第一版,现已发展成为世界上使用最广泛的通用编程语言之一。本 Ecma 标准定义了 ECMAScript 2022 Language,是 ECMAScript 语言规范的第 13 版。主要包含内容有:
引入了 top-level await,允许在模块的顶层使用关键字;
新的 class elements:公共和私有实例字段、公共和私有静态字段、私有实例方法和访问器以及私有静态方法和访问器;
类内的静态块,用于执行每个类的评估初始化;
#x in obj语法,用于测试对象上是否存在私有字段;
class X {
  #foo;
  method() {
    console.log(this.#foo)
  }
}

通过/d flag 的正则表达式匹配索引,为匹配的子字符串提供开始和结束索引;
Error对象的cause属性,可用于记录错误的因果链;
Strings、Arrays 和 TypedArrays 的at方法,允许相对索引;
以及Object.hasOwn,这是Object.prototype.hasOwnProperty的一个更简洁方便的替代方法。

let hasOwnProperty = Object.prototype.hasOwnProperty
if (hasOwnProperty.call(object, "foo")) {
  console.log("has property foo")
}
简化为:
if (Object.hasOwn(object, "foo")) {
  console.log("has property foo")
}

具体可查看:
ECMA-262, 13th edition Language Specification
ECMA-262_13th_edition_Language Specification.pdf