重学红宝书

重学红宝书

什么是js

js包括ECMAScript DOM BOM几个部分

ECMAScript

就是js的一些语法api等等等

DOM

其表示文档对象模型,就是把html页面抽象成树节点的方式

比如



 <head> 


 <title>Sample Page</title> 

 </head> 

 <body> 

 <p> Hello World!</p> 

 </body> 

</html> 

这些代码可以用树节点的形式表示

DOM可以通过创建文档的书,让开发者可以控制网页内容和结构。使用DOM api可以轻松删除添加替换修改节点

BOM

其表示浏览器对象模型,提供与浏览器交互的方法和接口,比如打开新窗口等等

html中的js
元素

将js中插入html主要使用script元素,其标签有以下八个属性

async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其

他脚本加载。只对外部脚本文件有效。

charset:可选。使用 src 属性指定的代码字符集。这个属性很少使用,因为大多数浏览器不

在乎它的值。

crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin=

"anonymous"配置文件请求不必设置凭据标志。crossorigin="use-credentials"设置凭据

标志,意味着出站请求会包含凭据。

defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。

在 IE7 及更早的版本中,对行内脚本也可以指定这个属性。

integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,

Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,

脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提

供恶意内容。

language:废弃。最初用于表示代码块中的脚本语言(如"JavaScript"、"JavaScript 1.2"

或"VBScript")。大多数浏览器都会忽略这个属性,不应该再使用它。

src:可选。表示包含要执行的代码的外部文件。

type:可选。代替 language,表示代码块中脚本语言的内容类型(也称 MIME 类型)。按照惯

例,这个值始终都是"text/javascript",尽管"text/javascript"和"text/ecmascript"

都已经废弃了。JavaScript 文件的 MIME 类型通常是"application/x-javascript",不过给

type 属性这个值有可能导致脚本被忽略。在非 IE 的浏览器中有效的其他值还有

"application/javascript"和"application/ecmascript"。如果这个值是 module,则代码会被当成 ES6 模块,而且只有这时候代码中才能出现 import 和 export 关键字。

语言基础

区分大小写,驼峰命名法

严格模式

脚本开头使用 “use strict” 函数内部开头使用也可以在里面开启严格模式,es3的一些不规范写法会在严格模式下被处理,对于不安全的活动会抛出错误

语句

可以没分号,也可以有,最好有

变量

es变量是松散类型的,变量可以保存为任何类型数据,有3个关键字声明变量 var let const;var所有es版本都可以使用,const和let只能es6以上版本使用

var

1.var声明作用域

关键的问题在于,使用 var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用 var

在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:

function test() { 

 var message = "hi"; // 局部变量

} 

test(); 

```javascript

```

console.log(message); // 出错!

这里,message 变量是在函数内部使用 var 定义的。函数叫 test(),调用它会创建这个变量并给

它赋值。调用之后变量随即被销毁,因此示例中的最后一行会导致错误。不过,在函数内定义变量时省

略 var 操作符,可以创建一个全局变量:

function test() { 

 **message = "hi"; //** **全局变量**

} 

test(); 

console.log(message); // "hi" 

去掉之前的 var 操作符之后,message 就变成了全局变量。只要调用一次函数 test(),就会定义

这个变量,并且可以在函数外部访问到

2.var声明提升

使用var时,下面代码不会报错,因为使用var声明的变量会将变量声明提到函数作用域顶部

function foo() { 
 console.log(age); 
 var age = 26; 
} 
foo(); // undefined 
之所以不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:
function foo() { 
 var age; 
 console.log(age); 
 age = 26; 
} 
foo(); // undefined
let声明

let和var左右差不多,但let不会变量提升,有着自己的块级作用域,

var声明的则是函数作用域

if (true) { 

 var name = 'Matt'; 

 console.log(name); // Matt 

} 

console.log(name); // Matt 26 第 3 章 语言基础

if (true) { 

 let age = 26; 

 console.log(age); // 26 

} 

console.log(age); // ReferenceError: age 没有定义

