Skip to content

js读书笔记-什么是JavaScript

引言

由于面试的时候老是发现自己的 js 底层基础原理还有所欠缺,为了能够彻底的理解 js 底层原理和基础,所以在网上看了大家的推荐,决定从今天开始阅读这本 js 的红宝书--《JavaScript高级程序设计》,后续看完的话可能还会再看看几本关于 js 最佳实践的书

什么是 JavaScript

为什么会诞生JS

随着 Web 日益流行,对客户端脚本语言的需求也越来越强烈。当时,大多数用户使用 28.8kbit/s 的调制解调器上网,但网页变得越来越大、越来越复杂。为验证简单的表单而需要大量与服务器的往返通信成为用户的痛点。想象一下,你填写完表单,单击“提交”按钮,等 30 秒处理,然后看到一条消息, 告诉你有一个必填字段没填。

在当时主要是为了做数据验证等,避免缓慢的刷新折磨用户的耐心,当然随着时代的发展,js有了更多的作用,渗透到浏览器的窗口和内容的方方面面。

JavaScript 实现

虽然 JavaScript 和 ECMAScript 基本上是同义词,但 JavaScript 远远不限于 ECMA-262( 欧洲计算机制造商协会创造的标准 ) 所定义的那样。 没错,完整的 JavaScript 实现包含以下几个部分:

  • 核心(ECMAScript)
  • 文档对象模型(DOM)
  • 浏览器对象模型(BOM)

ECMAScript

ECMAScript 并不涉及浏览器,主要是在基准层面去定义和描述语言

ECMAScript,即 ECMA-262 定义的语言,并不局限于 Web 浏览器。事实上,这门语言没有输入和 输出之类的方法。ECMA-262 将这门语言作为一个基准来定义,以便在它之上再构建更稳健的脚本语言。

Web 浏览器只是 ECMAScript 实现可能存在的一种宿主环境(host environment)。宿主环境提供 ECMAScript 的基准实现和与环境自身交互必需的扩展。扩展(比如 DOM)使用 ECMAScript 核心类型 和语法,提供特定于环境的额外功能。其他宿主环境还有服务器端 JavaScript 平台 Node.js 和即将被淘汰 的 Adobe Flash。

所以 ECMA 并不涉及浏览器,主要是在基准层面去定义和描述语言:

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 全局对象
版本迭代

这里只说明几个比较重要的版本

  • ECMA-262 第 3 版第一次真正对这个标准进行更新,更新了字符串处理、错误定义和数值输出。此 外还增加了对正则表达式、新的控制语句、try/catch 异常处理的支持,以及为了更好地让标准国际化 所做的少量修改。对很多人来说,这标志着 ECMAScript 作为一门真正的编程语言的时代终于到来了。
  • ECMA-262 第 6 版,俗称 ES6、ES2015 或 ES Harmony(和谐版),于 2015 年 6 月发布。这一版包 含了大概这个规范有史以来最重要的一批增强特性。ES6 正式支持了类、模块、迭代器、生成器、箭头 函数、期约( promise )、反射、代理和众多新的数据类型。
  • ECMA-262 第 8 版,也称为 ES8、ES2017,完成于 2017 年 6 月。这一版主要增加了异步函数(async/ await)、SharedArrayBuffer 及 Atomics API,以及 Object.values()/Object.entries()/Object. getOwnPropertyDescriptors()和字符串填充方法,另外明确支持对象字面量最后的逗号。
ECMAScript 的符合性

ECMA-262 阐述了什么是 ECMAScript 符合性。要成为 ECMAScript 实现,必须满足下列条件:

  • 支持 ECMA-262 中描述的所有“类型、值、对象、属性、函数,以及程序语法与语义”;
  • 支持 Unicode 字符标准。

此外,符合性实现还可以满足下列要求。

  • 增加 ECMA-262 中未提及的“额外的类型、值、对象、属性和函数”。ECMA-262 所说的这些额 外内容主要指规范中未给出的新对象或对象的新属性。
  • 支持 ECMA-262 中没有定义的“程序和正则表达式语法”(意思是允许修改和扩展内置的正则表 达式特性)。

以上条件为实现开发者基于 ECMAScript 开发语言提供了极大的权限和灵活度,也是其广受欢迎的原因之一。

DOM

文档对象模型(DOM,Document Object Model)是一个应用编程接口(API),用于在 HTML 中使 用扩展的 XML。DOM 将整个页面抽象为一组分层节点。HTML 或 XML 页面的每个组成部分都是一种 节点,包含不同的数据。

举个例子:

html
<html>
    <head>
        <title>Sample Page</title>
    </head>
    <body>
        <p> Hello World!</p>
    </body>
</html>

转化后:

DOM 通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构。使用 DOM API,可以轻松地删除、添加、替换、修改节点。

为什么DOM是必须的
  • 可以做到不刷新页面而修改页面外观和内容
DOM 级别

