前端的葵花宝典 - 红宝书《JavaScript高级程序设计(第4版)》学习笔记
目录
- 前言
- 1、第1章 什么是JavaScript
- 1.1 ECMAScript
- 1.2 DOM
- 1.3 BOM
- 小结
- 第2章 HTML 中的JavaScript
- 2.1 < script >元素
- 2.1.1 标签位置
- 2.1.2 推迟执行脚本
- 2.1.3 异步执行脚本
- 2.2 行内代码与外部文件
- 小结
- 第3章 语言基础
- 3.1 语法
- 3.1.1 区分大小写
- 3.1.2标识符
- 3.1.3 严格模式
- 3.1.4 语句
- 3.2 变量
- 3.2.1 var关键字
- 3.2.2 let关键字
- for 循环中的let声明
- 3.3.3 const 声明
- 3.4 数据类型
- 3.4.1 typeof操作符
- 3.4.2 Number类型
- 1. isNaN()函数
- 2. 数值转换
- 3.4.3 String 类型
- 1. 把一个值转换为字符串的两种方式:
- 2. 模板字面量
- 3.4.4 相等操作符
- 3.5 语句
- 3.5.1 if 语句
- 3.5.2 do-while 语句
- 3.5.3 while 语句
- 3.5.4 for 语句
- 3.5.5 for-in 语句
- 3.5.6 for-of 语句
- 3.5.7 标签语句
- 3.5.8 break 和continue 语句
- 3.5.9 with 语句
- 3.5.10 switch 语句
- 3.6 函数
- 小结
- 第4章 变量、作用域与内存
- 4.1 原始值与引用值
- 4.1.1 动态属性
- 4.1.2 传递参数
- 4.1.3 确定类型
- 4.2 执行上下文与作用域
- 4.2.1 作用域链增强
- 4.2.2 变量声明
- 4.3 垃圾回收
- 4.3.1 标记清理
- 4.3.2 引用计数
- 4.3.3 内存管理
- 第 5 章 基本引用类型
- 5.1 Date
- 5.2 RegExp
- 5.3 原始值包装类型
- 5.3.1 Number
- 5.3.2 String
- String 类型解析和操作字符串的方法
- 字符串操作方法
- 字符串包含方法
- `trim()`方法
- `repeat()`方法
- `padStart()`和`padEnd()`方法
- 字符串迭代与解构
- 字符串大小写转换
- 字符串模式匹配方法
- 5.4 单例内置对象
- 5.4.1 Global
- URL编码方法
- `eval()`方法
- Global 对象属性
- window 对象
- 5.4.2 Math
- 1)Math对象属性
- 2)min() 和 max() 方法
- 3)Math 舍入方法
- 4)Math.random()方法
- 小节
- 第6章 集合引用类型
- 6.1 Object
- 6.2 Array
- 6.2.1 创建数组
- 1)使用Array构造函数
- 2)数组字面量(array literal)表示法
- 6.2.2 数组空位
- 6.2.3 数组索引
- 6.2.4 检测数组
- 6.2.5 迭代器方法
- 6.2.6 复制和填充方法
- 6.2.7 转换方法
- 6.2.8 栈方法
- 6.2.9 队列方法
- 6.2.10 排序方法
- 6.2.11 数组方法
- 6.2.12 搜索和位置方法
- 6.2.12.1 严格相等
- 6.2.12.2 断言函数
- 6.2.13 迭代方法
- 6.2.14 归并方法
- 6.3 定型数组
- 6.3.1 ArrayBuffer
- ~~待续
前言
1998 年,国际标准化组织(ISO)和国际电工委员会(IEC)将ECMAScript 采纳为标准(ISO/IEC-16262)。自此以后,各家浏览器均以ECMAScript 作为自己JavaScript 实现的依据,虽然具体实现各有不同。
第四版全面深入地介绍了JavaScript 开发者必须掌握的前端开发技术,涉及JavaScript 的基础特性和高级特性。
本笔记中的各小节序号,未依照原书中的章节序号。
关于电子版PDF的【分享说明】1
1、第1章 什么是JavaScript
完整的JavaScript 实现包含以下几个部分:
- 核心(ECMAScript);
- 文档对象模型(DOM);
- 浏览器对象模型(BOM)
1.1 ECMAScript
ECMAScript,即ECMA-262 定义的语言,并不局限于Web 浏览器。这门语言没有输入和输出之类的方法。Web 浏览器是ECMAScript 实现的一种宿主环境(host environment)
JavaScript 实现了 ECMAScript。
1.2 DOM
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,可以轻松地删除、添加、替换、修改节点。
注意:DOM 并非只能通过JavaScript 访问,而且确实被其他很多语言实现了。不过对于浏览器来说,DOM 就是使用 ECMAScript 实现的,如今已经成为 JavaScript 语言的一大组成部分。
1.3 BOM
IE3 和 Netscape Navigator 3 提供了浏览器对象模型(BOM) API,用于支持访问和操作浏览器的窗口。使用BOM,开发者可以操控浏览器显示页面之外的部分。
总体来说,BOM 主要针对浏览器窗口和子窗口(frame),不过人们通常会把任何特定于浏览器的扩展都归在BOM 的范畴内。
小结
JavaScript 是一门用来与网页交互的脚本语言,包含以下 三个组成部分。
- ECMAScript:由ECMA-262 定义并提供核心功能;
- 文档对象模型(DOM):提供与网页内容交互的方法和接口;
- 浏览器对象模型(BOM):提供与浏览器交互的方法和接口。
第2章 HTML 中的JavaScript
本章内容:
- 使用< script>元素;
- 行内脚本与外部脚本的比较;
- 文档模式对JavaScript 有什么影响;
- 确保JavaScript 不可用时的用户体验
2.1 < script >元素
<script>元素有下列8 个属性:
属性 | 描述 |
---|---|
async
|
可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载(异步执行脚本)。只对外部脚本文件有效。 |
charset
|
可选。使用 src 属性指定的代码字符集(规定在外部脚本文件中使用的字符编码。)。这个属性很少使用,因为大多数浏览器不在乎它的值。 |
crossorigin
|
可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。 crossorigin="anonymous"配置文件请求不必设置凭据标志; crossorigin="use-credentials"设置凭据标志,意味着出站请求会包含凭据。 |
defer
|
可选。表示立即下载,但延迟执行(直到文档完全被解析和显示为止)。只对外部脚本文件有效。在IE7 及更早的版本中,对行内脚本也可以指定这个属性。 |
integrity
|
可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以用于确保内容分发网络(CDN)不会提供恶意内容。 |
language | 废弃。最初用于表示代码块中的脚本语言(如"JavaScript"、“JavaScript 1.2"或"VBScript”)。大多数浏览器都会忽略这个属性,不应该再使用它。 |
src
|
可选。表示包含要执行的代码的外部文件。 |
type | 可选。代替language,表示代码块中脚本语言的内容类型。这个值始终都是"text/javascript",尽管"text/javascript"和"text/ecmascript"都已经废弃了。JavaScript 文件的MIME 类型通常是"application/x-javascript",不过给type 属性这个值有可能导致脚本被忽略。在非IE 的浏览器中有效的其他值还有"application/javascript"和"application/ecmascript"。如果这个值是module,则代码会被当成ES6 模块,而且只有这时候代码中才能出现import 和export 关键字。 |
使用<script>的两种方式:
- 直接在网页中嵌入 JavaScript 代码;
- 在网页中导入外部 JavaScript 文件。
要嵌入行内 JavaScript 代码,直接把代码放在<script>元素中就行:
<script>function sayHi() {console.log("Hi!");}
</script>
包含在<script>内的代码会被从上到下解释执行。在上面的例子中,被解释的是一个函数定义,并且该函数会被保存在解释器环境中。在<script>元素中的代码被计算执行完成之前,页面的其余内容不会被加载,也不会被显示。
在使用行内JavaScript 代码时,要注意代码中不能出现字符串</script>。比如,下面的代码会导致浏览器报错:
<script>function sayScript() {console.log("</script>");}
</script>
只需在标签内添加转义字符”\
“即可解决
console.log("<\/script>");
这样修改之后,代码就可以被浏览器完全解释,不会导致任何错误。
此处的转义字符指在 JavaScript 中使用反斜杠“
\
”来向文本字符串添加特殊字符。
要包含外部文件中的JavaScript,就必须使用`src`属性。这个属性的值是一个URL,指向包含 JavaScript 代码的文件,比如:
<script src="example.js"></script>
这个例子在页面中加载了一个名为 example.js 的外部文件。与解释行内JavaScript 一样,在解释外部 JavaScript 文件时,页面也会阻塞(阻塞时间也包含下载文件的时间。)
注意:
按照惯例,外部 JavaScript 文件的扩展名是.js
。但这不是必须的,因为浏览器不会检查所包含JavaScript 文件的扩展名。这就为使用服务器端脚本语言动态生成 JavaScript 代码,或者在浏览器中将JavaScript 扩展语言(如TypeScript,或React 的JSX)转译为JavaScript提供了可能性。但请注意,服务器经常会根据文件扩展来确定响应的正确MIME 类型。如果不打算使用.js 扩展名,一定要确保服务器能返回正确的MIME 类型。
另外,使用了src 属性的<script>元素不应该再在<script>和</script>标签中再包含其他 JavaScript 代码。
如果两者都提供的话,则浏览器只会下载并执行脚本文件,从而忽略行内代码。
<script>元素的一个最为强大、同时也备受争议的特性是,它可以包含来自外部的JavaScript 文件。跟<img>元素很像,<script>元素的 src 属性可以是一个完整的URL,而且这个URL 指向的资源可以跟包含它的HTML 页面不在同一个域中,比如:
<script src="http://www.somewhere.com/afile.js"></script>
浏览器在解析这个资源时,会向src 属性指定的路径发送一个GET 请求,以取得相应资源,假定是一个JavaScript 文件。这个初始的请求不受浏览器同源策略限制,但返回并被执行的JavaScript 则受限制。当然,这个请求仍然受父页面HTTP/HTTPS 协议的限制。
如果是引用别人服务器上的JavaScript 文件时要格外小心,因为恶意的程序员随时可能替换这个文件。
要确保该域是自己所有的,或者该域是一个可信的来源。也可用。<script>标签的 integrity
属性来防范这种问题(但此属性于不同浏览器存在兼容性问题)。
2.1.1 标签位置
过去,所有<script>元素都被放在页面的标签内,如下所示:
<!DOCTYPE html>
<html><head><title>Example HTML Page</title><script src="example1.js"></script><script src="example2.js"></script></head><body><!-- 这里是页面内容 --></body>
</html>
但是,为了解决js 代码下载、解析、解释期间导致的页面渲染延迟问题(打开网页时一段时间的浏览器窗口空白),现代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>
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>
本示例中,由于指定了 defer
属性,即使<script>元素被放在了页面的<head>标签中,但它们仍会在浏览器解析到结束的</html>标签后才会执行。
defer
属性只对外部脚本文件才有效。这是HTML5 中明确规定的,因此支持 HTML5 的浏览器会忽略行内脚本的 defer 属性。
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 属性的目的是告诉浏览器,不必等脚本下载和执行完后再加载页面,同样也不必等到该异步脚本下载和执行后再加载其他脚本。
不过好的 Web 开发实践根本就不推荐使用这个方法。
2.2 行内代码与外部文件
虽然可以直接在HTML 文件中嵌入JavaScript 代码,但通常认为最佳实践是尽可能将JavaScript 代码放在外部文件中。推荐使用外部文件的理由如下:
- 可维护性;
- 缓存;
- 适应未来。
小结
JavaScript 是通过<script>元素插入到HTML 页面中的。这个元素可用于把JavaScript 代码嵌入到 HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的JavaScript。小结如下:
- 要包含外部JavaScript 文件,必须将src 属性设置为要包含文件的URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。
- 所有<script>元素会依照它们在网页中出现的次序被解释。在不使用
defer
和async
属性的情况下,包含在<script>元素中的代码必须严格按次序解释。 - 对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,才能继续渲染页面的剩余部分。应把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
- 可以使用defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。
- 可使用
async
属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。 - 通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。
第3章 语言基础
3.1 语法
ECMAScript 的语法很大程度上借鉴了C 语言
和其他类C语言
,如Java 和 Perl。熟悉这些语言的开发者,应该很容易理解ECMAScript 宽松的语法。
3.1.1 区分大小写
ECMAScript 中一切都区分大小写。无论是变量、函数名还是操作符,都区分大小写。
3.1.2标识符
标识符:变量、函数、属性或函数参数的名称。
标识符可以由一或多个下列字符组成:
- 第一个字符必须是一个字母、下划线(_)或美元符号($);
- 剩下的其他字符可以是字母、下划线、美元符号或数字。
推荐驼峰命名法,首字母小写,后面每个单词的首字母大写。
3.1.3 严格模式
严格模式是一种不同的 JavaScript 解析和执行模型,不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。如果要对整个脚本启用严格模式,在脚本开头加上这一行:
"use strict";
它其实是一个预处理指令。任何支持的 JavaScript 引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏ECMAScript 3 语法。
也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:
function doSomething() {"use strict";// 函数体
}
严格模式会影响JavaScript 执行的很多方面,因此本书在用到它时会明确指出来(所有现代浏览器都支持严格模式)。
3.1.4 语句
ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾,如下面的例子所示:
let sum = a + b // 没有分号也有效 —— 不推荐
let diff = a - b; // 加分号有效 —— 推荐!
即使语句末尾的分号不是必需的,也应该加上。如果没有结尾的分号,删除空行会导致语法错误。
加分号也有助于在某些情况下提升性能,因为解析器会尝试在合适的位置补上分号以纠正语法错误。
3.2 变量
CMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据,仅相当于一个点位符,可以保存任何类型的值。
有3 个关键字可以声明变量:var
、const
和let
。其中,var
在 ECMAScript 的所有版本中都可以使用,而 const
和 let
只能在ECMAScript 6 及更晚的版本中使用。
3.2.1 var关键字
使用var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用var
在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:
function test() {var message = "hi"; // 局部变量
}
test();
console.log(message); // 出错!
但是,如果在 函数内 定义变量时 省略var 操作符,可以创建一个全局变量:
function test() {message = "hi"; // 全局变量
}
test();
console.log(message); // 输出"hi"
这样,只要调用一次函数test(),就会定义这个变量,并且可以在函数外部访问到。
~~注意~~
虽然可以通过省略 var 操作符定义全局变量,但不推荐
这么做。在局部作用域中定义的全局变量很难维护,也会造成困惑。这是因为不能一下子断定省略var 是不是有意而为之。在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出ReferenceError
。
在一条语句中,连续声明多个变量(用逗号分隔,换行、空格、缩进非必须):
var message = "hi",found = false,age = 29;
var 存在变量提升,重复声明也不会报错。
严格模式下,不能定义名为eval
和arguments
的变量,否则会导致语法错误。
3.2.2 let关键字
let
跟 var
的作用差不多,但有着非常重要的区别。最明显的区别是,let
声明的范围是块作用域,而var
声明的范围是函数作用域。块作用域 是 函数作用域 的 子集
if (true) {var name = 'Matt';console.log(name); // Matt
}console.log(name); // Mattif (true) {let age = 26;console.log(age); // 26
}console.log(age); // ReferenceError: age 没有定义
1、let 没有变量提升,重复声明会报错。
2、let 在全局作用域中声明的变量不会成为window 对象的属性。不过,let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续(因此,需确保同一页面不会重复声明)
for 循环中的let声明
在let 出现之前,for 循环定义的迭代变量会渗透到循环体外部:
for (var i = 0; i < 5; ++i) {// 循环逻辑
}console.log(i); // 5
改成使用let 之后,这个问题就消失了,因为迭代变量的作用域仅限于for 循环块内部:
for (let i = 0; i < 5; ++i) {// 循环逻辑
}
console.log(i); // ReferenceError: i 没有定义
3.3.3 const 声明
const 的行为与let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改const 声明的变量会导致运行时错误。
它也不允许重复声明,声明的作用域也是块。
const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反const 的限制。
const age = 26;
age = 36; // TypeError: 给常量赋值
// const 也不允许重复声明
const name = 'Matt';
const name = 'Nicholas'; // SyntaxError
// const 声明的作用域也是块
const name = 'Matt';if (true) {const name = 'Nicholas';
}console.log(name); // Mattconst person = {};person.name = 'Matt'; // ok
不使用 var :限制自己只使用 let 和 const 有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
const 优先,let 次之
3.4 数据类型
ECMAScript 有 6 种简单数据类型和 1 种复杂数据类型(Object)。
数据类型 | 值 | 类型描述 |
---|---|---|
Undefined
|
Undefined | 值未定义( 是一个假值) |
null
|
null | 表示一个空对象指针,这也是给 typeof 传一个null 会返回"object"的原因 |
boolean
|
true false |
布尔值 (区分大、小写,True 和False是标识符而不是布尔值) |
string
|
字符串 | |
number
|
不同数值类型有不同的数值字面量 | 数值 |
object
|
表示值为对象(而非函数)或null
|
|
function
|
函数 | |
symbol
|
符号 |
虽然布尔值只有两个,但所有其他 ECMAScript 类型的值都有相应布尔值的等价形式。要将一个其他类型的值转换为布尔值,可以调用特定的 Boolean()
转型函数:
let message = "Hello world!";
let messageAsBoolean = Boolean(message);
Boolean()
转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值。什么值能转换为 true
或false
的规则取决于数据类型和实际的值。下表总结了不同类型与布尔值之间的转换规则。
数据类型 | 转换为 true 的值 | 转换为 false 的值 |
---|---|---|
boolean
|
true | false |
String
|
非空字符串 | “”(空字符串) |
Number
|
非零数值(包括无穷值) | 0、NaN(参见后面的相关内容) |
Object
|
任意对象 | null |
Undefined
|
N/A(不存在) | undefined |
理解以上转换非常重要,因为像if 等流控制语句会自动执行其他类型值到布尔值的转换,例如:
let message = "Hello world!";
if (message) {console.log("Value is true");
}
在这个例子中,console.log 会输出字符串"Value is true",因为字符串message 会被自动转换为等价的布尔值true。
由于存在这种自动转换,理解流控制语句中使用的是什么变量就非常重要。错误地使用对象而不是布尔值会明显改变应用程序的执行流。
3.4.1 typeof操作符
对任一个使用typeof
操作符会返回上表中所列类型之一(字符串)。
let message = "some string";
console.log(typeof message); // "string"
console.log(typeof(message)); // "string"
console.log(typeof 95); // "number"
typeof 是一个操作符而不是函数,不需要参数(但可以使用参数)。
严格来讲,函数在ECMAScript 中被认为是对象,并不代表一种数据类型。可是,
函数也有自己特殊的属性。为此,就有必要通过typeof
操作符来区分函数和其他对象。
var 或let 声明了变量但没有初始化时,就相当于给变量赋予了undefined 值:
let message;
console.log(message == undefined); // true
即使未初始化的变量会被自动赋予 undefined 值,但我们仍然建议在声明变量的同时进行初始化。这样,当
typeof
返回"undefined
"时,你就会知道那是因为给定的变 量尚未声明,而不是声明了但未初始化。
3.4.2 Number类型
有一个特殊的数值叫NaN
,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。
console.log(0/0); // NaN
console.log(-0/+0); // NaN
如果分子是非0 值,分母是有符号0 或无符号0,则会返回Infinity 或-Infinity:
console.log(5/0); // Infinity
console.log(5/-0); // -Infinity
1. isNaN()函数
该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值,如字符串 “10” 或布尔值。任何不能转换为数值的值都会导致这个函数返回 true。举例如下:
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值10
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值1
虽然不常见,但
isNaN()
可以用于测试对象。此时,首先会调用对象的valueOf()
方法,然后再确定返回的值是否可以转换为数值。如果不能,再调用toString()
方法,并测试其返回值
2. 数值转换
有 3 个函数可以将非数值转换为数值:
Number()
;parseInt()
;parseFloat()
Number()
是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。
let num1 = Number("Hello world!"); // NaN
let num2 = Number(""); // 0
let num3 = Number("000011"); // 11
let num4 = Number(true); // 1
通常在需要得到整数时可以优先使用parseInt()
函数。parseInt()
函数更专注于字符串是否包含数值模式。
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()
也接收第二个参数,用于指定底数(进制数)。如果知道要解析的值是十六进制,那么可以传入16 作为第二个参数,以便正确解析:
let num = parseInt("0xAF", 16); // 175
事实上,如果提供了十六进制参数,那么字符串前面的"0x"可以省掉:
let num1 = parseInt("AF", 16); // 175
let num1 = parseInt("AF"); // 没有传入进制数,出错了
因为不传底数参数相当于让parseInt()自己决定如何解析,所以为避免解析出错,建议始终传给它第二个参数。
多数情况下解析的应该都是十进制数,此时第二个参数就要传入10。
parseFloat()
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
3.4.3 String 类型
字符串可以使用双引号("
)、单引号('
)或反引号(`)标示,因此下面的代码都是合法的:
let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`
ECMAScript 中的字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量,如下所示:
let lang = "Java";
lang = lang + "Script";
后台过程:
首先会分配一个足够容纳10 个字符的空间,然后填充上 “Java” 和 “Script”。最后销毁原始的字符串 “Java” 和字符串 “Script”
1. 把一个值转换为字符串的两种方式:
toString()
方法:可见于数值、布尔值、对象和字符串值- 如果值有toString()方法,则调用该方法(不传参数)并返回结果;
- 如果值是null,返回"null";
- 如果值是undefined,返回"undefined
用加号操作符
+
给一个值加上空字符串""
2. 模板字面量
模板字面量最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个值。
技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript 句法表达式。
字符串插值通过在${}
中使用一个 JavaScript 表达式 实现:
let interpolatedTemplateLiteral =
`${ value } to the ${ exponent } power is ${ value * value }`;
所有插入的值都会使用toString()
强制转型为字符串,而且任何JavaScript 表达式都可以用于插值。嵌套的模板字符串无须转义:
console.log(`Hello, ${ `World` }!`); // Hello, World!
3.4.4 相等操作符
如果有任一操作数是NaN,则相等操作符返回false,不相等操作符返回true。
记住:即使两个操作数都是NaN,相等操作符也返回false,因为按照规则,NaN 不等于NaN。
let a = NaN == NaN;
console.log(a); // false
3.5 语句
3.5.1 if 语句
求值结果不一定是boolean
值。ECMAScript 会自动调用Boolean()
函数将这个表达式的值转换为boolean
值。
3.5.2 do-while 语句
后测试循环语句
1)do-while 语法示例:
// 只要i 小于10,循环就会重复执行。i 从 0 开始,每次循环递增 2。
let i = 0;
do {i += 2;
} while (i < 10);
2)使用场景:
需要循环体内的代码在退出前至少执行 1 次。
3.5.3 while 语句
先测试循环语句
while循环语法示例:
// 变量i 从0 开始,每次循环递增2。只要i 小于10,循环就会继续
let i = 0;
while (i < 10) {i += 2;
}
3.5.4 for 语句
也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式
for 语句示例:
let count = 10;
for (let i = 0; i < count; i++) {console.log(i);
}
与下面 while 效果相同:
let count = 10;
let i = 0;
while (i < count) {console.log(i);i++;
}
无法通过
while
循环实现的逻辑,同样也无法使用for
循环实现。因此for
循环只是将循环相关的代码封装在了一起而已。
在for 循环的初始化代码中,其实是可以不使用变量声明关键字的。不过,初始化定义的迭代器变量在循环执行完成后几乎不可能再用到了。因此,最清晰的写法是使用let 声明迭代器变量,这样就可以将这个变量的作用域限定在循环中。
初始化、条件表达式和循环后表达式都不是必需的。因此,下面这种写法可以创建一个无穷循环:
for (;;) { // 无穷循环doSomething();
}
如果只包含条件表达式,那么for 循环实际上就变成了while 循环:
let count = 10;
let i = 0;
for (; i < count; ) {console.log(i);i++;
}
这种多功能性使得for 语句在这门语言中使用非常广泛。
3.5.5 for-in 语句
for-in
语句是一种严格的迭代语句,用于枚举对象中的非符号键属性。
for (const propName in window) {// 循环显示 BOM 对象 window 的所有属性document.write(propName);
}
ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序,返回的顺序可能会因浏览器而异。
3.5.6 for-of 语句
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素。
示例:
for (const el of [2,4,6,8]) {document.write(el);
}
ES2018 对for-of 语句进行了扩展,增加了for-await-of 循环,以支持生成期约(promise)的异步可迭代对象
3.5.7 标签语句
标签语句用于给语句加标签。
示例:
start: for (let i = 0; i < count; i++) {console.log(i);
}
在这个例子中,start
是一个标签,可以在后面通过break
或continue
语句引用。标签语句的典型应用场景是嵌套循环。
3.5.8 break 和continue 语句
break
和continue
语句为执行循环代码提供了更严格的控制手段。其中,
- break 语句用于立即退出循环,强制执行循环后的下一条语句。
continue
语句也用于立即退出循环,但会再次从循环顶部开始执行。
示例:
let num = 0;
for (let i = 1; i < 10; i++) {if (i % 5 == 0) {break;}num++;
}
console.log(num); // 4
之所以循环执行了4 次,是因为当i 等于5 时,break 语句会导致循环退出,该次循环不会执行递增num 的代码。
如果将 break 换成 continue,则会出现不同的效果:
let num = 0;
for (let i = 1; i < 10; i++) {if (i % 5 == 0) {continue;}num++;
}
console.log(num); // 8
当 i =5 时,循环会在递增 num 之前退出,但会执行下一次迭代,此时 i 是6。然后,循环会一直执行到自然结束,即 i 等于10。最终num 的值是8 而不是9,是因为continue 语句导致它少递增了一次。
在嵌套循环中,break
和continue
都可以与标签语句
一起使用,返回代码中特定的位置。示例如下:
let num = 0;
outermost:
for (let i = 0; i < 10; i++) {for (let j = 0; j < 10; j++) {if (i == 5 && j == 5) {break outermost;}num++;}
}
console.log(num); // 55
本例中,outermost
标签标识的是第一个 for 语句。组合使用 标签语句
和break
、continue
能实现复杂的逻辑,但也容易出错。注意标签要使用描述性强的文本,而嵌套也不要太深。
3.5.9 with 语句
用途:将代码作用域设置为特定的对象。
使用的主要场景:是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便利,如下所示:
let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;
上述代码中每一行都用到了location
对象。如果使用with
语句,就可以少写一些代码:
with(location) {let qs = search.substring(1);let hostName = hostname;let url = href;
}
with
语句用于连接 location
对象。在该语句内部,每个变量首先会被认为是一个局部变量。如果没有找到该局部变量,则会搜索location
对象,看它是否有一个同名的属性。如果有,则该变量会被求值为location
对象的属性。
⚠️ 警告!
1、严格模式不允许使用with
语句,否则会抛出错误。
2、由于with 语句影响性能且难于调试其中的代码,通常不推荐在产品代码中使用with语句。
3.5.10 switch 语句
switch
语句是与 if
语句紧密相关的一种流控制语句,从其他语言借鉴而来。ECMAScript 中 switch 语句跟 C 语言中 switch 语句的语法非常相似,如下所示:
switch (expression) {case value1:statementbreak;case value2:statementbreak;case value3:statementbreak;case value4:statementbreak;default:statement
}
每个case
(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”,其中:
break
关键字:跳出switch 语句。如果没有break
,则代码会继续匹配下一个条件。default
关键字:不满足任何条件时,指定默认执行的语句(相当于else
语句)。
如果确实需要连续匹配几个条件,那么写个注释表明是有意忽略break
,如下所示:
switch (i) {case 25:/*跳过*/case 35:console.log("25 or 35");break;case 45:console.log("45");break;default:console.log("Other");
}
虽然switch 语句是从其他语言借鉴过来的,但ECMAScript 为它赋予了一些独有的特性。
- switch 语句可以用于所有数据类型(在一些语言中,它只能用于数值);
- 条件的值不需要是常量,也可以是变量或表达式。
示例如下:
switch ("hello world") {case "hello" + " world":console.log("Greeting was found.");break;case "goodbye":console.log("Closing was found.");break;default:console.log("Unexpected message was found.");
}
第1个条件使用的是表达式,求值为两个字符串拼接后的结果。因为拼接后的结果等于switch 的参数,所以会输出"Greeting wasfound."。
既然能够在条件判断中使用表达式,那么就可以在判断中加入更多逻辑:
let num = 25;
switch (true) {case num < 0:console.log("Less than 0.");break;case num >= 0 && num <= 10:console.log("Between 0 and 10.");break;case num > 10 && num <= 20:console.log("Between 10 and 20.");break;default:console.log("More than 20.");
}
switch 语句在比较每个条件的值时,会使用全等操作符,因此不会强制转换数据类型(比如,字符串"10"不等于数值10)。
3.6 函数
语法示例:
// 定义函数sayHi()
function sayHi(name, message) {console.log("Hello " + name + ", " + message);
}// 调用函数sayHi()
sayHi("Nicholas", "how are you today?");
1)ECMAScript 中的函数不需要指定是否返回值。
任何函数在任何时间都可以使用return
语句来返回函数的值,用法是后跟要返回的值。例如:
function sum(num1, num2) {return num1 + num2;
}// 调用 sum()
const result = sum(5, 10);
2)只要碰到 return
语句,函数就会立即停止执行并退出。
示例:
function sum(num1, num2) {return num1 + num2;console.log("Hello world"); // 不会执行
}
3)一个函数里也可以有多个return
语句
示例代码:
function diff(num1, num2) {if (num1 < num2) {return num2 - num1;} else {return num1 - num2;}
}
3)return 语句不带返回值。
函数会立即停止执行并返回undefined
。
这种用法常用于提前终止函数执行,并不是为了返回值。
示例:
function sayHi(name, message) {return;// 不会执行console.log("Hello " + name + ", " + message);
}
最佳实践:
函数要么返回值,要么不返回值。只在某个条件下返回值的函数会带来麻烦,尤其是调试时。
4)严格模式对函数的一些限制
- 函数不能以
eval
或arguments
作为名称; - 函数的参数不能叫
eval
或arguments
; - 两个命名参数不能拥有同一个名称。
如果违反上述规则,则会导致语法错误,代码也不会执行。
小结
JavaScript 的核心语言特性在ECMA-262 中以伪语言ECMAScript 的形式来定义。
ECMAScript 包含所有基本语法、操作符、数据类型和对象,能完成基本的计算任务,但没有提供获得输入和产生输出的机制。理解ECMAScript 及其复杂的细节是完全理解浏览器中JavaScript 的关键。
1)ECMAScript 中的基本元素。
- ECMAScript 中的基本数据类型包括
Undefined
、Null
、Boolean
、Number
、String
和Symbol
。 - 与其他语言不同,ECMAScript 不区分整数和浮点值,只有
Number
一种数值数据类型。 - Object 是一种复杂数据类型,它是这门语言中所有对象的基类。
- 严格模式为这门语言中某些容易出错的部分施加了限制。
- ECMAScript 提供了C 语言和类C 语言中常见的很多基本操作符,包括数学操作符、布尔操作符、关系操作符、相等操作符和赋值操作符等。
- 流控制语句大多是从其他语言中借鉴而来(如if 语句、for 语句和switch语句等)。
2) ECMAScript 函数与其他语言中的函数的区别
- 不需指定返回值,函数可以随时返回任何值。
- 不指定返回值的函数,实际上会返回特殊值
undefined
。
第4章 变量、作用域与内存
主要内容:
- 通过变量使用原始值与引用值;
- 理解执行上下文;
- 理解垃圾回收。
4.1 原始值与引用值
ECMAScript 变量可以包含两种不同类型的数据:原始值 和 引用值 。
- 原始值(primitive value):最简单的数据;
- 引用值(reference value):由多个值构成的对象。
在给一个变量赋值时,JavaScript 引擎必须确定这个值是原始值还是引用值。
- 保存原始值的变量是按值访问的,因为我们操作的就是存储在变量中的实际值;
- 保存引用值的变量是按引用访问的。
JavaScript 不允许直接访问内存位置,因此也就不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用而非实际的对象本身
4.1.1 动态属性
原始值和引用值的定义方式类似。但是,在变量保存这个值之后,可以对这个值做什么,则大有不同。对于引用值而言,可以随时添加、修改和删除其属性和方法。
如下所示:
let person = new Object();
person.name = "Nicholas";
console.log(person.name); // "Nicholas"
此代码中,首先创建了一个对象,并把它保存在变量person
中。然后,给这个对象添加了一个名为name
的属性,并给这个属性赋值字符串 “Nicholas”。就可以访问这个新属性,直到对象被销毁或属性被显式地删除。
原始值不能有属性,尽管尝试给原始值添加属性不会报错。比如:
let name = "Nicholas";
name.age = 27;
console.log(name.age); // undefined
前端的葵花宝典 - 红宝书《JavaScript高级程序设计(第4版)》学习笔记相关推荐
- JavaScript高级程序设计第四版学习--第二十四章
title: JavaScript高级程序设计第四版学习–第二十四章 date: 2021-5-31 10:46:01 author: Xilong88 tags: JavaScript 本章内容: ...
- 阅读JavaScript高级程序设计(第二版)笔记
第一章js简介 JavaScript诞生在1995年,当时负责进行输入型验证. JavaScript是一种专为与网页交互而设计的脚本语言,分为 : 1. ECMAScript核心语言功能. 2.文档对 ...
- javascript 高级程序设计(第4版)阅读笔记(三)
第3章,内容很长,所以更得慢,主要讲的是ECMAScript es的语言基础:语法.数据类型.基本操作符.流控制语句.理解函数,ECMAScript 的语法很大程度上借鉴了 C 语言和其他类 C ...
- JavaScript高级程序设计第三版.CHM【带实例】
从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...
- JavaScript高级程序设计[第3版]
JavaScript高级程序设计[第3版] package xyz.huning.toolkit.pdf;import java.io.FileOutputStream; import java.io ...
- JavaScript高级程序设计 第4版----String
JavaScript高级程序设计 第4版----String 文章目录 JavaScript高级程序设计 第4版----String 1.JavaScript 字符 2.字符串操作方法 1.conca ...
- Js高级程序设计第三版学习(十二章)
Js高级程序设计第三版学习(十二章) 第十二章 DOM2和DOM3 1.样式: 访问样式属性 任何支持style特性的HTML元素都有一 ...
- 新书-JavaScript高级程序设计:第2版(预订中,估价)
http://www.china-pub.com/196857 JavaScript的应用在广度和深度上日益扩大和加深,前端开发亟待掌握的JavaScript技能也越来越具有挑战性. 这个新版本几乎全 ...
- JavaScript高级程序设计第三版 第3章 基本概念
第3章 基本概念 3.1 语法 3.1.1 区分大小写 3.1.2 标识符 3.1.3 注释 3.1.4 严格模式 3.1.5 语句 3.2 关键字和保留字 3.3 变量 3.4 数据类型 3.4.1 ...
最新文章
- SUMO 设置车辆的换道模型
- .NET Standard 2.0 特性介绍和使用指南
- python中{%%}在HTML中的用法
- 【开发必知】基本缓存概念
- 查询今天、昨天、本周、上周、本月、上月数据
- linux console 下载 jdk
- FFmpeg下载编译好的WINDOWS库头文件
- Python + ElasticSearch:有了这个超级武器,你也可以报名参加诗词大会了!
- 计算机pdf转换word,PDF怎么转换成Word?解决PDF转Word的小妙招
- JAVA 实现《坦克大战联机版》游戏
- 计算机排查方法,电脑开不了机问题排查方法图解(硬件排查)
- 这样处理,Java中的注释代码也会执行
- apicloud缓存
- 通过驱动断链来隐藏驱动
- html中图片亮度调节,HTML+CSS+JS 模仿 Win10 亮度调节效果
- python基础学习 1
- 辨析:分段函数是不是初等函数?
- 用qt做网易云音乐--01标题栏实现
- Http的多线程下载
- LockSupport的park和unpark的原理
热门文章
- 【转载保存】推荐ApacheCN开源的一个机器学习路线图
- 【链接保存】十分钟上手sklearn:安装,获取数据,数据预处理
- 【转载保存】HtmlUnit的使用
- windows 安装tensorflow2.0
- 持续定义Saas模式云数据仓库+BI
- 阿里云机器学习怎么玩?这本新手入门指南揭秘了!
- “阿里巴巴小程序繁星计划”:20亿扶持200万小程序开发者和100万商家
- 极狐(GitLab)发布首款“GitNative”DevOps云一体化解决方案
- 云计算到底是谁发明的?
- AI行业真实现状:做芯片没工作,做视觉、语音血赚