高级程序设计第四版

第二章

  • 网页中所有内嵌 script 标签是按照顺序依次执行,如果遇到外部的同步 script,会阻塞网页解析

0 面试思考题

1. script标签中 integrity属性的作用
2. crossorigin 属性如何携带凭据?
3. defer属性的作用,defer属性加载的脚本执行顺序如何?
4. async属性的作用,async属性加载的脚本执行顺序如何?
5. JS动态加载脚本前,如何让浏览器预加载?

1 script 的 8 个属性

  1. async:可选。只对外部脚本文件有效,异步请求脚本
  2. charset:可选。指定的代码字符集,很少使用
  3. crossorigin:可选。配置相关请求的 CORS(跨源资源共享)设置。默认不使用 CORS
  4. defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效
  5. integrity:可选。验证资源签名,很少使用
  6. language:废弃。
  7. src:可选。表示包含要执行的代码的外部文件。
  8. type:可选。目前都是"text/javascript",如果是 ES6,需要设置为 module,才能使用 import 和 export

2 async 和 defer

1 async 异步加载资源,谁先加载完毕之后,就先执行,并不会按着 script 在页面中的顺序来执行,脚本的加载不计入 DOMContentLoaded 事件统计中

2 defer 异步加载资源,加载完毕之后会等待文档解析完成,即最后的</html>,也就是 DOMContentLoaded 之前按照顺序依次执行对应的脚本资源,全部执行完毕之后,才会执行 DOMContentLoaded,但是书中说的“在实际当中,推迟执行的脚本不一定总会按顺序执行或者在 DOMContentLoaded 事件之前执行,因此最好只包含一个这样的脚本”,目前没有遇到过

3 图例说明

graph

  • 使用 <script> ,脚本下载和执行会阻塞 HTML 文档的解析。
  • 使用 <script defer> ,脚本下载与 HTML 解析并行,等 HTML 解析完成后没脚本都会有序执行。
  • 使用 <script async> ,脚本下载与 HTML 解析并行,但一旦脚本加载完成,就会中断 HTML 解析,同时执行脚本。
  • <script type ="module"> 的行为类似于 <script defer> ,但是模块依赖关系也会被下载,
  • <script type ="module" async> 的行为类似于 <script async> ,额外的模块依赖关系也会被下载

4 integrity

  • 允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容
  • integrity 属性是资源完整性规范的一部分,它允许你为 script 提供一个 hash,用来进行验签,检验加载的 JavaScript 文件是否完整
<script
  crossorigin="anonymous"
  integrity="sha256-PJJrxrJLzT6CCz1jDfQXTRWOO9zmemDQbmLtSlFQluc="
  src="https://assets-cdn.github.com/assets/frameworks-3c926bc6b24bcd3e820b3d630df4174d158e3bdce67a60d06e62ed4a515096e7.js"
></script>

上面的代码来自 github 源码,integrity="sha256-PJJrxrJLzT6CCz1jDfQXTRWOO9zmemDQbmLtSlFQluc=" 告诉浏览器,使用 sha256 签名算法对下载的 js 文件进行计算,并与 intergrity 提供的摘要签名对比,如果二者不一致,就不会执行这个资源

intergrity 的作用有:

  • 减少由【托管在 CDN 的资源被篡改】而引入的 XSS 风险
  • 减少通信过程资源被篡改而引入的 XSS 风险(同时使用 https 会更保险)
  • 可以通过一些技术手段,不执行有脏数据的 CDN 资源,同时去源站下载对应资源

注意:启用 SRI 策略后,浏览器会对资源进行 CORS 校验,这就要求被请求的资源必须同域,或者配置了 Access-Control-Allow-Origin 响应头

5 crossorigin

  • 配置相关请求的 CORS(跨源资源共享)设置。默认不使用 CORS。crossorigin= "anonymous"配置文件请求不必设置凭据标志。crossorigin="use-credentials"设置凭据标志,意味着出站请求会包含凭据
  • crossorigin 的属性值可以是 anonymous、use-credentials,如果没有属性值或者非法属性值,会被浏览器默认做 anonymous
  • crossorigin 的作用有三个
    1. crossorigin 会让浏览器启用 CORS 访问检查,检查 http 相应头的 Access-Control-Allow-Origin
    2. 对于传统 script 需要跨域获取的 js 资源,控制暴露出其报错的详细信息
    3. 对于 module script ,控制用于跨域请求的凭据模式

