红宝书前三章总结

400 阅读16分钟

第一章

JavaScript 诞生于 1995 年,一开始是为了解决在客户端完成一些基本的验证任务,如今,JavaScript 的用途早已不再局限于简单的数据验证,而是具备了与浏览器窗口及其内容等几乎所有方面交互的能力。今天的 JavaScript 已经成为一门功能全面的编程语言,能够处理复杂的计算和交互,拥有了闭包、匿名(lamda,拉姆达)函数,甚至元编程等特性。

1997 年,以 JavaScript 1.1 为蓝本的建议被提交给了欧洲计算机制造商协会(ECMA,European Computer Manufacturers Association)。该协会指定 39 号技术委员会(TC39,Technical Committee #39) 负责“标准化一种通用、跨平台、供应商中立的脚本语言的语法和语义”(www.ecmainternational.org/memento/TC3… TC39 由来自 Netscape、Sun、微软、Borland 及其他关注脚本语言 发展的公司的程序员组成,他们经过数月的努力完成了 ECMA-262——定义一种名为 ECMAScript(发 音为“ek-ma-script”)的新脚本语言的标准。

第二年,ISO/IEC(International Organization for Standardization and International Electrotechnical Commission,国标标准化组织和国际电工委员会)也采用了 ECMAScript 作为标准(即 ISO/IEC-16262)。 自此以后,浏览器开发商就开始致力于将 ECMAScript 作为各自 JavaScript 实现的基础,也在不同程度 上取得了成功。

1.1 JavaScript 实现

虽然 JavaScript 和 ECMAScript 通常都被人们用来表达相同的含义,但 JavaScript 的含义却比 ECMA-262 中规定的要多得多。一个完整的 JavaScript 实现应该由下列三个不同的部分组成:核心(ECMAScript)、文档对象模型(DOM) 、浏览器对象模型(BOM)

image.png

1.1.1 ECMAScript

我们常见的 Web 浏览器只是 ECMAScript 实现可能的宿主环境之一。宿主环境不仅提供基本的 ECMAScript 实现,同时也会提供该语言的扩展,以便语言与环境之间对接交互。而这些扩展——如 DOM,则利用ECMAScript 的核心类型和语法提供更多更具体的功能,以便实现针对环境的操作。其他 宿主环境包括 Node(一种服务端 JavaScript 平台)和 Adobe Flash。

ECMA-262 标准规定了这门语言的下列组成部分:语法、类型、语句、关键字、保留字、操作符、对象

ECMAScript 就是对实现该标准规定的各个方面内容的语言的描述。JavaScript 实现了 ECMAScript, Adobe ActionScript 同样也实现了 ECMAScript。

ECMAScript 兼容:
支持 ECMA-262 描述的所有“类型、值、对象、属性、函数以及程序句法和语义”;
支持 Unicode 字符标准。

兼容的实现还可以进行下列扩展:
添加 ECMA-262 没有描述的“更多类型、值、对象、属性和函数”。ECMA-262 所说的这些新增特性,主要是指该标准中没有规定的新对象和对象的新属性;
支持 ECMA-262 没有定义的“程序和正则表达式语法”。

下表列出了 ECMAScript 受主流 Web 浏览器支持的情况

image.png

1.1.2 文档对象模型(DOM)

文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的应用程序编 程接口(API,Application Programming Interface)。DOM 把整个页面映射为一个多层节点结构。HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。

1.1.3 浏览器对象模型(BOM)

从根本上讲,BOM 只处理浏览器窗口和框架;但人们习惯上也把所有针对浏览器的 JavaScript 扩展 算作 BOM 的一部分。由于没有 BOM 标准可以遵循,因此每个浏览器都有自己的实现。虽然也存在一些事实标准,例如 要有 window 对象和 navigator 对象等,但每个浏览器都会为这两个对象乃至其他对象定义自己的属 性和方法。现在有了 HTML5,BOM 实现的细节有望朝着兼容性越来越高的方向发展。

1.2 JavaScript 版本

作为 Netscape“继承人”的 Mozilla 公司,是目前唯一还在沿用最初的 JavaScript 版本编号序列的浏 览器开发商。在 Netscape 将源代码提交给开源的 Mozilla 项目的时候,JavaScript 在浏览器中的最后一个 版本号是 1.3。(如前所述,1.4 版是只针对服务器的实现。)后来,随着 Mozilla 基金会继续开发 JavaScript, 添加新的特性、关键字和语法,JavaScript 的版本号继续递增。

1.3 小结

JavaScript 是一种专为与网页交互而设计的脚本语言,由下列三个不同的部分组成:
ECMAScript,由 ECMA-262 定义,提供核心语言功能;
文档对象模型(DOM),提供访问和操作网页内容的方法和接口;
浏览器对象模型(BOM),提供与浏览器交互的方法和接口。

JavaScript 的这三个组成部分,在当前五个主要浏览器(IE、Firefox、Chrome、Safari 和 Opera)中 都得到了不同程度的支持。其中,所有浏览器对 ECMAScript 第 3 版的支持大体上都还不错,而对 ECMAScript 5 的支持程度越来越高,但对 DOM 的支持则彼此相差比较多。对已经正式纳入 HTML5 标 准的 BOM 来说,尽管各浏览器都实现了某些众所周知的共同特性,但其他特性还是会因浏览器而异。

第二章小结

JavaScript是通过<script>元素插入到HTML页面中的。这个元素可用于把JavaScript代码嵌入到HTML页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的JavaScript。本章的重点可以总结如下:

(1)要包含外部JavaScript文件,必须将src属性设置为要包含文件的URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。

(2)所有<script>元素会依照它们在网页中出现的次序被解释。在不使用defer和async属性的情况下,包含在<script>元素中的代码必须严格按次序解释

(3)对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。

(4)可以使用defer属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。

(5)可以使用async属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。

(6)通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。

3.3 变量

(1)变量是一个用来存放数据的容器,其本质是内存里的一块空间,用来存储数据

(2)变量的初始化是声明变量并赋值,声明的本质就是去内存申请空间

(3)var关键字声明作用域是函数级的,变量声明会自动提升到函数顶部(注意不包含赋值),在全局作用域声明的变量会成为window对象的属性

function foo(){
    console.log(age);
    var age = 26;
}
foo(); // 不报错,打印出undefined,而不是26

(4)let关键字声明作用域是块级的(也就是{}范围内),变量的声明不会在作用域中提升,在let声明之前的执行瞬间称为暂时性死区,在全局作用域声明的变量不会成为window对象的属性

(5)for循环中var/let声明

// 退出循环时,迭代变量保存的是导致退出循环的值:5
for(var i = 0; i < 5; i++){
    setTimeout(() => console.log(i), 0)
} // 5 5 5 5 5

// 使用let声明变量时,js引擎在后台会给每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同的变量实例
for(let i = 0; i < 5; i++){
    setTimeout(() => console.log(i), 0)
} // 0 1 2 3 4

(6)const关键字声明也是块级的,声明的同时必须赋值且不能修改

(7)开发中优先使用const,let次之,这是因为const声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作

(8)js是一种弱类型(动态语言),不用提前声明变量的类型,在运行时会被自动确认

3.4 数据类型

3.4.1 数据类型的分类

简单数据类型:Number、String、Boolean、Undefined、Null、Symbol(ES6新增)

复杂数据类型:Object

3.4.2 typeof可用来获取检测变量的数据类型

"undefined"——值未定义
"boolean"——布尔值
"string"——字符串
"number"——数值
"object"——对象或者null
"function"——函数
"symbol"——符号

3.4.3 Undefined和Null

(1)当使用var或let声明了变量但没有初始化,此时变量的值为undefined
注意: 对未声明的变量调用typeof也会返回undefined

(2)null表示一个空对象指针,调用typeof会返回"object"

异同一: undefined是由null值派生而来的,所以ECMA-262将它们定义为表面相等

var a // undefined
var b = null
console.log(a == b); // true
console.log(a === b); // false

异同二: 任何时候,只要变量要保存对象,而当时又没有那个对象可以保存,就要用null来填充该变量。这样可以保持null是对空对象指针的语义,并进一步将其与undefined区分开。

异同三: 一个声明后没有赋值的变量会有默认值undefined,一个声明变量赋值null,存的值为空

    var a // undefined
    var b = null // 值为空
    console.log(1 + a); //NaN
    console.log(1 + b); //1

3.4.4 Boolean

(1)与数字型相加,true值为1,false为0

(2)类型转换: 代表空、否定的值都会被转换为false,如''、0、NaN、null、undefined,其余都会被转为true

3.4.5 Number

(1)在js中八进制前面加0,十六进制前面加0x
注意: 八进制字面量在严格模式下是无效的,会导致js引擎抛出语法错误,应使用前缀0o

(2)0.1+0.2=0.300 000 000 000 000 04,这是因为采用IEEE 745数值导致的舍入误差

(3)最大值Number.MAX_VALUE,最小值Number.MIN_VALUE(大于0)

(4)无穷大Infinity,无穷小-Infinity,可以用isFinite()函数判断一个值是否无限

(5)NaN代表一个非数值,用来表示本来要返回数值的操作失败了

console.log(0/0); // NaN
console.log(1/0); // Infinity
console.log(1/-0); // -Infinity

NaN不等于NaN在内的任何值

console.log(NaN == NaN) // false

isNaN()判断变量是否为"不是数值",该函数会尝试把它转换为数值,某些非数值的值可以直接转换为数值,比如字符串"10"或布尔值

(6)数值转换
Number()是强制转换函数,可用于任何数据类型,有如下规则:
null返回0、undefined返回NaN、字符串数字会忽略前面的零返回十进制整形/浮点型数值、字符串数字包含有效十六进制格式"0xf"则返回与之对应的十进制数值、空字符串返回0、其他字符串情况返回NaN

parseInt(string)、parseFloat(string)函数,主要用于将字符串转换为数值,有如下规则:
空字符返回NaN,parseInt能指定进制如parseInt("AF", 16)//175、parseFloat对十六进制数值始终返回0

利用算术运算隐式转换(- * /),其中减0最常用:'12' - 0

3.4.6 String

(1)HTML标签用双引号,js推荐用单引号

(2)转义符
\n换行、\反斜杠、'单引号、"双引号、\t缩进、\b空格、\r回车、\f换页、`反引号

(3)字符串是不可变的

(4)转换为字符串 toString():返回当前值的字符串等价物,null和undefined没有toString方法,toString可以接受一个底数参数,即以什么底数来输出数值的字符串表示。

var num = 10
console.log(num.toString()); // 10
console.log(num.toString(2)); // 1010
console.log(num.toString(8)); // 12
console.log(num.toString(16)); // a

String()函数逻辑:优先调用toString()方法返回结果、如果值为null返回"null"、如果值为undefined返回"undefined"

加号拼接字符串(隐式转换):字符串相加会把任何类型转化为字符串

3.4.7 Symbol

符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突危险

3.4.8 Object

(1)对象其实就是一组数据和功能的集合,对象通过new操作符后跟对象类型的名称来创建,然后可以给对象添加属性和方法

(2)每个Object实例都有如下属性和方法
constructor:用于创建当前对象的函数。
hasOwnProperty(propertyName): 用于判断当前对象实例(不是原型)上是否存在给定的属性。
isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。
toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
toString():返回对象的字符串表示。
valueOf():返回对象对应的字符串、数值或布尔值表示。通常于toString()的返回值相同。

3.5 操作符

操作符包含:算数运算符、递增递减运算符、比较运算符、逻辑运算符、赋值运算符 运算符优先级

image.png

3.5.1 一元操作符

只操作一个值的操作符,有:递增递减、一元加或减

3.5.2 位操作符

操作内存中表示数据的比特(位),有:按位非(~)、按位与(&)、按位或(|)、按位异或(^)、左移(<<)、有符号右移(>>)、无符号右移(>>>)

3.5.3 布尔操作符

布尔操作符一共有3个: (1)逻辑非(!)
同时使用两个叹号(!!),相当于调用了转型函数Boolean (2)逻辑与(&&)
逻辑与操作符是一种短路操作符,如果第一个操作数是false,第二个操作数将不会被求值 (3)逻辑或(||)
逻辑或操作符也是一种短路操作符,如果第一个操作数是true,第二个操作数将不会被求值

3.5.4 乘性操作符

乘法操作符(*)、除法操作符(/)、取模操作符(%)

3.5.5 指数操作符

3的平方表示方法:3*2、Math.pow(3,2)、3*=2

3.5.6 加性操作符

即加法和减法操作符,注意字符串加数值,会转化为字符串

3.5.7 关系操作符

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)、大于等于(>=)
数值和字符串比较,字符串就会先被转换位数值;与NaN比较,无论小于还是大于等于,结果都会返回false