DOM 并非只能通过 JavaScript 访问,而且确实被其他很多语言实现了。不过对于浏 览器来说,DOM 就是使用 ECMAScript 实现的,如今已经成为 JavaScript 语言的一大组成 部分。

DOM主要由两个模块组成 DOM Core 和 DOM HTML:

  • DOM Core 提供了一种映射 XML 文档,从而方便访问和操作文档任意部分的方式
  • DOM HTML 扩展了前者,并增加了特定于 HTML 的对象和方法。

BOM

浏览器对象模型(BOM) API,用于支持访问和操作浏览器的窗口。使用 BOM,开发者可以操控浏览器显示页面之外的部分。而 BOM 真正独一无二的地方,当然也是 问题最多的地方,就是它是唯一一个没有相关标准的 JavaScript 实现。

HTML5 改变了这个局面,这个版 本的 HTML 以正式规范的形式涵盖了尽可能多的 BOM 特性。由于 HTML5 的出现,之前很多与 BOM 有关的问题都迎刃而解了。

总体来说,BOM 主要针对浏览器窗口和子窗口(frame),不过人们通常会把任何特定于浏览器的 扩展都归在 BOM 的范畴内。比如,下面就是这样一些扩展:

  • 弹出新浏览器窗口的能力;
  • 移动、缩放和关闭浏览器窗口的能力;
  • navigator 对象,提供关于浏览器的详尽信息;
  • location 对象,提供浏览器加载页面的详尽信息;
  • screen 对象,提供关于用户屏幕分辨率的详尽信息;
  • performance 对象,提供浏览器内存占用、导航行为和时间统计的详尽信息;
  • 对 cookie 的支持;
  • 其他自定义对象,如 XMLHttpRequest 和 IE 的 ActiveXObject。

小结

JavaScript 是一门用来与网页交互的脚本语言,包含以下三个组成部分。

  • ECMAScript:由 ECMA-262 定义并提供核心功能。
  • 文档对象模型(DOM):提供与网页内容交互的方法和接口。
  • 浏览器对象模型(BOM):提供与浏览器交互的方法和接口。

JavaScript 的这三个部分得到了五大 Web 浏览器(IE、Firefox、Chrome、Safari 和 Opera)不同程度的支持。所有浏览器基本上对 ES5(ECMAScript 5)提供了完善的支持,而对 ES6(ECMAScript 6)和 ES7(ECMAScript 7)的支持度也在不断提升。这些浏览器对 DOM 的支持各不相同,但对 Level 3 的支 持日益趋于规范。HTML5 中收录的 BOM 会因浏览器而异,不过开发者仍然可以假定存在很大一部分 公共特性。

HTML 中的 JavaScript

`<script>`元素

将 JavaScript 插入 HTML 的主要方法是使用 <script> 元素。这个元素是由网景公司创造出来,并 最早在 Netscape Navigator 2 中实现的。后来,这个元素被正式加入到 HTML 规范。 <script> 元素有下 列 8 个属性。

  • async: 可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效。
  • charset: 可选。使用 src 属性指定的代码字符集。这个属性很少使用,因为大多数浏览器不在乎它的值。
  • crossorigin: 可选。配置相关请求的 CORS(跨源资源共享)设置。默认不使用 CORS。crossorigin= "anonymous" 配置文件请求不必设置凭据标志。crossorigin="use-credentials" 设置凭据标志,意味着出站请求会包含凭据。
  • defer: 可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。在 IE7 及更早的版本中,对行内脚本也可以指定这个属性。
  • integrity: 可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI, 12 Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容
  • language: 废弃。最初用于表示代码块中的脚本语言(如"JavaScript"、"JavaScript 1.2" 或"VBScript")。大多数浏览器都会忽略这个属性,不应该再使用它。
  • src: 可选。表示包含要执行的代码的外部文件。
  • type: 可选。代替 language,表示代码块中脚本语言的内容类型(也称 MIME 类型)。按照惯 例,这个值始终都是"text/javascript",尽管"text/javascript"和"text/ecmascript" 都已经废弃了。JavaScript 文件的 MIME 类型通常是"application/x-javascript",不过给 type 属性这个值有可能导致脚本被忽略。在非 IE 的浏览器中有效的其他值还有 "application/javascript"和"application/ecmascript"。如果这个值是 module,则代 码会被当成 ES6 模块,而且只有这时候代码中才能出现 import 和 export 关键字。

src注意:按照惯例,外部 JavaScript 文件的扩展名是.js。这不是必需的,因为浏览器不会检 查所包含 JavaScript 文件的扩展名。这就为使用服务器端脚本语言动态生成 JavaScript 代 码,或者在浏览器中将 JavaScript 扩展语言(如 TypeScript,或 React 的 JSX)转译为 JavaScript 提供了可能性。不过要注意,服务器经常会根据文件扩展来确定响应的正确 MIME 类型。 如果不打算使用.js 扩展名,一定要确保服务器能返回正确的 MIME 类型。