在这里,age 变量之所以不能在 if 块外部被引用,是因为它的作用域仅限于该块内部。块作用域

是函数作用域的子集,因此适用于 var 的作用域限制同样也适用于 let。

let 也不允许同一个块作用域中出现冗余声明。这样会导致报错:

var name; 

var name; 

let age; 

let age; // SyntaxError;标识符 age 已经声明过了

当然,JavaScript 引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标

识符不会报错,而这是因为同一个块中没有重复声明:

var name = 'Nicholas'; 

console.log(name); // 'Nicholas' 

if (true) { 

 var name = 'Matt'; 

 console.log(name); // 'Matt' 

} 

let age = 30; 

console.log(age); // 30 

if (true) { 

 let age = 26; 

 console.log(age); // 26 

} 

对声明冗余报错不会因混用 let 和 var 而受影响。这两个关键字声明的并不是不同类型的变量,

它们只是指出变量在相关作用域如何存在。

var name; 

let name; // SyntaxError 

let age; 

var age; // SyntaxError 

\1. 暂时性死区

let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升。

// name 会被提升

console.log(name); // undefined 

var name = 'Matt'; 

// age 不会被提升

console.log(age); // ReferenceError:age 没有定义

let age = 26; 

在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方

式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此

阶段引用任何后面才声明的变量都会抛出 ReferenceError。3.3 变量 27

  1. 全局声明

与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声

明的变量则会)。

var name = 'Matt';

console.log(window.name); // 'Matt'

let age = 26;

console.log(window.age); // undefined

不过,let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续。因此,为了

避免 SyntaxError,必须确保页面不会重复声明同一个变量。

3.条件声明

因为let声明只在其代码块内生效,所以于 let 这个新的 ES6 声明关键字,不能依赖条件声明模式

注意:不能使用 let 进行条件式声明是件好事,因为条件声明是一种反模式,它让程序变

得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式

4.for循环中的let

for (var i = 0; i < 5; ++i) { 

 setTimeout(() => console.log(i), 0) 

} 

// 你可能以为会输出 0、1、2、3、4 

// 实际上会输出 5、5、5、5、5 

之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时

逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。

而在使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。

每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循

环执行过程中每个迭代变量的值。

for (let i = 0; i < 5; ++i) { 

 setTimeout(() => console.log(i), 0) 

} 

// 会输出 0、1、2、3、4 

这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of循环。

const

const与let基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。

const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,

那么修改这个对象内部的属性并不违反 const 的限制。

声明分格及最佳实践

1.不使用var

2.const优先,let次之

数据类型

简单数据类型:

1.undefined:表示值为定义

2.boolean:布尔值

3.string:字符串

4.number:数值

5.null:空值

6.symbol:符号

复杂数据类型:

object:对象


type of操作符

 "undefined"表示值未定义;

 "boolean"表示值为布尔值;

 "string"表示值为字符串;

 "number"表示值为数值;

 "object"表示值为对象(而不是函数)或 null;

 "function"表示值为函数;

 "symbol"表示值为符号。

Undefined

其类型只有一个值,就是特殊值undefined,当使用 var 或 let 声明了变量但没有初始化时,就相当于给变量赋予了 undefined 值

注意,包含 undefined 值的变量跟未定义变量是有区别的。请看下面的例子:

let message; // 这个变量被声明了,只是值为 undefined 

// 确保没有声明过这个变量

// let age 

console.log(message); // "undefined" 

console.log(age); // 报错

Null类型

其类型只有一个特殊值null,逻辑上讲,null 值表示一个空对象指针,这也是给typeof 传一个 null 会返回"object"的原因:

在定义将来要保存对象值的变量时,建议使用 null 来初始化,不要使用其他值。这样,只要检查这个变量的值是不是 null 就可以知道这个变量是否在后来被重新赋予了一个对象的引用

undefined 值是由 null 值派生而来的,因此 ECMA-262 将它们定义为表面上相等,如下面的例子所示:

console.log(null == undefined); // true 

用等于操作符(==)比较 null 和 undefined 始终返回 true。但要注意,这个操作符会为了比较

