一、什么是 JavaScript
1、JavaScript 实现
完整的 JavaScript 实现包含以下几个部分:
-- --
核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM)
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 可以表示为一组分层节点
DOM 通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构。使用 DOM API, 可以轻松地删除、添加、替换、修改节点。
3、BOM
浏览器对象模型(BOM) API,用于支持访问和操作浏览器的窗口。使用BOM,开发者可以操控浏览器显示页面之外的部分。
-- --
总体来说,BOM主要针对浏览器窗口和子窗口(frame),不过人们通常会把任何特定于浏览器的扩展都归在BOM的范畴内。比如,下面就是这样一些扩展:
- 弹出新浏览器窗口的能力;
- 移动、缩放和关闭浏览器窗口的能力;
- navigator对象,提供关于浏览器的详尽信息;
- location对象,提供浏览器加载页面的详尽信息;
- screen对象,提供关于用户屏幕分辨率的详尽信息;
- performance对象,提供浏览器内存占用、导航行为和时间统计的详尽信息;
- 对cookie的支持;
- 其他自定义对象,如XMLHttpRequest和IE的ActiveXObject。
4、总结
JavaScript是一门用来与网页交互的脚本语言,包含以下三个组成部分。
- ECMAScript:由ECMA-262定义并提供核心功能。
- 文档对象模型(DOM):提供与网页内容交互的方法和接口。
- 浏览器对象模型(BOM):提供与浏览器交互的方法和接口。
二、HTML中的 JavaScript
1、<script> 元素
将JavaScript插入HTML的主要方法是使用<script>元素。有下列属性:
- async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效。
- defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。
- src:可选。表示包含要执行的代码的外部文件。
- type:可选。代替language,表示代码块中脚本语言的内容类型(也称MIME类型)。
crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin= "anonymous"配置文件请求不必设置凭据标志。crossorigin="use-credentials"设置凭据标志,意味着出站请求会包含凭据。
integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,
Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
charset:可选。使用src属性指定的代码字符集。这个属性很少使用,因为大多数浏览器不在乎它的值。
-- --
注意:
- 在使用行内JavaScript代码时,要注意代码中不能出现字符串</script>。浏览器解析行内脚本的方式决定了它在看到字符串时,会将其当成结束的标签。想避免这个问题,只需要转义字符“\”即可。
- 使用了src属性的<script>元素不应该在<script>和</script>标签中再包含其他JavaScript代码。如果两者都提供的话,则浏览器只会下载并执行脚本文件,从而忽略行内代码。
2、行内代码与外部文件
虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。
-- --
- 可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存 所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑 代码。
- 缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。
- 适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。 包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。
3、<noscript> 元素
用于给不支持 JavaScript 的浏览器提供替代内容。
-- --
在下列两种 情况下,浏览器将显示包含在 <noscript> 中的内容:
- 浏览器不支持脚本;
- 浏览器对脚本的支持被关闭。
任何一个条件被满足,包含在<noscript>中的内容就会被渲染。否则,浏览器不会渲染<noscript>中的内容。
4、总结
JavaScript 是通过 <script> 元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到 HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。
- 要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。
- 所有<script>元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的 情况下,包含在<script>元素中的代码必须严格按次序解释。
- 对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面 的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
- 可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。
- 可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。
- 通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。
三、语言基础
1、严格模式
ECMAScript 5 增加了严格模式(strict mode)的概念。严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript 3 的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。
-- --
要对 整个脚本启用严格模式,在脚本开头加上这一行:
"use strict";
虽然看起来像个没有赋值给任何变量的字符串,但它其实是一个预处理指令。任何支持的 JavaScript 引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏 ECMAScript 3 语法。
-- --
也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:
function doSomething() { "use strict"; // 函数体 }
2、for 循环中的 var 和 let 声明
for (var i = 0; i < 5; ++i) { setTimeout(() => console.log(i), 0) } // 输出:5、5、5、5、5
之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。
for (let i = 0; i < 5; ++i) { setTimeout(() => console.log(i), 0) } // 输出: 0、1、2、3、4
使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。 每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。
3、数据类型
简单数据类型(也称为原始类型):
Undefined、Null、Boolean、Number、 String 和 Symbol。
Symbol(符号)是 ECMAScript 6 新增的。
-- --
复杂数据类型:
Object(对 象)。Object 是一种无序名值对的集合。
4、数值转换
有 3 个函数可以将非数值转换为数值:Number()、parseInt() 和 parseFloat()。
- Number()是转型函数,可用于任何数据类型。
- 后两个函数主要用于将字符串转换为数值。
Number()函数基于如下规则执行转换。
- 布尔值,true 转换为 1,false 转换为 0。
- 数值,直接返回。
- null,返回 0。
- undefined,返回 NaN。
- 字符串,应用以下规则。
- 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。 因此,Number("1")返回 1,Number("123")返回 123,Number("011")返回 11(忽略前面 的零)
- 如果字符串包含有效的浮点值格式如"1.1",则会转换为相应的浮点值(同样,忽略前面的零)。
- 如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整 数值。
- 如果是空字符串(不包含字符),则返回 0。
- 如果字符串包含除上述情况之外的其他字符,则返回 NaN。
- 对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用 toString()方法,再按照转换字符串的规则转换。
示例:
let num1 = Number("Hello world!"); // NaN let num2 = Number(""); // 0 let num3 = Number("000011"); // 11 let num4 = Number(true); // 1
通常在需要得到整数时可以优先使 用 parseInt()函数。parseInt()函数更专注于字符串是否包含数值模式。
- 字符串最前面的空格会被忽略,从第一个非空格字符开始转换。
- 如果第一个字符不是数值字符、加号或减号,parseInt()立即 返回 NaN。这意味着空字符串也会返回 NaN(这一点跟 Number()不一样,它返回 0)。
- 如果第一个字符 是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符。
比如,"1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地,"22.5"会被转换为 22,因为小数 点不是有效的整数字符。
console.log(parseInt('1234blue')); // 1234 console.log(parseInt('22.5')); // 22
假设字符串中的第一个字符是数值字符,parseInt()函数也能识别不同的整数格式(十进制、八 进制、十六进制)。
换句话说,如果字符串以"0x"开头,就会被解释为十六进制整数。如果字符串以"0" 开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。
如下示例:
let num1 = parseInt("1234blue"); // 1234 let num2 = parseInt(""); // NaN let num3 = parseInt("0xA"); // 10,解释为十六进制整数 let num4 = parseInt(22.5); // 22 let num5 = parseInt("70"); // 70,解释为十进制值 let num6 = parseInt("0xf"); // 15,解释为十六进制整数
不同的数值格式很容易混淆,因此 parseInt()也接收第二个参数,用于指定底数(进制数)。
如下示例:
let num1 = parseInt("10", 2); // 2,按二进制解析 let num2 = parseInt("10", 8); // 8,按八进制解析 let num3 = parseInt("10", 10); // 10,按十进制解析 let num4 = parseInt("10", 16); // 16,按十六进制解析
parseFloat()函数的工作方式跟 parseInt()函数类似,都是从位置 0 开始检测每个字符。同样, 它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。
这意味着第一次出现的小数点是有 效的,但第二次出现的小数点就无效了,此时字符串的剩余字符都会被忽略 。
console.log(parseFloat('22.34.5')); // 22.34
parseFloat()函数的另一个不同之处在于
- 它始终忽略字符串开头的零。
- 这个函数能识别前面讨论的所有浮点格式,以及十进制格式(开头的零始终被忽略)。
- 十六进制数值始终会返回 0。因为 parseFloat()只解析十进制值,因此不能指定底数。
- 最后,如果字符串表示整数(没有小数点或者小 数点后面只有一个零),则 parseFloat()返回整数。
如下示例:
let num1 = parseFloat("1234blue"); // 1234,按整数解析 let num2 = parseFloat("0xA"); // 0 let num3 = parseFloat("22.5"); // 22.5 let num4 = parseFloat("22.34.5"); // 22.34 let num5 = parseFloat("0908.5"); // 908.5 let num6 = parseFloat("3.125e7"); // 31250000