以下内容是本人的学习笔记,内容不详细,只是摘取其一部分,如果有错误请提出指正。
一、什么是JavaScript
1995年,JavaScript问世。当时,它的主要用途是代替Perl等服务器端语言处理输入验证。
1.1 JavaScript实现
虽然JavaScript和ECMAScript基本上是同义词,但JavaScript远远不限于 ECMA-262所定义的那样。
完整的JavaScript实现包含以下几个部分:
- 核心(ECMAScript)
- 文档对象模型(DOM)
- 浏览器对象模型(BOM)
1.1.1 ECMAScript
ECMAScript,即ECMA-262定义的语言。Web浏览器只是ECMAScript实现可能存在的一种宿主环境(host environment)。宿主环境提供ECMAScript的基准实现和与环境自身交互必需的扩展。扩展(比如DOM)使ECMAScript核心类型和语法,提供特定于环境的额外功能。其他宿主环境还有服务器端JavaScript平台Node.js和即将被淘汰的Adobe Flash。
如果不涉及浏览器的话,ECMA-262到底定义了什么?在基本的层面,它描述这门语言的如下部分:
- 语法
- 类型
- 语句
- 关键字
- 保留字
- 操作符
- 全局对象
ECMAScript只是对实现这个规范描述的所有方面的一门语言的称呼。
JavaScript实现了ECMAScript,而Adobe ActionScript同样也实现了ECMAScript。
1.1.2 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可以表示为一组分层节点,如下所示:
由于网景和微软采用不同思路开发DHTML,开 发者写一个HTML页面就可以在任何浏览器中运行的好日子就此终结。为了保持Web跨平台的本性,万维网联盟(W3C,World WideWeb Consortium) 制定了DOM标准。
注意 DOM并非只能通过JavaScript访问,而且确实被其他很 多语言实现了。不过对于浏览器来说,DOM就是使用 ECMAScript实现的,如今已经成为JavaScript语言的一大组成 部分。
1.2.3 BOM
IE3和Netscape Navigator 3提供了浏览器对象模型(BOM) API,用于 支持访问和操作浏览器的窗口。
它是唯一一个没有相关标准的JavaScript实现。HTML5改 变了这个局面,这个版本的HTML以正式规范的形式涵盖了尽可能多的 BOM特性。
总体来说,BOM主要针对浏览器窗口和子窗口(frame),不过人们通 常会把任何特定于浏览器的扩展都归在BOM的范畴内。比如,下面就 是这样一些扩展:
- 弹出新浏览器窗口的能力;
- 移动、缩放和关闭浏览器窗口的能力;
- navigator对象,提供关于浏览器的详尽信息;
- location对象,提供浏览器加载页面的详尽信息;
- screen对象,提供关于用户屏幕分辨率的详尽信息;
- performance对象,提供浏览器内存占用、导航行为和时间统计的
- 详尽信息;
- 对cookie的支持;
- 其他自定义对象,如XMLHttpRequest和IE的ActiveXObject。
1.4 小结
JavaScript是一门用来与网页交互的脚本语言,包含以下三个组成部分。
- ECMAScript:由ECMA-262定义并提供核心功能。
- 文档对象模型(DOM):提供与网页内容交互的方法和接口。
- 浏览器对象模型(BOM):提供与浏览器交互的方法和接口。 JavaScript的这三个部分得到了五大Web浏览器(IE、Firefox、Chrome、 Safari和Opera)不同程度的支持。所有浏览器基本上对 ES5(ECMAScript 5)提供了完善的支持,而对ES6(ECMAScript 6,2015年6月发布) 和ES7(ECMAScript 7)的支持度也在不断提升。这些浏览器对DOM的 支持各不相同,但对Level 3的支持日益趋于规范。HTML5中收录的 BOM会因浏览器而异,不过开发者仍然可以假定存在很大一部分公共 特性。
二、HTML中的JavaScript
将JavaScript引入网页,首先要解决它与网页的主导语言HTML的关系问 题。
2.1 <script>元素
将JavaScript插入HTML的主要方法是使用<script>元素。<script>元素有下列8个属性,用到的最多的应该是src指出要执行的代码的外部文件的路径,其余用到的比较少。(可能只是我用得比较少)
使用<script>的方式有两种:通过它直接在网页中嵌入JavaScript代码, 以及通过它在网页中包含外部JavaScript文件。
在使用行内JavaScript代码时,要注意代码中不能出现字符 串</script>。比如,下面的代码会导致浏览器报错:
<script>
function sayScript() {
console.log("</script>");
}
</script>
浏览器解析行内脚本的方式决定了它在看到字符串</script>时,会将 其当成结束的</script>标签。想避免这个问题,只需要转义字符“\” 即 可:
<script>
function sayScript() {
console.log("<\/script>");
}
</script>
另外,使用了src属性的<script>元素不应该再在<script>和</script> 标签中再包含其他JavaScript代码。如果两者都提供的话,则浏览器只会 下载并执行脚本文件,从而忽略行内代码。
<script>元素的src属性可以是一个完整的URL,而且这个URL指向的资源可以跟包含它的HTML页面不在同一个域中。
不管包含的是什么代码,浏览器都会按照<script>在页面中出现的顺序 依次解释它们,前提是它们没有使用defer和async属性。第二个<script>元的代码必须在第一个<script>元素的代码解释完毕才能开始解释,第三个则必须等第二个解释完,以此类推。
2.1.1 标签占位符
把所有JavaScript文件都放在<head>里,也就意味着必须把所有JavaScript代码都下载、解析和解释完成后,才能开始渲染页面(页面在浏览器解析到<body>的起始标签时开始渲染)。
对于需要很多JavaScript的页面,这会导致页面渲染的明显延迟,在此期间浏览器窗口完全空白。为解决这个问题,现代Web应用程序通常将所有JavaScript引用放在<body>元素中的页面内容后面。如下面的例子所示:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
</head>
<body>
<!-- 这里是页面内容 -->
<script src="example1.js"></script>
<script src="example2.js"></script>
</body>
</html>
这样一来,页面会在处理JavaScript代码之前完全渲染页面。用户会感觉 页面加载更快了,因为浏览器显示空白页面的时间短了。
2.1.2 推迟执行脚本
HTML 4.01为<script>元素定义了一个叫defer的属性。只对外部脚本文件有效。这个属性表示脚本在执行的时候不会改变页面的结构。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在<script>元素中设置defer属性, 相当于告诉浏览器立即下载,但延迟执行。
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script defer src="example1.js"></script>
<script defer src="example2.js"></script>
</head>
<body>
<!-- 这里是页面内容 -->
</body>
</html>
虽然这个例子中的<script>元素包含在页面的<head>中,但它们会在浏 览器解析到结束的</html>标签后才会执行。
在实际当中,推迟执行的脚本不一定总会按顺序执行或者在DOMContentLoaded事件之前执行,因此最好只包含一个这样的脚本。
对defer属性的支持是从IE4、Firefox 3.5、Safari 5和Chrome 7开始的。 其他所有浏览器则会忽略这个属性,按照通常的做法来处理脚本。考虑到这一点,还是把要推迟执行的脚本放在页面底部比较好。
2.1.3 异步执行脚本
HTML5为<script>元素定义了async属性。从改变脚本处理方式上 看,async属性与defer类似。当然,它们两者也都只适用于外部脚本, 都会告诉浏览器立即开始下载。不过,与defer不同的是,标记为async 的脚本并不保证能按照它们出现的次序执行,比如:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script async src="example1.js"></script>
<script async src="example2.js"></script>
</head>
<body>
<!-- 这里是页面内容 -->
</body>
</html>
给脚本添加async属性的目的是告诉浏览器, 不必等脚本下载和执行完后再加载页面,同样也不必等到该异步脚本下 载和执行后再加载其他脚本。正因为如此,异步脚本不应该在加载期间 修改DOM。
异步脚本保证会在页面的load事件前执行,但可能会在DOMContentLoaded之前或之后。
2.1.4 动态加载脚本
通过向DOM中动态添加script元素同样可以加载指定的脚本。只要创建一个script元素并将其添加到DOM即可。
let script = document.createElement('script');
script.src = 'gibberish.js';
document.head.appendChild(script);
当然,在把HTMLElement元素添加到DOM且执行到这段代码之前不会发 送请求。默认情况下,以这种方式创建的<script>元素是以异步方式加 载的,相当于添加了async属性。
不是所有浏览器都支持async属性。因此,如果要统一动态脚本的加载行为,可以明确将其设置为同步加载:
let script = document.createElement('script');
script.src = 'gibberish.js';
script.async = false;
document.head.appendChild(script);
以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它 们在资源获取队列中的优先级。
要想让预加载器知道这些动态请求文件的存在,可以在文档头部显式声明它们:
<link rel="preload" href="gibberish.js">
2.3 <noscript>元素
<noscript>元素,被用于给不支持JavaScript的浏览器 提供替代内容。
<noscript>元素可以包含任何可以出现在<body>中的HTML元素,<script>除外。在下列两种情况下,浏览器将显示包含在<noscript>中的内容:
- 浏览器不支持脚本;
- 浏览器对脚本的支持被关闭。
下面有一个例子:
<!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>
这个例子是在脚本不可用时让浏览器显示一段话。如果浏览器支持脚 本,则用户永远不会看到它。
2.4 小结
JavaScript是通过<script>元素插入到HTML页面中的。这个元素可用于 把JavaScript代码嵌入到HTML页面中,跟其他标记混合在一起,也可用 于引入保存在外部文件中的JavaScript。本章的重点可以总结如下。
-
要包含外部JavaScript文件,必须将src属性设置为要包含文件的URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。
-
所有<script>元素会依照它们在网页中出现的次序被解释。在不使用defer和async属性的情况下,包含在<script>元素中的代码必须严格按次序解释。
-
对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
-
可以使用defer属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。
-
可以使用async属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。
-
通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。