而转换它的操作数(本章后面将详细介绍)。

即使 null 和 undefined 有关系,它们的用途也是完全不一样的。如前所述,永远不必显式地将变量值设置为 undefined。但 null 不是这样的。任何时候,只要变量要保存对象,而当时又没有那个

对象可保存,就要用 null 来填充该变量。这样就可以保持 null 是空对象指针的语义,并进一步将其与 undefined 区分开来。

Boolean

Boolean(布尔值)类型是 ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和 false。

这两个布尔值不同于数值,因此 true 不等于 1,false 不等于 0。

虽然布尔值只有两个,但所有其他 ECMAScript 类型的值都有相应布尔值的等价形式。要将一个其

他类型的值转换为布尔值,可以调用特定的 Boolean()转型函数:

Number类型

Number 类型使用 IEEE 754 格式表示整

数和浮点值(在某些语言中也叫双精度值)。不同的数值类型相应地也有不同的数值字面量格式。

整数也可以用八进制(以 8 为基数)或十六进制(以 16 为基数)字面量表示。对于八进制字面量,

第一个数字必须是零(0),然后是相应的八进制数字(数值 0~7)。如果字面量中包含的数字超出了应

有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数,如下所示:

let octalNum1 = 070; // 八进制的 56 

let octalNum2 = 079; // 无效的八进制值,当成 79 处理

let octalNum3 = 08; // 无效的八进制值,当成 8 处理

八进制字面量在严格模式下是无效的,会导致 JavaScript 引擎抛出语法错误。①

要创建十六进制字面量,必须让真正的数值前缀 0x(区分大小写),然后是十六进制数字(0~9 以

及 A~F)。十六进制数字中的字母大小写均可。下面是几个例子:

let hexNum1 = 0xA; // 十六进制 10 

let hexNum2 = 0x1f; // 十六进制 31 

使用八进制和十六进制格式创建的数值在所有数学操作中都被视为十进制数值。

注意: 由于 JavaScript 保存数值的方式,实际中可能存在正零(+0)和负零(-0)。正零和负零在所有情况下都被认为是等同的,这里特地说明一下。

1.浮点值

要定义浮点值,数值中必须包含小数点

let floatNum1 = 1.1; 
let floatNum2 = 0.1; 
let floatNum3 = .1; // 有效,但不推荐

因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为

整数。在小数点后面没有数字的情况下,数值就会变成整数。类似地,如果数值本身就是整数,只是小数点后面跟着 0(如 1.0),那它也会被转换为整数,如下例所示:

let floatNum1 = 1.; // 小数点后面没有数字,当成整数 1 处理
let floatNum2 = 10.0; // 小数点后面是零,当成整数 10 处理

对于非常大或者非常小的数值可以用科学计数法表示,

科学记数法用于表示一个应该乘以10 的给定次幂的数值。ECMAScript 中科学记数法的格式要求是一个数值(整数或浮点数)后跟一个大写或小写的字母 e,再加上一个要乘的 10 的多少次幂。比如:

let floatNum = 3.125e7; // 等于 31250000 

浮点值的精确度最高可达 17 位小数,但在算术计算中远不如整数精确。例如,0.1 加 0.2 得到的不

是 0.3,而是 0.300 000 000 000 000 04。由于这种微小的舍入错误,导致很难测试特定的浮点值。比如下面的例子:

if (a + b == 0.3) { // 别这么干! 

 console.log("You got 0.3."); 

} 

这里检测两个数值之和是否等于 0.3。如果两个数值分别是 0.05 和 0.25,或者 0.15 和 0.15,那没问题。但如果是 0.1 和 0.2,如前所述,测试将失败。因此永远不要测试某个特定的浮点值。

注意 之所以存在这种舍入错误,是因为使用了 IEEE 754 数值,这种错误并非 ECMAScript所独有。其他使用相同格式的语言也有这个问题。

值得范围

ECMAScript 可以表示的最小

数值保存在 Number.MIN_VALUE 中,这个值在多数浏览器中是 5e324;可以表示的最大数值保存在

