首页 > 编程笔记
JavaScript介绍(非常详细)
在如今种类众多的编程语言中,JavaScript 是一个非常特殊的存在。
从语言设计的角度来分析,JavaScript 应该被归类为基于原型的、解释型的高级编程语言。也就是说,尽管 JavaScript 在语法上与 Java、C/C++ 非常相似,但从编程方法的应用上来说,它受 Self 和 Scheme 这类语言的影响多一些,因此在具体使用方式上更接近后者。
除此之外,JavaScript 还是一门支持多种编程范式的语言。它支持面向对象编程、指令式编程以及函数式编程,因而具有极为灵活的表达能力。
在适用领域方面,JavaScript 从最初的纯浏览器端脚本语言,逐步发展成了如今这样一门可在 Web 浏览器端、移动设备端、桌面应用端以及服务器端通用的强大编程语言。
由于这门语言在使用上的深度和广度,以及其在语法、设计理念上的复杂度,因此在具体学习该语言之前,需要先系统地了解一下它的概况。
在阅读完本文之后,希望读者能了解:
但众所周知的是,在过去相当长的一段时间里,Web 站点都只是一组依靠超链接简单串联在一起的 HTML 文档。这些文档既无数据处理能力,也无法响应用户的操作,简单到甚至都不能被称为程序,更像一本被放在互联网上供人浏览的书。
然而,随着 Web 站点的业务需求与日俱增(例如电子商务、线上交友、视频分享等),开发者越来越希望自己所创建的 Web 站点具有更强大的数据交互功能,并能即时响应用户的操作。于是,JavaScript 应运而生。
当然,罗马不是一天建成的,JavaScript 这门语言也不是生来就如此强大的,甚至它最初的设计目标只是想在 Web 浏览器中运行一些嵌入式脚本而已。
JavaScript 的历史开始于 1995 年,当时世界上非常成功的 Web 浏览器提供商——网景公司聘请了一个名叫布兰登·艾奇(Brendan Eich)的人,希望他研发一门能与 Java 语言搭配使用、语法也与其相似的浏览器端脚本语言。他没有辜负网景公司的重托,本着速战速决的态度,仅用了 10 天就完成了该语言的原型设计,并在 Netscape Navigator 2.0 的 Beta 版中发布了它,当时这门语言的名称还是 LiveScript。
同年 12 月,网景公司在 Netscape Navigator 2.0 Beta 3 版发布时又将它重新命名为 JavaScript,相传这样做主要是想让这门新生的编程语言蹭一下 Java 的热度。他们当时可能也没有想到,这个名字反而在日后成了大众对该语言的诸多误解之一。事实上,Java 和 JavaScript 之间并没有任何从属关系。
之后的事情大家都耳熟能详了。网景公司凭借着 JavaScript 在 Web 浏览器市场上大获成功,这最终引起了微软公司的注意。为了与之竞争,微软公司随后在 Internet Explorer 3.0 浏览器上提供了自家的 JavaScript 实现,即 JScript。
如果这能带来良性竞争倒也不失为一件好事,但让人非常遗憾的是,当时微软公司在自己的版本中加入了很多IE(Internet Explorer)浏览器的专属特性。这些举措让不少基于 IE 浏览器设计的 Web 页面无法在非 IE 浏览器中正常显示,结果就导致了它与网景公司之间的恶性竞争,最终引燃了一场非常惨烈且影响深远的浏览器大战(时间大约是从 1996 年到 2001 年)。
站在今天回顾那段历史,我们会发现那场大战不只开启了开源运动的时代,也让 JavaScript 这门语言的标准化问题被提上了议程。
在次年的 6月,ECMA 就以当下的 JavaScript 语言实现为基础制定了 ECMA-262 标准规范。自此,JavaScript 核心部分的实现也被称为 ECMAScript。
截至 2019 年 5 月,ECMA 一共更新了 9 个版本的标准规范,如下表所示:
所以,在讨论 JavaScript 这门语言的时候,读者必须要了解该语言除了被 ECMAScript 标准所定义的核心部分,还有其所在的具体运行环境。
例如,当我们讨论基于 Web 浏览器的 JavaScript 的时候,就应该知道这时候的讨论内容除了 ECMAScript 标准所规定的语法和基本对象,通常还会涉及用于处理 Web 页面内容的文档对象模型(Document Object Model,DOM)和用于处理 Web 浏览器事务的浏览器对象模型(Brower Object Model,BOM)。但在 Node.js 运行环境中,DOM 和 BOM 就不存在了,这时候就要专注于 Node.js 所提供的核心模块,以及各种特定用途的第三方模块了。
总而言之,JavaScript 这个术语所代表的不仅是 ECMAScript 标准所规范的一门脚本语言,还涉及这门语言所在的运行环境。在之后学习 JavaScript 的过程中,我们会越来越意识到这一点的重要性。这种意识将有助于理解 JavaScript 在 Web 应用的前后端开发中扮演的不同角色,而不至于产生混淆。
下面是 JavaScript 的特性介绍:
例如,如果我们在 JavaScript 代码中定义了一个名为 x 的变量,那么 x 的初始值可以为数字,然后在执行过程中被重新赋值为字符串,JavaScript 的运行环境会负责自动识别该变量中存储了什么类型的数据。
采用单线程的最大好处是不用像多线程编程那样处理很容易产生 bug 的同步问题,这就从根本上避免了死锁问题,也避免了线程上下文交换所带来的性能上的开销。
当然,单线程的执行方式也有它自身的弱点。例如,它无法充分发挥多核处理器的性能、一个错误就会导致整个程序崩溃,以及执行大量计算时会因长期占用处理器而影响其他异步 I/O 的执行。
不过,虽然大家都认为回调函数是执行异步调用并接收其返回数据的最佳方式,但这种方式会导致代码的编写顺序与其具体执行顺序不一致。对于很多习惯同步编程方式的人来说,阅读这样的代码会是一个不小的挑战。
另外在流程控制方面,由于程序中穿插了各种异步方法和回调函数,因此代码在可读性上也远没有常规的同步方式那么一目了然,这也会给程序的调试和维护工作带来一定的麻烦。
既然我们已经对 JavaScript 的语言特性有了一定的了解,那么接下来就可以对这门语言的适用领域做一些分析了。这些分析将有助于初学者明确 JavaScript 适合用来解决什么问题,不适合用来解决什么问题,以便厘清自己的学习需求和努力方向。
以下是一些适合用 JavaScript 来解决问题的领域。
正是由于 JavaScript 赋予了 Web 页面在浏览器端强大的用户交互能力,我们才迎来了电子商务、云端办公、社交网络等各种 Web 应用蓬勃发展的 Web 2.0 时代。
与 ASP、PHP 这些传统的服务器端脚本语言相比,JavaScript 支持事件驱动、异步编程的特性使它在实现轻量级数据密集型的服务器应用方面有一些高性能、高负载的优势。
当然在另一方面,单线程执行和非阻塞 I/O 的特性也让 JavaScript 在资源利用率和安全性方面受到了一些限制,使其不适合被用来实现需要大规模并行计算,或对数据安全有高要求的应用。
由于其单线程执行的特性使 JavaScript 无法充分利用多核处理器的计算资源,因而不适合用来实现需要大规模并行计算的桌面应用。
当然,目前这种形式的应用正在逐渐被人们遗忘,毕竟 HTML5 为我们提供了更好的选择。
除此之外,JavaScript 有时候还会被用来实现一些 Web 浏览器的扩展与插件、移动端的一些应用,甚至一些用于系统管理的命令行脚本。
总而言之,虽然目前 JavaScript 已经发展成了一门无处不在的全能型编程语言,但还是得注意语言特性的发挥,能发挥出其特性优势的领域才是它真正适用的领域。反之,不分场合地强行使用这门语言只会弄巧成拙、事倍功半。
从语言设计的角度来分析,JavaScript 应该被归类为基于原型的、解释型的高级编程语言。也就是说,尽管 JavaScript 在语法上与 Java、C/C++ 非常相似,但从编程方法的应用上来说,它受 Self 和 Scheme 这类语言的影响多一些,因此在具体使用方式上更接近后者。
除此之外,JavaScript 还是一门支持多种编程范式的语言。它支持面向对象编程、指令式编程以及函数式编程,因而具有极为灵活的表达能力。
在适用领域方面,JavaScript 从最初的纯浏览器端脚本语言,逐步发展成了如今这样一门可在 Web 浏览器端、移动设备端、桌面应用端以及服务器端通用的强大编程语言。
由于这门语言在使用上的深度和广度,以及其在语法、设计理念上的复杂度,因此在具体学习该语言之前,需要先系统地了解一下它的概况。
在阅读完本文之后,希望读者能了解:
- JavaScript 的发展史和标准化过程;
- JavaScript 所具有的特性;
- JavaScript 适用的领域。
JavaScript的前世今生
如果仔细观察一下如今在世界范围内广受欢迎的那些 Web 应用程序,例如 Twitter、Facebook、YouTube、哔哩哔哩、新浪微博、淘宝等,就会发现它们能获得如此大的成功主要是因为它们首先在 Web 浏览器端实现了与桌面应用相似的人机交互体验,然后在移动端也实现了同样良好的用户体验。但众所周知的是,在过去相当长的一段时间里,Web 站点都只是一组依靠超链接简单串联在一起的 HTML 文档。这些文档既无数据处理能力,也无法响应用户的操作,简单到甚至都不能被称为程序,更像一本被放在互联网上供人浏览的书。
然而,随着 Web 站点的业务需求与日俱增(例如电子商务、线上交友、视频分享等),开发者越来越希望自己所创建的 Web 站点具有更强大的数据交互功能,并能即时响应用户的操作。于是,JavaScript 应运而生。
当然,罗马不是一天建成的,JavaScript 这门语言也不是生来就如此强大的,甚至它最初的设计目标只是想在 Web 浏览器中运行一些嵌入式脚本而已。
JavaScript 的历史开始于 1995 年,当时世界上非常成功的 Web 浏览器提供商——网景公司聘请了一个名叫布兰登·艾奇(Brendan Eich)的人,希望他研发一门能与 Java 语言搭配使用、语法也与其相似的浏览器端脚本语言。他没有辜负网景公司的重托,本着速战速决的态度,仅用了 10 天就完成了该语言的原型设计,并在 Netscape Navigator 2.0 的 Beta 版中发布了它,当时这门语言的名称还是 LiveScript。
同年 12 月,网景公司在 Netscape Navigator 2.0 Beta 3 版发布时又将它重新命名为 JavaScript,相传这样做主要是想让这门新生的编程语言蹭一下 Java 的热度。他们当时可能也没有想到,这个名字反而在日后成了大众对该语言的诸多误解之一。事实上,Java 和 JavaScript 之间并没有任何从属关系。
之后的事情大家都耳熟能详了。网景公司凭借着 JavaScript 在 Web 浏览器市场上大获成功,这最终引起了微软公司的注意。为了与之竞争,微软公司随后在 Internet Explorer 3.0 浏览器上提供了自家的 JavaScript 实现,即 JScript。
如果这能带来良性竞争倒也不失为一件好事,但让人非常遗憾的是,当时微软公司在自己的版本中加入了很多IE(Internet Explorer)浏览器的专属特性。这些举措让不少基于 IE 浏览器设计的 Web 页面无法在非 IE 浏览器中正常显示,结果就导致了它与网景公司之间的恶性竞争,最终引燃了一场非常惨烈且影响深远的浏览器大战(时间大约是从 1996 年到 2001 年)。
站在今天回顾那段历史,我们会发现那场大战不只开启了开源运动的时代,也让 JavaScript 这门语言的标准化问题被提上了议程。
JavaScript的标准化
1996 年 11 月,为了反制微软公司的恶性竞争,同时也为了使 JavaScript 语言的实现趋于标准化,网景公司正式向 ECMA(European Computer Manufacturers Association,欧洲计算机制造商协会)提交语言标准。在次年的 6月,ECMA 就以当下的 JavaScript 语言实现为基础制定了 ECMA-262 标准规范。自此,JavaScript 核心部分的实现也被称为 ECMAScript。
截至 2019 年 5 月,ECMA 一共更新了 9 个版本的标准规范,如下表所示:
版 本 | 发表日期 | 相关说明 |
---|---|---|
1.0 | 1997年6月 | 随着 1.0 版标准规范的发布,JavaScript 语言进入标准化的时代 |
2.0 | 1998年6月 | 这一版修正了语言的编程格式,使其形式与 ISO/IEC16262 国际标准一致 |
3.0 | 1999年 12月 | 这一版增加了新的控制指令、异常处理功能以及功能强大的正则表达式,优化了词法作用域链、错误定义,以及数据输出格式等特性 |
4.0 | 2008年7月(放弃) | 由于草案的目标过于激进,相关各方对标准的方案出现了严重分歧,争论过于激烈,因此 ECMA 最终决定放弃发布 4.0 版的标准规范 |
5.0 | 2009年12月 | 这一版新增了“严格模式(strict mode)”,提供更彻底的错误检查,以避免结构出错。澄清了许多 3.0 版标准中的模糊之处,并适应了与规范不一致的真实世界实现的行为。除此之外,这一版的 ECMAScript 还增加了部分新的功能,例如 getters 及 setters,支持 JSON 的解析和序列化等 |
5.1 | 2011年6月 | ECMAScript 的 5.1 版所做的修改主要是为了与国际标准 ISO/IEC 16262—2011保持一致 |
6.0 | 2015年6月 | ECMAScript 2015(ES2015),这一版最早被称为ECMAScript 6(ES6)。这一版的 ECMAScript 新增了类和模块的语法,以及包括迭代器、Python 风格的生成器和生成器表达式、箭头函数、二进制数据、静态类型数组、集合(maps、sets和weak maps)、promise、reflection 和 proxies 在内的其他特性 |
7.0 | 2016年6月 | 即 ECMAScript 2016(ES2016),这一版小幅度地新增了一些新的语言特性 |
8.0 | 2017年6月 | 即 ECMAScript 2017(ES2017),这一版小幅度地新增了一些新的语言特性 |
9.0 | 2018年6月 | 即 ECMAScript 2018(ES2018),这一版新增了异步循环、生成器、新的正则表达式特性和 rest/spread 语法 |
下面来了解一下目前主流的 JavaScript 脚本引擎对不同版本标准规范的兼容性,如下表所示。与所有编程语言的标准化工作一样,标准规范与实际生产过程中的实现或多或少还是会存在一些偏差。目前市场上所使用的 ECMAScript 基本以 ES5、ES6 为主。
脚本引擎 | 代表性浏览器 | ES5 | ES6 | ES7 | ES7 之后 |
---|---|---|---|---|---|
Chakra | Microsoft Edge 18 | 100% | 96% | 100% | 58% |
SpiderMonkey | Firefox 63 | 100% | 98% | 100% | 78% |
Chrome V8 | Google Chrome 70 | 100% | 98% | 100% | 100% |
JavaScriptCore(Nitro) | Safari 12 | 99% | 99% | 100% | 90% |
JavaScript的组成结构
如前所述,JavaScript 最初只是一门依附于 Web 浏览器的脚本语言,正是由于 Node.js 运行环境的出现,它才发展成了如今这样一门横跨 Web 开发领域的前后端、移动设备端以及桌面应用端的全能型编程语言。所以,在讨论 JavaScript 这门语言的时候,读者必须要了解该语言除了被 ECMAScript 标准所定义的核心部分,还有其所在的具体运行环境。
例如,当我们讨论基于 Web 浏览器的 JavaScript 的时候,就应该知道这时候的讨论内容除了 ECMAScript 标准所规定的语法和基本对象,通常还会涉及用于处理 Web 页面内容的文档对象模型(Document Object Model,DOM)和用于处理 Web 浏览器事务的浏览器对象模型(Brower Object Model,BOM)。但在 Node.js 运行环境中,DOM 和 BOM 就不存在了,这时候就要专注于 Node.js 所提供的核心模块,以及各种特定用途的第三方模块了。
总而言之,JavaScript 这个术语所代表的不仅是 ECMAScript 标准所规范的一门脚本语言,还涉及这门语言所在的运行环境。在之后学习 JavaScript 的过程中,我们会越来越意识到这一点的重要性。这种意识将有助于理解 JavaScript 在 Web 应用的前后端开发中扮演的不同角色,而不至于产生混淆。
JavaScript特性
JavaScript 自诞生以来一以贯之的设计理念让它具备了一些与众不同的特性,这些特性基本上决定了它的编程思想以及专长的领域。下面是 JavaScript 的特性介绍:
1) 动态化类型
和大多数动态脚本语言一样,JavaScript 中的数据类型是直接取决于变量中的“值”的,变量本身没有数据类型上的约束,这也一直是动态脚本语言与编译型语言最大的区别之一。也就是说,JavaScript 中的同一个变量可以存储不同类型的值。例如,如果我们在 JavaScript 代码中定义了一个名为 x 的变量,那么 x 的初始值可以为数字,然后在执行过程中被重新赋值为字符串,JavaScript 的运行环境会负责自动识别该变量中存储了什么类型的数据。
2) 多范式编程
JavaScript 虽然在语法上与 Java、C/C++ 非常类似(例如 if-else、switch 条件语句,while、for 循环语句等),但在内在的设计上,它更接近 Self 和 Scheme 这一类语言。也就是说,它既支持面向对象编程,也支持指令式编程和函数式编程,因而具有极为灵活的表达能力。3) 单线程执行
由于最初脱胎于 Web 浏览器,JavaScript 一直习惯采用单线程的执行模式(尽管如今有了支持多线程的 Worker 组件),这一习惯即使到了 Node.js 运行环境中也没变。采用单线程的最大好处是不用像多线程编程那样处理很容易产生 bug 的同步问题,这就从根本上避免了死锁问题,也避免了线程上下文交换所带来的性能上的开销。
当然,单线程的执行方式也有它自身的弱点。例如,它无法充分发挥多核处理器的性能、一个错误就会导致整个程序崩溃,以及执行大量计算时会因长期占用处理器而影响其他异步 I/O 的执行。
4) 事件驱动
在 Web 开发领域,JavaScript 之所以能在浏览器端扮演越来越重要的角色,很大程度上得益于其具有与桌面应用相似的事件驱动模型。当然,这种编程模型虽然具有轻量级、松耦合等优势,但在多个异步任务的场景下,由于程序中的各个事件是彼此独立的,因此它们之间的协作就成了一个需要费心解决的问题。5) 异步编程
在目前流行的 Vue、React 等 JavaScript 前端框架以及 Node.js 运行环境提供的接口中,我们可以很容易地观察到其大部分操作都是以异步调用的方式进行的,而这些异步调用往往以回调函数的形式存在,这成了使用 JavaScript 编程的一大特色。不过,虽然大家都认为回调函数是执行异步调用并接收其返回数据的最佳方式,但这种方式会导致代码的编写顺序与其具体执行顺序不一致。对于很多习惯同步编程方式的人来说,阅读这样的代码会是一个不小的挑战。
另外在流程控制方面,由于程序中穿插了各种异步方法和回调函数,因此代码在可读性上也远没有常规的同步方式那么一目了然,这也会给程序的调试和维护工作带来一定的麻烦。
JavaScript的适用领域
如前所述,如果想判断一门编程语言是否适用于某个领域,很大程度上要去分析该领域是否能发挥出该语言的特性优势。既然我们已经对 JavaScript 的语言特性有了一定的了解,那么接下来就可以对这门语言的适用领域做一些分析了。这些分析将有助于初学者明确 JavaScript 适合用来解决什么问题,不适合用来解决什么问题,以便厘清自己的学习需求和努力方向。
以下是一些适合用 JavaScript 来解决问题的领域。
1) Web浏览器端的应用
JavaScript 在 Web 浏览器端的优势是最显而易见且无可争议的,毕竟这门语言最初就是为解决这一领域的问题而设计的。正是由于 JavaScript 赋予了 Web 页面在浏览器端强大的用户交互能力,我们才迎来了电子商务、云端办公、社交网络等各种 Web 应用蓬勃发展的 Web 2.0 时代。
2) 轻量级的服务器应用
Node.js 运行环境的出现让 JavaScript 的适用领域扩展到了 Web 浏览器之外,尤其是在服务器端的应用。与 ASP、PHP 这些传统的服务器端脚本语言相比,JavaScript 支持事件驱动、异步编程的特性使它在实现轻量级数据密集型的服务器应用方面有一些高性能、高负载的优势。
当然在另一方面,单线程执行和非阻塞 I/O 的特性也让 JavaScript 在资源利用率和安全性方面受到了一些限制,使其不适合被用来实现需要大规模并行计算,或对数据安全有高要求的应用。
3) 轻量级的桌面应用
Electron 框架的出现让 JavaScript 可以被用来实现一些适用于事件驱动、异步编程、非阻塞型 I/O 等特性的轻量级桌面应用。目前流行的 VSCode、Atom 等代码编辑器都是基于这一框架的 JavaScript 应用。由于其单线程执行的特性使 JavaScript 无法充分利用多核处理器的计算资源,因而不适合用来实现需要大规模并行计算的桌面应用。
4) 富媒体式的应用
在 HTML5 出现之前,市面上用于创作富媒体的应用程序(如 Flash)大多数采用的是 ActionScript 脚本。由于它也是一种基于 ECMAScript 标准的脚本语言,所以也可视它为 JavaScript 的一种应用。当然,目前这种形式的应用正在逐渐被人们遗忘,毕竟 HTML5 为我们提供了更好的选择。
除此之外,JavaScript 有时候还会被用来实现一些 Web 浏览器的扩展与插件、移动端的一些应用,甚至一些用于系统管理的命令行脚本。
总而言之,虽然目前 JavaScript 已经发展成了一门无处不在的全能型编程语言,但还是得注意语言特性的发挥,能发挥出其特性优势的领域才是它真正适用的领域。反之,不分场合地强行使用这门语言只会弄巧成拙、事倍功半。