历史

  • 1995.05:Netscape 公司的 Brendan Eich 用 10 天设计完成了语言的初版(Mocha)
  • 1996.12:JavaScript 被提交给 ECMA,并成立 TC39 技术委员会负责制定标准
  • 1997.09:正式发布《ECMA-262 第 1 版》(ECMAScript 1.0)
  • 1998.07:ECMAScript 2.0 发布
  • 1999.12:ECMAScript 3.0 发布
  • 2007 - 2008:ES3.1 和 ES4.0 之间争议过大,最终废除了 4.0 版本
  • 2009.12:ECMAScript 5.0 发布
  • 2011.06:ECMAScript 5.1 发布
  • 2015.06:ECMAScript 6.0 发布(更名为 ECMAScript 2015)
  • 每年发布一个新版本…

JavaScript 二十年

语言特点

  • 动态语言(代码内容在运行时决定,类型检查和转换是在程序执行时进行的,而不是在编译时)
  • 弱类型语言(可以隐式转换)
  • 解释型语言(执行时代码逐句解释)
  • 自动分号插入(ECMAScript® 2025 Language Specification (tc39.es)
  • 严格区分大小写

变量

  • 字母数字_$ 组成,不能以数字开头
  • 硬编码的常量一般使用全大写加下划线的形式:const DEFAULT_AGE = 18
  • 常规命名通常采用驼峰形式:firstSecond
  • 不能以 保留关键字 作为变量名

注释

  • 单行:// xxx
  • 多行:/* xxx */

代码表示 how,注释表示 why

浏览器中 JavaScript 的实现(含义)

  • 核心:ECMAScript
  • 文档对象模型:DOM

在 HTML 中引入 JavaScript

1. 内联 JavaScript

使用 <script></script> 标签插入 <body> 中,script 的执行会阻塞下面的内容加载和显示:

<body>
  ...
  <script></script>
</body>

2. 外部 JavaScript

<script src="script.js"></script> ,如果设置了 src 属性,则 script 标签内部包裹的代码会失效
使用外部引入的好处是浏览器会下载它,然后将它保存到浏览器的缓存中

当浏览器加载 HTML 时遇到 <script>...</script> 标签,浏览器就不能继续构建 DOM,它必须立刻执行此脚本,这会导致两个重要的问题:

  • 脚本不能访问到位于它们下面的 DOM 元素,因此,脚本无法给它们添加处理程序等
  • 如果页面顶部有一个笨重的脚本,它会阻塞页面。在该脚本下载并执行结束前,用户都不能看到页面内容

在外部 JavaScript 的写法下,可以增加两种属性不阻塞页面:

  • async:异步属性,立即在后台下载,但是加载完成后立即执行,与其他脚本不会互相等待,和 DOMContentLoaded 之间也不会互相等待(独立脚本使用)
  • defer:延迟属性,立即在后台下载,但是在 DOM 解析完成之后,DOMContentLoaded 之前执行,所有 defer 的脚本按照出现顺序执行(需要整个 DOM 或加载顺序重要时使用)

3. 动态脚本

可以使用 JavaScript 动态地创建一个脚本,并将其附加(append)到文档(document)中:

let script = document.createElement('script')
script.src = "..."
document.body.append(script)

当脚本被 append 到文档时,脚本就会立即开始加载,先加载完成就先执行
默认是异步的,类似 2 中的 async 属性,可以显式设置 script.async=false 变成类似 defer 的效果,但是不会阻塞 DOMContentLoaded 事件

严格模式

ES5 增加了严格模式:"use strict"; 必须在文件的开头声明,或者在一个函数的开头声明才有效(classmodule 中会自动启用严格模式),用于修改旧有 JS 中不完善的地方。严格模式下:

  • 变量必须先声明再使用,给未经声明的变量赋值会报错:ReferenceError (非严格模式:会创建一个全局变量)
  • 函数声明只在块级作用域中能够访问,非严格模式下则能够全局访问
  • 对不可写的属性等进行写入操作时,会出现错误
  • 八进制不再允许使用前缀 0 表示,必须使用 0o0O
  • eval 有属于自己的词法环境。因此不能从外部访问在 eval 中声明的函数和变量
  • 增加了保留关键字
  • 不能用 with 语句
  • this 的值是 undefined,非严格模式下是 window
  • 非严格模式下违反对象描述符的行为会被忽略(如:对只读的属性进行赋值),但是严格模式下会报错

转译器 和 Polyfill

JavaScript 语言每年都会增加新的功能,为了在旧引擎中使用新的特性,有 2 种方式:

  • 转译器:将源码转译成另一种源码的特殊软件,主要针对从新到旧。如 Babel
  • polyfill:更新/添加新函数的脚本,主要针对从无到有。如 core-js(方法在规范中存在,但是引擎还不支持)

垃圾回收