JavaScript
笔者突然想把所学的JS梳理整理一边,一方面完善自己的不足,另一方面也希望看到这篇文章的人能指出笔者的不足,在此谢过了。(根据红宝书结合自己的想法整理出来,同时也会加上一些相关的面试题)
HTML里的JS
1. <script>
标签
将javascript
插入html
中主要使用的是script
这个标签这个标签有8个属性
async:可选属性。表示可以立刻开始下载脚本,但不能阻止其他页面动作。
只对外部文件有效
charset:可选属性。使用src指定代码字符集。
crossorigin:可选属性。配置相关请求的CROS设置。
defer:可选属性。表示在文档解析和显示完成后在执行脚本是没有问题的。只对外部脚本文件有效
integrity:可选属性。允许比对接收到的资源和指定的加密签名以验证子资源完整性
src:可选属性。表示要执行的外部文件
type:可选属性。代替language表示代码块中脚本语言的内容类型(也称MIME类型)。
language:已废弃
src
script
标签如果使用了src
属性就不能在标签中使用代码,若两者都提供的话,就只会下载脚本而忽略标签中的代码。同时script
的src属性可以是一个完整的url
,而且这个url
指向的资源可以跟包含他的HTML
不是同一个域
<script src="https://www.somewhere.com/afile.js"></script>
同时浏览器在解析这个资源时,会向src
属性指定的路径发送一个GET
请求已取得资源。这个请求不受浏览器同源策略的限制,但返回并被执行的JavaScript
则受限制,同时这个请求仍然受父页面HTTP/HTTPS
协议的限制。
defer
推迟执行脚本:script
元素添加defer属性后,不会改变页面的结构,所以这个脚本可以在整个页面解析完后在执行。设置这个属性会告诉浏览器立即下载脚本,但是最后执行脚本
同时多个设置了defer
属性的script
元素,按照HTML5
规范的要求应该按照顺序执行。
因为不是所有的浏览器都支持defer属性所以,最好把推迟执行的脚本放在页面的底部。
值得注意的是对于XHTML文档指定defer属性时应该写成defer="defer"
async(不推荐使用这个方法)
异步执行脚本:从改变脚本执行方式上来看async与defer类似。两个属性都只适用于外部脚本文件,都会告诉浏览器立刻开始下载文件,但是使用async的脚本文件不一定会按照顺序执行,所以使用async的脚本之间不能存在依赖关系
,给脚本添加async属性的目的就是告诉浏览器,不用等到脚本下载和执行完后在加载页面,同时也不用等到异步脚本下载和执行后再加载其他脚本,所以异步脚本不应该在加载期间修改DOM元素
值得注意的是对于XHTML文档指定defer属性时应该写成async="async"
动态加载脚本
因为JavaScript可以使用DOM的API所以通过向DOM中添加script元素同样可以加载指定脚本。只要创建script元素并添加到DOM中就行
let script = document.createElement(`script`);
script.src = 'gibberish.js';
document.head.appendChild(script);
这样添加到DOM元素后,执行这段代码之前是不会发送请求的。因为默认情况向相当于script元素添加了async属性,但是所有的浏览器都支持createElement()方法,但不是所有的浏览器都支持async,所以要统一动态脚本的加载行为,可以将其设置成同步加载:
let script = document.createElement(`script`);
script.src = 'gibberish.js';
script.async = false;
document.head.appendChild(script);
但是这种方式也是有缺陷的,因为这种方式获取的资源对浏览器预加载器是不可见的,这会严重影响他们在资源获取队列中的优先级。所以要让预加载器知道这些请求文件可以在文档头部显示声明:
<link rel="preload" href="gibberish.js">
2. <noscript>
标签
针对早期浏览器不支持JavaScript
的问题,需要一个优雅降级的处理方案,所以noscript
出现用以对不支持JavaScript
提供替代内容。对于禁用JavaScript
的浏览器有自己的用处。
noscript元
素可以包含任何可以出现在body
中的HTML元素,script
除外。在下列两种情况下,浏览器将显示包含在noscript
中的内容
- 浏览器不支持脚本
- 浏览器对脚本的支持关闭
3. 面试题
简述一下src
与href
的区别
src
用于替换当前元素,href
用于在当前文档和引用资源之间确立联系。src
是source
的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src
资源时会将其指向的资源下载并应用到文档内,例如js
脚本,img
图片和frame
等元素href
是Hypertext Reference
的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,
如果我们在文档中添加
<link href="common.css" rel="stylesheet"/>
那么浏览器会识别该文档为css文件
,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css
,而不是使用@import方式
<script src ="js.js"></script>
当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将js
脚本放在底部而不是头部
script 的位置是否会影响首屏显示时间
- 在解析 HTML 生成
DOM
过程中,js
文件的下载是并行的,不需要DOM
处理到 script 节点。因此,script
的位置不影响首屏显示的开始时间。 - 浏览器解析 HTML 是自上而下的线性过程,
script
作为 HTML 的一部分同样遵循这个原则。因此,script
会延迟DomContentLoad
,只显示其上部分首屏内容,从而影响首屏显示的完成时间
script标签的defer
和async
有什么区别
script
:HTML暂停解析,下载JS
,执行JS
,在继续解析HTML。defer
:HTML继续解析,并行下载JS
,HTML
解析完在执行JS
(不用把script放到body后面,我们在head中<script defer>
让js脚本并行加载会好点)async
:HTML继续解析,并行下载JS
,执行JS
(加载完毕后立即执行,谁先加载完成谁先执行
),在继续解析HTML。加载完毕后立即执行,这导致async
属性下的脚本是乱序的,对于script
有先后依赖关系的情况,并不适用
注意:
JS
是单线程的,JS
解析线程和DOM
解析线程共用同一个线程,JS执行和HTML解析是互斥的
,加载资源可以并行
prefetch
和dns-prefetch
分别是什么
preload
和prefetch
preload
资源在当前页面使用,会优先加载。可以指明哪些资源是在页面加载完成后即刻需要的,浏览器在主渲染机制介入前就进行预加载,这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。需要as指定资源类型
prefetch
资源在未来页面使用,空闲时加载。其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档
<head>
<!-- 当前页面使用 -->
<link rel="preload" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
<!-- 未来页面使用 提前加载 比如新闻详情页 -->
<link rel="prefetch" href="other.js" as="script" />
<!-- 当前页面 引用css -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- 当前页面 引用js -->
<script src="main.js" defer></script>
</body>
目前可用的属性类型如下:
audio: 音频文件。
document: 一个将要被嵌入到<frame>或<iframe>内部的HTML文档。
embed: 一个将要被嵌入到<embed>元素内部的资源。
fetch: 那些将要通过fetch和XHR请求来获取的资源,比如一个ArrayBuffer或JSON文件。
font: 字体文件。
image: 图片文件。
object: 一个将会被嵌入到<embed>元素内的文件。
script: JavaScript文件。
style: 样式表。
track: WebVTT文件。
worker: 一个JavaScript的web worker或shared worker。
video: 视频文件。
dns-preftch
和preconnect
dns-pretch
DNS预查询preconnect
DNS预连接
通过预查询和预连接减少DNS解析时间
<head>
<!-- 针对未来页面提前解析:提高打开速度 -->
<link rel="dns-pretch" href="https://font.static.com" />
<link rel="preconnect" href="https://font.static.com" crossorigin />
</head>