当 crossorigin 属性如下时,则表示不带凭证,即请求头不带 Cookie

<script crossorigin>
<script crossorigin="">
<script crossorigin="anonymous">

当 crossorigin 属性如下时,则表示带凭证,即请求头带 Cookie

<script crossorigin="use-credentials">

此时,资源的响应头 Access-Control-Allow-Origin 不能为通配符 *,且必须设置 Access-Control-Allow-Credentials 为 true

res.setHeader('Access-Control-Allow-Credentials', true)

应用,比如 bootstrap4 的官网

<script
  src="https://code.jquery.com/jquery-3.1.1.slim.min.js"
  integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n"
  crossorigin="anonymous"></script>

针对非同源的资源,如果在没有添加 crossorigin 属性的情况下,资源的 JS 报错了,是不能拿到具体的错误信息的,只有在设值了 crossorigin 之后才能获取到具体的错误信息,但是无论如何,错误信息都会在控制台打印

// 主站域名
my.com

// 请求资源: 此时index.js中报错是不能拿到具体的错误信息
<script src="user.com/index.js"></script>
// 比如错误信息如下
	Arguments(5) ["Script error.", "", 0, 0, null, callee: ƒ, Symbol(Symbol.iterator): ƒ]
	0: "Script error."
	1: ""
	2: 0
	3: 0
	4: null

// 请求资源: 此时index.js中报错就可以拿到具体的错误信息了
<script src="user.com/index.js" crossorigin ></script>
// 比如错误信息如下
	Arguments(5) ["Uncaught ReferenceError: a is not defined", "http://x/x/x.js", 4, 23, ReferenceError: a is not defined
		at http://x/x/x.js:4:23, callee: ƒ, Symbol(Symbol.iterator): ƒ]
	0: "Uncaught ReferenceError: a is not defined"
	1: "http://x/x/x.js"
	2: 4
	3: 23
	4: ReferenceError: a is not defined at http://x/x/x.js:4:23

扩展知识

  • 在 html5 的标签中,有些标签时自带跨域功能的,比如<audio>、<img>、<link>、<script>、<video> 标签,他们的 src 属性可以是任意源的链接,并且均可以进行加载,即:都有一个跨域属性 (crossOrigin property),它允许你配置元素获取数据的 CORS 请求
  • 如果在标签中添加了 anonymous 属性,那么浏览器再去解析这些跨域资源的时候,就不会以它的自带跨域功能去加载了,而是 使用 CORS 的方式加载,就像我们的 ajax 一样,需要服务器设置跨域头,才可以完成加载,否则会报跨域问题,导致加载失败
关键字描述
anonymous对此元素的 CORS 请求将不设置凭据标志
use-credentials对此元素的 CORS 请求将设置凭证标志;这意味着请求将提供凭据
""设置一个空的值,如 crossorigin 或 crossorigin="",和设置 anonymous 的效果一样

第三章

1 语法

  1. 区分大小写:ECMAScript 中一切都区分大小写。无论是变量、函数名还是操作符,都区分大小写
  2. 标识符:所谓标识符,就是变量、函数、属性或函数参数的名称,标识符可以由一或多个下列字符组成:
  • 第一个字符必须是一个字母、下划线(_)或美元符号($)
  • 剩下的其他字符可以是字母、下划线、美元符号或数字
  1. 注释:// 单行注释 /_ 这是多行注释 _/
  2. defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效
  3. integrity:可选。验证资源签名,很少使用
  4. language:废弃。
  5. src:可选。表示包含要执行的代码的外部文件。
  6. type:可选。目前都是"text/javascript",如果是 ES6,需要设置为 module,才能使用 import 和 export

2 关键字和保留字

ECMA-2626 版规定的所有关键字如下:
break do in typeof
case else instanceof var
catch export new void
class extends return while
const finally super with
continue for switch yield
debugger function this
default if throw
delete import try

以下是 ECMA-2626 版为将来保留的所有词汇。
始终保留:
	enum
严格模式下保留:
	implements package public
	interface protected static
	let private
模块代码中保留:
	await

3 最佳实践

  • 有 3 个关键字可以声明变量:var、const 和 let,声明变量推荐使用 let 和 const
  • 语句使用分号结尾 let diff = a - b;
  • 条件语句须有大括号 js if (test) { console.log(test); }