JavaScript全栈开发
上QQ阅读APP看书,第一时间看更新

1.2 JavaScript的组成与特性

1.2.1 组成结构

如前所述,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应用的前后端开发中扮演的不同角色,而不至于产生混淆。

1.2.2 语言特性

当然,这里所说的“全能型编程语言”仅仅指JavaScript适用的领域很广泛,并不是说可以用这门语言来解决所有的编程问题。JavaScript自诞生以来一以贯之的设计理念让它具备了一些与众不同的特性,这些特性基本上决定了它的编程思想以及专长的领域。下面是JavaScript的特性介绍。

  • 动态化类型。和大多数动态脚本语言一样,JavaScript中的数据类型是直接取决于变量中的“值”的,变量本身没有数据类型上的约束,这也一直是动态脚本语言与编译型语言最大的区别之一。也就是说,JavaScript中的同一个变量可以存储不同类型的值。例如,如果我们在JavaScript代码中定义了一个名为x的变量,那么x的初始值可以为数字,然后在执行过程中被重新赋值为字符串,JavaScript的运行环境会负责自动识别该变量中存储了什么类型的数据。
  • 多范式编程。JavaScript虽然在语法上与Java、C/C++非常类似(例如if-elseswitch条件语句,whilefor循环语句等),但在内在的设计上,它更接近Self和Scheme这一类语言。也就是说,它既支持面向对象编程,也支持指令式编程和函数式编程,因而具有极为灵活的表达能力。
  • 单线程执行。由于最初脱胎于Web浏览器,JavaScript一直习惯采用单线程的执行模式(尽管如今有了支持多线程的Worker组件),这一习惯即使到了Node.js运行环境中也没变。采用单线程的最大好处是不用像多线程编程那样处理很容易产生bug的同步问题,这就从根本上避免了死锁问题,也避免了线程上下文交换所带来的性能上的开销。当然,单线程的执行方式也有它自身的弱点。例如,它无法充分发挥多核处理器的性能、一个错误就会导致整个程序崩溃,以及执行大量计算时会因长期占用处理器而影响其他异步I/O的执行。
  • 事件驱动。在Web开发领域,JavaScript之所以能在浏览器端扮演越来越重要的角色,很大程度上得益于其具有与桌面应用相似的事件驱动模型。当然,这种编程模型虽然具有轻量级、松耦合等优势,但在多个异步任务的场景下,由于程序中的各个事件是彼此独立的,因此它们之间的协作就成了一个需要费心解决的问题。
  • 异步编程。在目前流行的Vue、React等JavaScript前端框架以及Node.js运行环境提供的接口中,我们可以很容易地观察到其大部分操作都是以异步调用的方式进行的,而这些异步调用往往以回调函数的形式存在,这成了使用JavaScript编程的一大特色。不过,虽然大家都认为回调函数是执行异步调用并接收其返回数据的最佳方式,但这种方式会导致代码的编写顺序与其具体执行顺序不一致。对于很多习惯同步编程方式的人来说,阅读这样的代码会是一个不小的挑战。另外在流程控制方面,由于程序中穿插了各种异步方法和回调函数,因此代码在可读性上也远没有常规的同步方式那么一目了然,这也会给程序的调试和维护工作带来一定的麻烦。