跳到主要内容

相关概念

JavaScript 由 核心 ( ECMAScript )、文档对象模型 ( DOM )、浏览器对象模型( BOM ) 3 部分组成。

核心

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 对象

文档对象模型

文档对象模型( Document Object Model,DOM )是针对 XML 但经过扩展用于 HTML 的应用程序编程接口。 DOM 把整个页面映射成一个多层节点结构。 HTML 或 XML 每一部分都是某个类型的节点。

这些节点又包含了不同类型的数据。

DOM 1 级( DOM Lever 1 )于 1998 年 10 月成为 W3C 标准,由 DOM 核心和 DOM HTML 两个模块组成。

DOM 核心规定的是如何映射基于 XML 的文档结构,以简化对文档中任意部分的访问和操作。

DOM 2 级的目标则宽泛多了,在原来的 DOM 基础上又扩充了鼠标和用户界面事件、范围、遍历(迭代 DOM 文档的方法),而且通过对象接口增加了对 css 的支持。

DOM 2 级引入以下模块:

  • DOM 视图 ( DOM Views ):定义了跟踪不同文档(如应用 css 之前和之后的文档)视图的接口
  • DOM 事件 ( DOM Event ):定义了事件和事件的处理接口
  • DOM 样式 ( DOM Style ):定义了基于 css 为元素应用样式的接口
  • DOM 遍历和范围( DOM Traversal and Range ) : 定义了遍历和操作文档树的接口

DOM 3 级进一步扩展了 DOM ,引进了以统一方式加载和保存文档的方法,在 DOM 加载和保存( DOM Load and Save )模块定义。 DOM 3 级也对 DOM 的核心进行了扩展,开始支持 XML1.0 规范,涉及 XML Infoset 、 XPath 和 XML Base 。

浏览器对象模型

IE 3.0 和 Netscape Navigator 3.0 提供了一种特性,即 BOM (浏览器对象模型),可以对浏览器窗口进行访问和操作。使用 BOM ,可以移动窗口、改变状态栏中的文本以及执行其它的页面内容与不直接相关的动作。

与 DOM 不同的是, BOM 只是 JavaScript 的一部分,没有相关的标准。

BOM 主要处理浏览器窗口和框架,不过通常浏览器特定的 JavaScript 扩展都被看做 BOM 的一部分,这些包括:

  • 弹出新的浏览器窗口
  • 移动、关闭浏览器窗口以及调整窗口的大小
  • 提供 Web 浏览器详细的信息的定位对象
  • 提供用户分辨率详细信息的屏幕对象
  • 对 cookie 的支持
  • IE 扩展了 BOM ,加入了 ActiveX Object 类,可以通过 JavaScript 实例化 ActiveX 对象

嵌入 js

通过 标签引入, html 为 js 定义的 6 个属性 。

  • async 可选,表示立即下载脚本,但不应妨碍页面的其它操作

  • charset 可选,表示 src 的字符集,大多数浏览器忽略该值,一般也不用添加

  • crossorigin :可选。配置相关请求的 CORS (跨源资源共享)设置。默认不使用 CORS 。 crossorigin= "anonymous" 配置文件请求不必设置凭据标志。 crossorigin="use-credentials" 设置凭据 标志,意味着出站请求会包含凭据

  • defer 可选,脚本延迟到文档完全被解析和显示后执行

  • integrity :可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性( SRI , Subresource Integrity )。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错, 脚本不会执行。这个属性可以用于确保内容分发网络( CDN , Content Delivery Network )不会提 供恶意内容

  • language 已废弃

  • src 可选

  • type 可选,可以看成是 language 的替代属性

浏览器在解析这个资源时,会向 src 属性指定的路径发送一个 GET 请求,以取得相应资源,假定 是一个 JavaScript 文件。这个初始的请求不受浏览器同源策略限制,但返回并被执行的 JavaScript 则受限制。当然,这个请求仍然受父页面 HTTP/HTTPS 协议的限制。

加载顺序

HTML 文档在浏览器中的解析过程是:按文档流的先后顺序逐步解析页面的结构和信息。 JavaScript 代码是嵌入的脚本也算是 HTML 的一部分,所以, 依次序加载,除非设置 defer 和 async 属性。

延迟加载

设置 defer 属性,使脚本下载后延迟执行。 最好只包含一个一个延迟脚本

在 XHTML 类型的文件中, defer 属性应该定义为 defer="defer" 。 defer 属性只适用于外部脚本文件。