3.5.8 相等操作符

等于(==)和不等于(!=)、全等(===)和不全等(!==),前者做比较时忽略数据类型

3.5.9 条件操作符

给max赋予最大值let max = (num1 > num2) ? num1 : num2

3.5.10 赋值操作符

*=、/=等简写不会提升性能

3.5.11 逗号操作符

逗号操作符可以用来在一条语句中执行多个操作let num1 = 1, num2 = 2, num3 = 3; 在赋值时使用都逗号操作符分隔值,最终会返回最后一个值let num = (5,1,0); // num的值为0

3.6语句

3.6.1 if语句

连续使用多个语句

if(condition1) statement1 else if(condition2) statement2 else statement3

3.6.2 do-while语句

后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值

3.6.3 while语句

先测试循环语句,即先检测退出条件,再执行循环体内的代码

3.6.4 for语句

for循环只是将循环相关的代码封装在了一起,无法通过while实现的逻辑,也就无法用for实现

3.6.5 for-in语句

是一种严格的迭代语句,用于枚举对象中非符号键属性

for(const propName in window){
    document.write(propName);
}

循环显示了BOM对象window的所有属性,for-in语句不能保证返回对象属性的顺序

3.6.6 for-of语句

是一种严格的迭代语句,用于遍历可迭代对象的元素