另外,使用了 src 属性的 <script> 元素不应该再在 <script></script> 标签中再包含其他 JavaScript 代码。如果两者都提供的话,则浏览器只会下载并执行脚本文件,从而忽略行内代码。

不管包含的是什么代码,浏览器都会按照 <script>在页面中出现的顺序依次解释它们,前提是它 们没有使用 defer 和 async 属性。第二个 <script> 元素的代码必须在第一个 <script> 元素的代码解 释完毕才能开始解释,第三个则必须等第二个解释完,以此类推。

defer 和 async

defer

该属性表示脚本在执行的时候不会改 变页面的结构。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在 <script> 元素上 设置 defer 属性,相当于告诉浏览器立即下载,但延迟执行。

HTML5 规范要求脚本应该按照它们出现的顺序执行,因此第一个推迟的脚 本会在第二个推迟的脚本之前执行,而且两者都会在 DOMContentLoaded 事件之前执行

async

从改变脚本处理方式上看,async 属性与 defer 类似。当然,它们两者也都只适用于外部脚本,都会告诉浏览器立即开始下载。不过,与 defer 不同的 是,标记为 async 的脚本并不保证能按照它们出现的次序执行。

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

异步脚本保证会在页面的 load 事件前执行,但可能会在 DOMContentLoaded(参见第 17 章)之 前或之后,好的 Web 实践不推荐使用

动态加载 script 脚本

就是用 document.createElement 去实现,但是以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它们在资源获取队列中的优先级。根据应用程序的工作方式以及怎么使用,这种方式可能会严重影响性能。要想让预加载器知道这些 动态请求文件的存在,可以在文档头部显式声明它们:

HTML:
html
<link rel="preload" href="gibberish.js">
JS:
js
let script = document.createElement('script');
script.src = 'gibberish.js';
script.async = false;
document.head.appendChild(script);

行内文件和外部文件

虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代 码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下:

  • 可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存 所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑 代码。
  • 缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都 用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。
  • 适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。 包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。

在配置浏览器请求外部文件时,要重点考虑的一点是它们会占用多少带宽。在 SPDY/HTTP2 中, 预请求的消耗已显著降低,以轻量、独立 JavaScript 组件形式向客户端送达脚本更具优势。

比如,第一个页面包含如下脚本:

html
    <script src="mainA.js"></script>
    <script src="component1.js"></script>
    <script src="component2.js"></script>
    <script src="component3.js"></script>
    ...

后续页面可能包含如下脚本:

html
    <script src="mainB.js"></script>
    <script src="component3.js"></script>
    <script src="component4.js"></script>
    <script src="component5.js"></script>
    ...

在初次请求时,如果浏览器支持 SPDY/HTTP2,就可以从同一个地方取得一批文件,并将它们逐个 放到浏览器缓存中。从浏览器角度看,通过 SPDY/HTTP2 获取所有这些独立的资源与获取一个大 JavaScript 文件的延迟差不多。

在第二个页面请求时,由于你已经把应用程序切割成了轻量可缓存的文件,第二个页面也依赖的某 些组件此时已经存在于浏览器缓存中了。

当然,这里假设浏览器支持 SPDY/HTTP2,只有比较新的浏览器才满足。如果你还想支持那些比较 老的浏览器,可能还是用一个大文件更合适。

ps:HTTP2 支持多路复用和二进制分帧,而 HTTP1 如果多个独立轻量的文件要多次建立请求,HTTP1.1的话也是比较建议多个,因为长连接。

`<noscript>`

<noscript> 元素可以包含任何可以出现在 <body> 中的 HTML 元素, <script> 除外。在下列两种 情况下,浏览器将显示包含在 <noscript> 中的内容:

  • 浏览器不支持脚本;
  • 浏览器对脚本的支持被关闭。 任何一个条件被满足,包含在 <noscript> 中的内容就会被渲染。否则,浏览器不会渲染 <noscript> 中的内容。

下面是一个例子:

html
<!DOCTYPE html>
<html>
<head>
    <title>Example HTML Page</title>
    <script defer="defer" src="example1.js"></script> 
    <script defer="defer" src="example2.js"></script> 
</head>
<body>
    <noscript>
        <p>This page requires a JavaScript-enabled browser.</p> 
    </noscript>
</body>
</html>

这个例子是在脚本不可用时让浏览器显示一段话。如果浏览器支持脚本,则用户永远不会看到它。

小结

JavaScript 是通过 <script> 元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到 HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。本章的重点可 以总结如下:

  • 要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同 一台服务器上,也可以位于完全不同的域。
  • 所有 <script> 元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的 情况下,包含在 <script> 元素中的代码必须严格按次序解释。
  • 对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面 的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签 之前。
  • 可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出 的次序执行。
  • 可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异 步脚本不能保证按照它们在页面中出现的次序执行。
  • 通过使用 <noscript> 元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启 用脚本,则 <noscript> 元素中的任何内容都不会被渲染。

备案号:闽ICP备2024028309号-1