Number.MAX_VALUE 中,这个值在多数浏览器中是 1.797 693 134 862 315 7e+308。如果某个计算得到的

数值结果超出了 JavaScript 可以表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity(无

穷)值。任何无法表示的负数以-Infinity(负无穷大)表示,任何无法表示的正数以 Infinity(正

无穷大)表示。

如果计算返回正 Infinity 或负 Infinity,则该值将不能再进一步用于任何计算。这是因为Infinity 没有可用于计算的数值表示形式。要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使用 isFinite()函数,如下所示:

let result = Number.MAX_VALUE + Number.MAX_VALUE; 
console.log(isFinite(result)); // false

3.NaN

有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作

失败了(而不是抛出错误)。比如,用 0 除任意数值在其他语言中通常都会导致错误,从而中止代码执行。但在 ECMAScript 中,0、+0 或0 相除会返回 NaN:

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 

NaN 有几个独特的属性。首先,任何涉及 NaN 的操作始终返回 NaN(如 NaN/10),在连续多步计算时这可能是个问题。其次,NaN 不等于包括 NaN 在内的任何值。例如,下面的比较操作会返回 false:

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

为此,ECMAScript 提供了 isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断

这个参数是否“不是数值”。把一个值传给 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 

上述的例子测试了 5 个不同的值。首先测试的是 NaN 本身,显然会返回 true。接着测试了数值 10

和字符串"10",都返回 false,因为它们的数值都是 10。字符串"blue"不能转换为数值,因此函数返回 true。布尔值 true 可以转换为数值 1,因此返回 false。

注意: 虽然不常见,但 isNaN()可以用于测试对象。此时,首先会调用对象的 valueOf()方法,然后再确定返回的值是否可以转换为数值。如果不能,再调用 toString()方法,并测试其返回值。这通常是 ECMAScript 内置函数和操作符的工作方式

数值转换

有三个函数可以将非数值转化为数值

Number()

Number()函数基于如下规则执行转换。

 布尔值,true 转换为 1,false 转换为 0。

 数值,直接返回。

 null,返回 0。

 undefined,返回 NaN。

 字符串,应用以下规则。

 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。因此,Number("1")返回 1,Number("123")返回 123,Number("011")返回 11(忽略前面的零)。

 如果字符串包含有效的浮点值格式如"1.1",则会转换为相应的浮点值(同样,忽略前面的零)。

 如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整

数值。

 如果是空字符串(不包含字符),则返回 0。

 如果字符串包含除上述情况之外的其他字符,则返回 NaN。

parseInt()

parseFloat()

对象,调用 valueOf()方法,并按照上述规则转换返回的值。如果转换结果是 NaN,则调用toString()方法,再按照转换字符串的规则转换。从不同数据类型到数值的转换有时候会比较复杂,看一看 Number()的转换规则就知道了。下面是

几个具体的例子:

let num1 = Number("Hello world!"); // NaN 

let num2 = Number(""); // 0 

let num3 = Number("000011"); // 11 

let num4 = Number(true); // 1 

可以看到,字符串"Hello world"转换之后是 NaN,因为它找不到对应的数值。空字符串转换后是 0。字符串 000011 转换后是 11,因为前面的零被忽略了。最后,true 转换为 1。

考虑到用 Number()函数转换字符串时相对复杂且有点反常规,通常在需要得到整数时可以优先使

用 parseInt()函数。parseInt()函数更专注于字符串是否包含数值模式。字符串最前面的空格会被忽略,从第一个非空格字符开始转换。如果第一个字符不是数值字符、加号或减号,parseInt()立即返回 NaN。这意味着空字符串也会返回 NaN(这一点跟 Number()不一样,它返回 0)。如果第一个字符是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符。比如,

"1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地,"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,解释为十六进制整数

其也接收第二个参数,用于指定进制数

let num = parseInt("0xAF", 16); // 175
posted @ 2021-12-22 10:35  hw558  阅读(67)  评论(0)    收藏  举报