for(const el of [2,4,6,8]){
    document.write(el);

for-of循环会按照可迭代对象的next()方法产生值得顺序迭代元素,ES2018增加了for-await-of循环,以支持生成期promise得异步可迭代对象

3.6.7 标签语句

标签语句用于给语句加标签

start:for(let i = 0; i < count; i++) {
    console.log(i)
}

start是一个标签,可以在后面通过break或者continue语句引用,主要在嵌套循环中使用

3.6.8 break和continue语句

break跳出循环,continue跳出当次循环

3.6.9 with语句

with语句的用途是将代码作用域设置为特定对象;针对一个对象反复操作场景,就可以将代码作用域设置为该对象

let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;

使用with语句

with(location) {
    let qs = search.substring(1);
    let hostName = hostname;
    let url = href;
}

严格模式不允许使用with语句,并且因为with语句影响性能且难于调试代码,不推荐在产品代码中使用

3.6.10 switch语句

switch语句可以用于所有数据类型,条件的值也可以是遍历或者表达式

3.7 函数

ECMAScript中的函数不需要指定是否返回值;return语句也可以不带返回值,函数会立即停止并返回undefined,严格模式下规定:
(1)函数不能以eval或arguments作为名称;
(2)函数的参数不能叫eval或arguments;
(3)两个命名参数不能叫同一个名称。

3.8 小结

(1)ECMAScript中的基本数据类型包括Undefined、Null、Boolean、Number、String和Symbol。
(2)与其他语言不同,ECMAScript不区分整数和浮点值,只有Number一种数值数据类型。
(3)Object是一种复杂数据类型,它是这门语言中所有对象的基类。
(4)严格模式为这门语言中某些容易出错的部分施加了限制。
(5)ECMAScript提供了C语言和类C语言中常见的很多基本操作符,包括数学操作符、布尔操作符、关系操作符、相等操作符和赋值操作符等。
(6)这门语言中的流控制语句大多是从其他语言中借鉴而来的,比如if语句、for语句和switch语句等。