设置异步响应

设置 async 属性,但不保证执行顺序。 同 defer 属性类似,都只用于外部脚本。

指定 async 的目的是不让页面等待两个脚本文件下载完后再执行,从而异步执行页面的其它内容。

给 脚本添加 async 属性的目的是告诉浏览器,不必等脚本下载和执行完后再加载页面,同样也不必等到 该异步脚本下载和执行后再加载其它脚本。正因为如此,异步脚本不应该在加载期间修改 DOM 。

执行 JavaScript 程序

JavaScript 解释过程分为两个阶段:预处理(也称预编译)和执行。在预编译阶段, JavaScript 解释器将完全对 JavaScript 代码的预处理操作,把 JavaScript 代码转化为字节码;在执行期间,JavaScript 解释器把字节码生成二进制机械码,并顺序执行,完成程序设计的任务。

预编译

当 JavaScript 引擎解析脚本时,它会在预编译时对所有声明的变量 和 函数 预处理。

声明变量和函数可在文档的任意位置,但是最好在所有的 Javascript 的代码之前声明所有的全局变量和函数,并对变量进行赋值操作。

预编译包括词法分析和语法分析。词法分析主要对 JavaScript 脚本进行逐一分析,检查脚本是否符合 JavaScript 规范,是否存在语法错误;词法分析主要把程序收集的信息储存到数据结构中,如符号表和语法树。

  • 符号表:储存程序中所有符号的一个表,包括所有的字符串、直接量、变量名和函数名等
  • 构建程序结构的一个树形表示,并将使用这个树形结构来生成只间代码

变量声明在预编译期间被处理的,在执行期间对所有代码来说,都是可见的。但是,代码的初始化过程发生在执行期间,而不是预编译。

代码块

用 标签分开的代码段。

JavaScript 解释器在执行脚本时,是按照块执行的。浏览器在解析 HTML 文档时,如果遇到一个 标签,则 JavaScript 解释器会等这个代码块都加载完后,先对代码块进行预编译,然后再执行。执行完后才能够继续解析下面的 HTML 文档流,同时 JavaScript 解释器也准备好处理下一个代码块。

JavaScript 是按块执行的,但是不同块属于同一个全局作用域,块之间的变量和函数是可以共享的。

在执行一个 JavaScript 块中调用代码后面的代码中声明的变量或是函数就会提示语法错误。

响应事件

JavaScript 响应操作是通过事件驱动的模式来实现的,由于事件发生的不确定性,所以 JavaScript 事件的响应顺序也是不确定的。

onload 事件只有在文档加载完毕才会响应。因此为了运行安全,一般设计都会在页面初始化之后才允许 JavaScript 代码执行,这样就避免了代码加载延迟对 JavaScript 执行的影响。同时也避开了 HTML 文档流对于 JavaScript 执行的限制。

设计动态脚本

使用 console.log 方法输出 JavaScript 脚本时,这些动态输出的脚本执行顺序也不同。

运行代码时, console.log() 方法先将输出的 JavaScript 字符串写入到标签所在的文档位置,浏览器解析完后 console.log() 输出的内容,然后按照顺序解析后面的 HTML 文档。

当然,在把 HTMLElement 元素添加到 DOM 且执行到这段代码之前不会发送请求。默认情况下, 以这种方式创建的 元素是以异步方式加载的,相当于添加了 async 属性。不过这样做可能会有问题,因为所有浏览器都支持 createElement() 方法,但不是所有浏览器都支持 async 属性。因此, 如果要统一动态脚本的加载行为,可以明确将其设置为同步加载

使用 console.log() 方法输出的脚本在执行时存在一定风险,因为不同的 JavaScript 引擎对其执行的顺序不同,同时不同的浏览器在解析时也不出现各种 Bug 。

语法糖

用 JavaScript 打印页面

window.print();

window.print()属于浏览器内置的 API ,可以直接打印页面。

语法规则

空白

空白包括空格键插入的空格、 Tab 键插入的缩进以及回车(也就是 Enter 键换行)

多行书写

一条语句必须在一行内完成,但是如果一行代码过长,则可以采用多行书写方式,由于 JavaScript 将分号 " ; "作为语句之间的分隔符,所以只需使用空白就可以完成多行书写方式

点语法

. 用来标明与某个对象相关的属性和方法,也用于标识变量的目标路径。点语法表达式由对象名开始,接着是一个点,紧跟的是要指定的属性、方法或者变量

