JS 的定义
JavaScript 是一种脚本,一门编程语言,它可以在网页上实现复杂的功能,网页展现给你的不再是简单的静态信息,而是实时的内容更新,交互式的地图,2D/3D 动画,滚动播放的视频等等。
img
它是标准 Web 技术蛋糕的第三层。
HTML 是一种标记语言,用来结构化我们的网页内容。CSS 是一种样式规则语言,可将样式应用于结构化的 HTML 内容,控制其外观。JavaScript 是一种动态脚本语言,用于动态创建和控制页面内容(包含结构化的内容及其样式),它可以响应用户输入并做出及时反馈。
所谓结构化,就是一层一层的包含关系。下面就是一段结构化的 HTML 文本:
<div>
<span>LIYI</span>
<span>yishulun.com</span>
</div>
两个子标签 span 包含在一个父标签 div 之内。
HTML 用了尖括号作为了标签的边界符,这只是一种语法规定,事实上如果我们愿意,我们使用 JSON 或 TOML 格式实现结构化也是可以的,我们也可以使用方括号代替尖括号,但从实际结果来看,还是使用 XML 格式更好,尖括号因为在文本中使用不多,用它作为边界符也比较合适。
一种样式规则规定了一种特定的渲染效果,例如 color:red 表示红色,padding:15px 表示 15 个像素的内间距。
没有任何其它渲染库,像 CSS 这样既简单、又丰富地实现了对各种渲染效果的控制,再加上 HTML 标签几乎不够约束的构建结构化内容的能力,CSS+HTML 成为了普适性最强的界面构建标准,再加上 JS 强大而灵活的互动能力,CSS+HTML+JS 理所当然地成为了最流行的大前端界面构建标准。从 Web,到手机 App,再到 PC(Personal Computer)桌面软件,CSS+HTML+JS 三剑客畅通无阻,一切可以用前端技术实现的界面,终将全部被大前端技术所覆盖。
JS 在 Web 页面上几乎能干一切事,举例:
JS 能干这么事情,一些是基于它本身语言具有的能力,一些基于浏览器宿主环境提供的 API,一些则基于第三方 API。
浏览器 API 主要包括:
第三方 API 由第三方厂商发布,例如 Twitter API、新浪微博 API 等。
img
浏览器在读取一个网页时,代码(HTML, CSS 和 JavaScript)将在一个运行环境(浏览器标签页)中得到执行。就像一间工厂,将原材料(代码)加工为一件产品(网页)。在 HTML 和 CSS 集合组装成一个网页后,浏览器的 JavaScript 引擎将执行 JavaScript 代码。这保证了当 JavaScript 开始运行之前,网页的结构和样式已经就位。
在这个过程中,浏览器具体都做了什么事?
这个过程有点复杂,主要有以下 11 步:
image-20230602081007700
什么是解释型语言?
在解释型语言中,代码自上而下运行,且实时返回运行结果。代码在由浏览器执行前,不需要将其转化为其他形式(二进制机器码)。代码将直接以文本格式(text form)被接收和处理。
什么是编译型语言?
编译型语言需要先将代码转化(编译)成另一种形式才能运行,比如 C/C++ 先被编译成汇编语言,然后才能由计算机运行。
这里有个问题,C++代码是被编译为了汇编代码,还是被编译为了机器码?这里的描述有没有问题?
计算机大厦是一级一级向上建起来的,早期 C++编译器甚至都是直接将 C++代码编译为 C 代码,然后直接扔给 C 编译器处理。不同 CPU 架构具有不同的指令集,因此汇编指令也不同;即使是同样的 CPU 架构,操作系统不同,可以复用的汇编语言代码集也是不同的。在 C/C++的编译过程中,先是将源代码编译为目标文件,这个目标文件依 CPU 架构不同、依系统不同,具有不同的汇编语言代码集,目标文件经过进一步链接,才变成了可执行文件。
image-20230602074439397
所以,上面讲“C/C++ 先被编译成汇编语言,然后才能由计算机运行”问题也不大,确定在中间状态编译为了汇编语言,只是中间少了一句“接着被连接为可执行文件”。
无论是解释型语言,还是编译型语言,最终在 CPU 中运行的,肯定都是只有 0 和 1 的机器码,两种类型的语言只是在不同程度上利用了系统能力。解释型语言比较懒惰,是直接将代码明文文本交给解释器,然后由解释器翻译成汇编代码或机器码,利用系统能力执行;编译型语言勤快一点,在执行之前就已经由编译器编译为了汇编代码,或机器码,或中间状态的字节码,最后由系统或运行时提供的能力完成执行。如果将两类语言的代码比作农民种植的稻谷,解释型语言是直接销售的带壳的稻子,编译型语言则是已经脱粒的稻子。
从这点看,编译型语言与解释型语言并无本质不同。
这是一种普遍的 server-client 架构,在服务器上,由 PHP、Python、Ruby 等后端语言,输出一个 HTML 页面,由客户端主动请求,发送到客户端并在客户端浏览器上运行。
这种 Web 架构一般也称为 B/S 架构,其中 B 指 Browser,S 是 Server;另一种与 B/S 相对的架构是 C/S 架构,其中指 Client。
在页面内部直接使用 script 标签添加:
<script>
// 在此编写 JS 代码
</script>
这和使用 style 标签在页面内添加样式代码是类似的:
<style>
// 在此编写CSS代码
</style>
对于外部的 js 文件,这样引入:
<script src="https://.../js-url.js" async></script>
async 标记表示这个网络资源不需阻塞主线程,因为是布尔标记,所以可以直接书写属性名称,而不必写值。对于布尔属性,标记即为 true,不标即为 false。
对于外部的样式代码,对比一下,可以这样引入:
<link rel="stylesheet" type="text/css" href="https://.../style-url.css"/>
有一些简单的代码,可以直接写在 HTML 元素的属性上:
<button onclick="alert('hi')">按钮</button>
相对地,CSS 样式代码也可以直接写在 style 属性上:
<div style="background-color:#66CC99; color:#993300; height:30px; line-height:30px;">yishulun.com</div>
JS 代码一般要通过 DOM API 操作页面元素,有一个事件——DOMContentLoaded 可以帮助开发者确定 DOM API 百分百可用:
document.addEventListener("DOMContentLoaded", function({
// JS代码
});
另一种简单的,但通常不被采纳的作法是:将 js 文件放在页面底部</body>
标签前,这种方法是不可靠的,解析走到这里的时候,并不代表所有外网资源(js、style)已经加载完毕并且可用了。
这里有一个问题:DOMContentLoaded 是何时触发的,它在上面的浏览器解析流程中,是在哪个节点触发的?在 Render 树构建时触发的吗?
DOMContentLoaded 事件是 HTML 文档(包括 CSS、JS,但不包括多媒体资源例如图片、音频、视频、字体等)被加载以及解析完成之后触发。该事件的触发,与浏览器解析流程走到哪个节点,没有必要的关系,它有可能会触发在 First Paint(首次渲染)之前,也可能发生在它后面。
默认情况下,script 标签加载外网 js 文件,是会阻塞页面解析的。
但如果添加了 async 标记,js 文件便不会阻塞 HTML 的解析,js 文件开始异步加载(async 是异步的意思),同时 HTML 继续向后解析,待 js 文件加载完成后,js 文件执行,这个时候 HTML 有可能解析完,也有可能没有解析完。
如果页面中有三个外网 js 文件:
<script async src="https://.../js/script1.js"></script>
<script async src="https://.../js/script2.js"></script>
<script async src="https://.../js/script3.js"></script>
它们都使用了 async 异步标记,但它们加载完成的时间点和执行的先后是无法确定的。
为了解决这个问题,defer 标记诞生了,该标记会按出现的顺序依次延迟脚本的执行,并且会在页面解析完成后执行,但仍然不会阻塞页面的解析。例如:
<script defer src="https://.../js/script1.js"></script>
<script defer src="https://.../js/script2.js"></script>
<script defer src="https://.../js/script3.js"></script>
这三个文件会先执行 script1,最后执行的是 script3。
脚本调用策略小结:
async
。(async 加载完就会执行,不会管 DOM API 是否可用。)defer
,将关联的脚本按所需顺序置于 HTML 中。补充一条:使用 DOMContentLoaded 事件检测 DOM 树是否可用,是一个好习惯,但并不是在所有情况下都需要这样做。实现开发中,许多程序员懒惰,统一都是在.ready()中写起始代码的。(注:.ready()是 jQuery 类库中的一个方法,代表页面 DOM 树可用。)
分为两类,单行注释与多行注释:
// 单行注释
/*
多行
注释
*/
JS 的多行注释并不要求在中间每行都写一个星号,这一点还是比较友好的。
- End -
小步快跑,正向反馈;面向未来,不求完美。
与时间做朋友,一同成长。
声明:本文纯人工编写