花括号

花括号 { } 分块

条件语句、循环语句也经常用到花括号进行分块; Object 对象也可以使用花括号,但它不是分块,而是对象的初始化。

分号

分号作为一条语句的分隔符

一般情况下,一行就是一条 JavaScript 语句,不需要添加分号作为结尾,因为解释引擎会在行尾自动添加一个分号。

不过这种情况有两个例外:一个是当插入的分号将作为一条空语句时,那么不会被自动插入;另一个是当插入的分号作为 for 语句中括号内两个分号之一时不会被自动插入。

圆括号

当定义一个函数时,要把参数放在圆括号中

当调用一个函数时,也要把要传递的参数放在圆括号中。

圆括号还可以用来改变 JavaScript 运算符的优先级或者使编写的 JavaScript 程序更容易理解。

字母的大小写

变量和对象都是区分大小写的。

程序注释

当程序语句较多时,为了阅读和后期管理的方便,在程序中加入解释性的注释是十分必要的。而且添加注释有助于合作开发者更好地理解编写的程序,从而提高工作效率。

关键字

JavaScript 保留一些单词用于特定的用途,因此,不能用这些保留字作为变量名、函数名或者标签名。

命名方法

Pascal Notation (帕斯卡命名法)

帕斯卡命名法规定所有单词的第一个字母大写,其它字母小写。经常被用在类、接口的声明中,例如, HelloWorld 就可以作为一个类名,而接口名经常在前面加一个大写字母 I ,例如 IHelloWorld 。

Camel Notation (驼峰命名法)

驼峰命名法规定除了第一个单词外,所有单词第一个字母大写,其它字母小写。变量、函数、方法、属性等基本都采用这种命名方法。

Hungarian Notation (匈牙利命名法)

前两种命名法现在使用得最多,大部分流行的语言,例如 Java 、 C# 等,都使用帕斯卡命名法或驼峰命名法。而对于传统的 Windows 编程,例如 C 、 C++ 等,则较多使用匈牙利命名法。这种命名法因为由一名匈牙利程序员最初归纳而得名,。

变量名 = 特性 + 描述 特性就是一个字母前缀,用于指示作用域、类型等信息(这一部分可以多个同时连用),然后是变量的功能描述信息。功能描述信息是首字母大写的一个或多个单词的组合,该单词往往要指明变量的用途。

重构

重构,即重新优化代码行的行为。在实际应用中,应该尽量避免使用较大的源文件,如果一个源文件里的代码超过 500 行(甚至更低),则必须考虑将代码合理地分割到不同的类中。

方法也是同样的道理,一个典型的方法代码应该在 30 行以内,这样,在大多数显示器内的一屏中就可以尽览该方法的全部代码。如果一个方法的代码超过 30 行,应该考虑将其分解为不同的方法。但也不能过度分解,否则将适得其反,会造成阅读困难。当分解方法时,要注意让每个方法仅完成一项任务,这样也有利于方法的重用。

XHTML 中的变化

可扩展超文本标记语言( XHTML , Extensible HyperText Markup Language )是将 HTML 作为 XML 的应用重新包装的结果。与 HTML 不同,在 XHTML 中使用 JavaScript 必须指定 type 属性且值为 text/javascript , HTML 中则可以没有这个属性。 XHTML 虽然已经退出历史舞台,但实践中偶尔 可能也会遇到遗留代码。

在 XHTML 中编写代码的规则比 HTML 中严格,这会影响使用 元素嵌入 JavaScript 代码。 HTML 中,解析 元素会应用特殊规则。 XHTML 中则没有这些规则。这意味着 a < b 语句中的小于号( < )会被解释成一个标签的开始,并且由于作为标签开始的小于号后面不能有空格, 这会导致语法错误。

是把所有代码都包含到一个 CDATA 块中。 XHTML (及 XML )中, CDATA 块表示 文档中可以包含任意文本的区块,其内容不作为标签来解析,因此可以在其中包含任意字符,包括小于号,并且不会引发语法错误。 在兼容 XHTML 的浏览器中,这样能解决问题。但在不支持 CDATA 块的非 XHTML 兼容浏览器中 则不行。为此, CDATA 标记必须使用 JavaScript 注释来抵消。

// <![CDATA[
function compare(a, b) {
if (a < b) {
console.log('A is less than B');
} else if (a > b) {
console.log('A is greater than B');
} else {
console.log('A is equal to B');
}
}
// ]]>