# JavaScript高级程序设计(第四版)深入学习 > 2021/6/14 > 本书出版于2020/9 > > 2019的内容 ## 第1章 什么事JavaScript 1995 网景公司。应对低网速,实现客户端表单验证 我要好好学习!!! * ECMAScript * DOM * BOM ## 第2章 HTML中的JavaScript 建议使用外部文件 理解加载顺序 延迟和下载完执行 ## 第3章 语言基础 ### 3.1 语法 * 区分大小写 * 第一个字母是_和$或者字母(unicode的所有字符,包括汉字) * 剩下的可以添加数字 * 关键字、保留子、true、false、null不可以 * 单行注释和多行注释//。/**/ * 严格模式,在脚本第一行加入`"use strict"`,这是全局严格模式,函数头部分可以定义整个函数 * 最好使用分号 * 最好使用代码块 ### 3.2 保留字和关键字 ### 3.3 变量 #### 3.1 var var可以在所有版本适用 let const值在ecma6以上 var 作用域在return后销毁 var可以重新赋值,也可以重新定义类型但不推荐 省略var定义的是全局变量**不推荐**严格模式不支持 可以反复定义一个变量,可以在使用后定义(声明提升),但不建议 #### 3.2 let let相比于var,let作用域是语句块,比如在if语句中声明的变量除了if就失效 let不允许多次声明(在子块中可以再次声明) let和var混用不会报错,只是针对不同的应用范围 let不会被提升 全局声明:window.关键字只能作用于var的全局变量,let则不行 #### 3.3 const常量 声明时必须赋值 不允许重复声明 不能修改值 和let相似 ### 3.4 数据类型 6种简单类型:Undefined,Null,Boolean,Number,String,Symbol(ES6) 复杂类型:Object对象 ```js let a="hello world!"; typeof a;//"string" typeof(a);//"string" typeof 32;//"number" //typeof是一个操作符,不是一个函数,所以不需要参数,但是也可以使用参数 ``` #### 3.4.2 Undefined let和var未赋值 undefined主要用于比较 未定义和使用未出现的值不一样,未出现的值会报错 ```js let a; console.log(a);//undefined console.log(b);//error console.log(typeof b);//undefined,对于未声明的变量只有一个正确操作符就是typeof ``` ```js let a;//undefined if(a)//不执行 if(!a)//执行 if(c)//error ``` #### 3.4.3 Null null指空对象 ```js let a=null; console.log(typeof a);//Object ``` null用于声明空对象 #### 3.4.4 Boolean值 true!=1 false!=0 区分大小写 转换其他类型 **P33** #### 3.4.5 Number 10进制直接输入 8进制在严格模式下不支持,一般模式下第一位是零,后面有超过基数的会当成十进制 16进制0x(区分大小写) ##### 3.4.5.1浮点型 允许.11出现但不建议 默认将无效小数部分转化为整数 0.1+0.2=0.300000000004 ##### 3.4.5.2范围 最大值:Number.MAX_VALUE 最小值:Number.MIN_VALUE Number.NEGATIVE_INFINITY +infinity Number.POSTTIVE_INFINITY -infinity ##### 3.4.5.3NaN 不是数值,返回操作数值时失败 分母为零时 NaN不参与操作包括if比较 isNaN("32")//isNaN()检测函数 ##### 3.4.5.4 数值转换 Number() parseInt()第二个参数可以指定进制parseInt("AF",16)//175 不指定进制需要使用标准进制表示法0xAF 开头必须是数字,否则识别为NaN,空字符也为NaN parseFloat() 详细规则**P36** #### 3.4.6 String ##### 3.4.6.1字符字面量 ``` \n \t \b 退格 \r \f 换页 \\ ``` 转义字符在字符串中算1个字符 ##### 3.4.6.2 字符串拼接 同Python ##### 3.4.6.3 转换为字符串 a.toString()//null和undefined没有这个方法 一般不传参,和parseInt()一样,参数代表进制 ##### 3.4.6.4模板字面量 换行会出现在字符串中正确表达 方便了HTML ##### 3.4.6.5 字符串插值 ```js `${ a }scac`//这种样式的字符串插值必须使用1旁边的分词号 ``` ##### 3.4.6.7 原始字符串,不转义 ```js String.raw'xasxas' a="xasxax\xasxasx" a.raw; console.log(a.raw) ``` #### 3.4.6Symbol #### 3.4.7 Object() let a= new Object(); ### 3.5 操作符 ### 3.6 语句 #### 3.6.1 if ```js if (){}else{} if(){ }else if(){ }else{ } ``` #### 3.6.2 do while ```js do{ }while() ``` #### 3.6.3while ```js while(){ } ``` #### 3.6.4 for语句 ```js for(){ } ``` #### 3.6.5 for in ```js for(a in b){ } ``` #### 3.6.6 for of ```js for(a of b){}//严格迭代 i of window不行 ``` #### 3.6.7 标签语句 #### 3.6.8 break和continue语句 #### 3.6.9with 将作用域设置给特定对象 ```js with(location){ let qs=search.substring(1); let url=href; } //实际上是location.search.substring(1) ``` #### 3.6.10 switch 和c一样 ``` switch(){ case value: 语句 break; } ``` ### 3.7 函数 eval和arguments不能出现在函数名称和函数参数中 ## 第4章 变量、作用域和内存 ### 4.1原始值和引用值 原始值不能添加属性,有六个原始值 ```js let a=new String("xs");//这里变成了一哥obj let b="xsx" a.name="xs"//这里是错误的,但是不会报错,只是无法输出 b.name="xs" console.log(a.name)//xs console.log(b.name)//undefined console.log(typeof a)//Object console.log(typeof a)//String ``` #### 4.1.2 复制值 原始值直接复制 引用值复制的是对象指针,修改原数据,复制值也会改变 #### 4.1.3 函数传参 传参都是按值传参(各自的方式) 原始值和引用值都是通过自己的方式复制过去 #### 4.1.4 确定类型 typeof面对对象或者undefined。 输出的是Object ### 4.2 执行上下文和作用域 #### 4.2.2 变量声明 1var 自动添加到最近的上下文 2let 块级 3**标识符查找** 函数面对标识符时先在最近的上下文查找,查找不到在进行上下文查找,如果在最近的查找到了,就不会再运用全局的 ### 4.3 垃圾回收 标记清理 引用计数(有问题已弃用) #### 4.3.3性能 调度垃圾回收过于频繁会造成性能浪费 #### 4.3.4内存 手动释放 变量=null 多使用 let 和const 隐藏类:用同一个函数初始化,在定义函数时候初始化属性,让新创建的函数隐藏函数保持一致 内存泄漏:没有声明的变量直接赋值,成为全局变量,不会自动回收 ## 第5章 基本引用类型 引用类型不是类 new 构造函数;//创建一个对象 ### 5.1 Date 1970/1/1 ```js let now = new Date(); now=new Date(Date.parse("6/15/2021"));//指定日期 now=new Date("6/15/2021");//同上面,构造函数隐式调用 now=new Date(Date.UTC(2021,0,1,12,5,21)) now=new Date(2021,0,1,12,5,21)//构造函数隐式调用 let starttime=Date.now() let wndtime=Date.now() ``` #### 5.1.1 继承的方法 ```js now.toLocaleString();//本地时间 now.toString();//包含时区 ``` #### 5.1.2 日期格式化 ```js now=new Date() //Date Tue Jun 15 2021 15:53:25 GMT+0800 (中国标准时间) now.toLocaleString() //"2021/6/15 下午3:53:25" now.toDateString() //"Tue Jun 15 2021" now.toTimeString() //"15:53:25 GMT+0800 (中国标准时间)" now.toLocaleDateString() //"2021/6/15" now.toLocaleTimeString() //"下午3:53:25" now.toUTCString() //"Tue, 15 Jun 2021 07:53:25 GMT" ``` #### 5.1.3 日起时间方法 **P106** ### 5.2 RegExp 正则表达式 ### 5.3 原始值包装类型 ```js let str1="i love u!"; let str2=str1.substring(2);//正常逻辑下这是错误的 ``` 第二次访问str1的时候,以只读模式进行 ```js let str1=new String("i love u!"); let str2=str1.substring(2); s1=null; ``` #### 5.3.1 Boolean ```js let booleantrueobj=new Obiect(true);//这是一个存储布尔值的对象 //因为是对象,不管布尔值是什么,用对象做比较永远是true ``` 尽量永远不要使用boolean对象 #### 5.3.2 Number toFixed(2)//指定小数位数 四舍五入 toExponential(2)//指定科学计数法 .toPrecision(5)//数字总位数 Number.isInteger(num);//判断是你不是整数 #### 5.3.3 String 1 JavaScript字符 str.length charCodeAt()可以查看字符的编码 2 normalize()方法 某些unicode字符有多个遍吗 3 字符串操作方法 常用的str1.slice(起始,-3) Str1.substring(2) 第二个字符后面的 Str1.substr(-3) 4 字符串位置方法 Str1.indexOf("o",起始位置int) Str1.lastIndexOf("0",起始位置int) 5 6 trim()删除前后的空格 str1.trim() 7 repeat()赋值几遍str1.repeat(5)//赋值5遍 8 **填充** padStart(),padEnd() 字符串超限无法使用 ```js let a="ABCDE" console.log(a.padStart(10,"*")) //*****ABCDE debugger eval code:1:9 console.log(a.padEnd(10,"*")) //ABCDE***** debugger eval code:1:9 ``` 9 迭代 ```js for (const c of "abcde") { console.log(c); } ``` 10 字符串大小写转换 ```js let stringValue = "hello world"; console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD" console.log(stringValue.toUpperCase()); // "HELLO WORLD" console.log(stringValue.toLocaleLowerCase()); // "hello world" console.log(stringValue.toLowerCase()); // "hello world" ``` 11 12 localeCompare()方法 比较字符顺序 ```js let a="何"; let b="小"; let c="啊" a.localeCompare(b); //-1 b.localeCompare(a) //1 a.charCodeAt() 20309 b.charCodeAt() 23567 c.charCodeAt() 21834 ``` ### 5.4 单例内置对象 #### 5.4.1Global 1 URL编码方法 将字符替换为编码 ```js let uri = "http://www.wrox.com/illegal value.js#start"; // "http://www.wrox.com/illegal%20value.js#start" console.log(encodeURI(uri)); // "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start" console.log(encodeURIComponent(uri)); ``` 将编码替换为字符(只能替换自己支持的字符) ```js let uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start"; // http%3A%2F%2Fwww.wrox.com%2Fillegal value.js%23start console.log(decodeURI(uri)); // http:// www.wrox.com/illegal value.js#start console.log(decodeURIComponent(uri)); ``` 2 eval() 将内容加入到上下文中 ```js eval("console.log('hi')"); // 上面这行代码的功能与下一行等价: console.log("hi"); ``` #### 5.4.2 Math 2 min(数组), max(数组) 3 舍入方法 ```js //Math.ceil()方法始终向上舍入为最接近的整数。5.4 单例内置对象 133 //Math.floor()方法始终向下舍入为最接近的整数。 //Math.round()方法执行四舍五入。 //Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示。 ``` 4 random()随机数0-1不包括1 5 其他方法 Math.abs(x)绝对值 Math.pow(a,b)a的b次幂 Math.trunc(x)x的整数 ## 第6章 集合引用类型 ### 6.1 Object 显式地创建 ```js //第一种是使用 new 操作符和 Object 构造函数 let person = new Object(); person.name = "Nicholas"; person.age = 29; //另一种方式是使用 *对象字面量* (object literal)表示法 let person = { name: "Nicholas", age: 29 }; //在对象字面量表示法中,属性名可以是字符串或数值,比如: let person = { "name": "Nicholas", "age": 29, 5: true //自动将5转化为字符串格式 }; //属性一般是通过点语法来存取的,这也是面向对象语言的惯例 //但也可以使用中括号来存取属性。在使用中括号时,要在括号内使用属性名的字符串形式 console.log(person["name"]); // "Nicholas" console.log(person.name); // "Nicholas" ``` ### 6.2 Array 跟其他语言中的数组一样,ECMAScript 数组也是**一组有序的数据**,但跟其他语言 不同的是,数**组中每个槽位可以存储任意类型的数据**。 #### 6.2.1创建数组 ```js //使用 Array 构造函数 let arr = new Array(); let arr = new Array(20);//长度为20,可省略new let arr= new Array("Hello","World","!");//含有三个,可省略new //使用数组字面量(array literal)表示法 let colors = ["red", "blue", "green"]; // 创建一个包含 3 个元素的数组 let names = []; // 创建一个空数组 let values = [1,2,]; // 创建一个包含 2 个元素的数组 //与对象一样,在使用数组字面量表示法创建数组不会调用 Array 构造函数 ``` from 和 of ```js const a1 = [1, 2, 3, 4]; const a2 = Array.from(a1, x => x**2); const a3 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2}); console.log(a2); // [1, 4, 9, 16] console.log(a3); // [1, 4, 9, 16] ``` #### 6.2.2 数组空位 用逗号创建 ES6之前将空位解释为undefined #### 6.2.3 数组索引 同其他语言,但是可以超过长度,被解析为undefined ```js //使用 length 属性可以方便地向数组末尾添加元素,如下例所示: let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组 colors[colors.length] = "black"; // 添加一种颜色(位置 3) colors[colors.length] = "brown"; // 再添加一种颜色(位置 4) ``` 数组最多可以包含 4 294 967 295 个元素 #### 6.2.4 检测数组 ```js if (Array.isArray(value)){ // 操作数组 } ``` #### 6.2.5 迭代器方法 keys()返回数组索引的迭代器,values()返回数组元素的迭代器,而 entries()返回 索引/值对的迭代器: ```js const a = ["foo", "bar", "baz", "qux"]; // 因为这些方法都返回迭代器,所以可以将它们的内容 // 通过 Array.from()直接转换为数组实例 const aKeys = Array.from(a.keys()); const aValues = Array.from(a.values()); const aEntries = Array.from(a.entries()); console.log(aKeys); // [0, 1, 2, 3] console.log(aValues); // ["foo", "bar", "baz", "qux"] console.log(aEntries); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]] //使用 ES6 的解构可以非常容易地在循环中拆分键/值对 const a = ["foo", "bar", "baz", "qux"]; for (const [idx, element] of a.entries()) { alert(idx); alert(element); } ``` #### 6.2.6 复制和填充方法 ```js //fill() let arr=[1,2,3,1,4,5,6,5] arr.fill(填充内容,起始,结束(不包含)) arr.copyWithin(从哪儿开始填充,原来文本的开始,原来文本的结束(不包含)) ``` #### 6.2.7 转化方法 ```js let colors = ["red", "green", "blue"]; alert(colors.join(",")); // red,green,blue alert(colors.join("||")); // red||green||blue ``` #### 6.2.8 栈方法 ```js let a=["i","l","o","v","e","u"] a.push("!"); a.push("w","j","y") a.pop();//删除并返回,同时减少length ``` #### 6.2.9 队列方法 ```js a.shift();//删除开头并返回; ``` #### 6.2.10 排序方法 ```js let values = [1, 2, 3, 4, 5]; values.reverse();//反响排序,此方法不够灵活 values.sort(); //升序,但是先转化为字符串,10在5前面 //比较函数 values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0); //同下 function compare(value1, value2) { if (value1 < value2) { return 1; } else if (value1 > value2) { return -1; } else { return 0; } } let values = [0, 1, 5, 10, 15]; values.sort(compare); alert(values); // 15,10,5,1,0 ``` #### 6.2.11 操作方法 concat();我感觉可以用append()代替 Slice(a,b);切片可以用负值 **splice();** * 删除splice(开始删除的位置,删除的数量) * 插入splice(开始位置,删除数量,插入值(后面可以是多个)) * 替换splice(开始位置,删除数量(也就是替换,为0不替换),插入值(同上)) #### 6.2.12 搜索和位置方法 1 严格相等 ```js a.indexOf()有返回对象,没有返回-1 a.lastIndexOf()有返回对象,没有返回-1 a.includes()返回布尔值 ``` #### 6.2.13 迭代 ?? #### 6.2.14 归并方法 ?? ### 6.3 定型数组 ### 6.4 Map #### 6.4.1 基本API ```js const m = new Map(); alert(m.has("firstName")); // false alert(m.get("firstName")); // undefined alert(m.size); // 0 m.set("firstName", "Matt") .set("lastName", "Frisbie"); alert(m.has("firstName")); // true alert(m.get("firstName")); // Matt alert(m.size); // 2 m.delete("firstName"); // 只删除这一个键/值对 alert(m.has("firstName")); // false alert(m.has("lastName")); // true alert(m.size); // 1 m.clear(); // 清除这个映射实例中的所有键/值对 alert(m.has("firstName")); // false alert(m.has("lastName")); // false alert(m.size); // 0 ``` #### 6.4.2 #### 6.4.3 选择 P168 ### 6.5 WeakMap ### 6.6 Set ### 6.7 WeakSet ## 第7章 迭代器与生成器 ## 第8章 对象、类与面向对象编程 ## 第9章 代理与反射 ECMAScript6 新增 ### 9.1 代理基础 代理是目标对象的抽象 代理类似C++指针 可以用作目标对象的替身、但又完全独立于目标对象。 #### 9.1.1 创建空代理 ## 第10章 函数 函数定义的方法: 1. 函数声明 ```js function function_name(argument) { // body... return argument; }; ``` 2. 函数表达式 ```js let fun=function(a){ return a; }; ``` 3. 构造函数 ```js let fun=Function("a","return a"); //所有函数定义都会在最后转化为这种格式 ``` ### 10.1 箭头函数 ```js let fun=(a,b)=>{return a+b;}; ``` ### 10.2 函数名 函数名是指向函数的指针,所以函数可以有多个名称 函数都有一个name属性,a.name//a ### 10.3 理解参数 ### 10.4 没有重载 在js种重复声明函数会覆盖,好像还会报错 ### 10.5 默认参数值 ```js function makeKing(name) { name = (typeof name !== 'undefined') ? name : 'Henry'; //如果函数调用时没有参数,默认为。。。 return `King ${name} VIII`; } function makeKing(name = 'Henry', numerals = 'VIII') { //多个默认参数 return `King ${name} ${numerals}`; } console.log(makeKing()); // 'King Henry VIII' console.log(makeKing('Louis')); // 'King Louis VIII' //箭头函数也可以 ``` * 默认参数作用域与暂死区 ```js // 参数初始化顺序遵循“暂时性死区”规则,即前面定义的参数不能引用后面定义的。像这样就会抛出错误: // 调用时不传第一个参数会报错 function makeKing(name = numerals, numerals = 'VIII') { return `King ${name} ${numerals}`; } // 参数也存在于自己的作用域中,它们不能引用函数体的作用域: // 调用时不传第二个参数会报错 function makeKing(name = 'Henry', numerals = defaultNumeral) { let defaultNumeral = 'VIII'; return `King ${name} ${numerals}`; } ``` ### 10.6 ### 10.7 函数声明与函数表达式 JavaScript引擎在加载数据时对他们区别对待,在任何代码执行钱,会先读取函数声明,并在执行上下文中生成函数定义,而函数表达式必须等到代码执行到它的一行才会在执行上下文中生成函数定义 ```js // 没问题 console.log(sum(10, 10)); function sum(num1, num2) { return num1 + num2; } // 会出错 console.log(sum(10, 10)); let sum = function(num1, num2) { return num1 + num2; }; ``` ### 10.8 函数作为参数 ### 10.9 函数内部 在 ECMAScript 5 中,函数内部存在两个特殊的对象:arguments 和 this。ECMAScript 6 又新增 了 new.target 属性。 #### 10.9.1 argments ```js function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } } //阶乘函数 function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); //无论函数名是什么,都可以使用arguments.callee()调用 } } ``` #### 10.9.2 this #### 10.9.3 caller ```js function outer() { inner(); } function inner() { console.log(inner.caller); } outer();//会显示outer的源代码 inner();//null ``` ### 10.10函数属性和方法 每个函数都有两个属性:length 和 prototype。其中,length 属性保存函数定义的命名参数的个数 ```js function sayName(name) { console.log(name); } function sum(num1, num2) { return num1 + num2; } function sayHi() { console.log("hi"); } console.log(sayName.length); // 1 console.log(sum.length); // 2 console.log(sayHi.length); // 0 ``` ### 10.11 函数表达式 ### 10.12 递归 ??????????????????? ### 10.14 闭包 ### 10.15 立即调用的函数表达式 ```js 使用 IIFE 可以模拟块级作用域,即在一个函数表达式内部声明变量,然后立即调用这个函数。这 样位于函数体作用域的变量就像是在块级作用域中一样。ECMAScript 5 尚未支持块级作用域,使用 IIFE 模拟块级作用域是相当普遍的。比如下面的例子: // IIFE (function () { for (var i = 0; i < count; i++) { console.log(i); } })(); console.log(i); // 抛出错 ``` ### 10.16 私有变量 ## 第11章 期约与异步函数 ### 11.2 期约promise #### 11.2.1 准备 ##### 11.2.1.1 区别实例对象和函数对象 1. 实例对象:通过new创建 ```js function Fn() { // body... } const fn = new Fn();//Fn是构造函数 //fn 是实例对象 ``` 2. 将函数作为对象 ```js //连接上 console.log(Fn.prototype) ``` ()左边是函数 .的左边是函数对象 ##### 11.2.1.2 回调函数 1. 同步回调函数 理解:立即执行,完全止血了才结束,不会放入回调队列中 ```js let arr=[1,3,5] arr.forEach(item = >{ console.log(item) }) console.log("forEach()之后") //forEach()括号中的回调函数是同步回调函数,不会放在回调队列,而是立即执行 ``` 2. 异步回调函数 理解:不会立即执行,会放入回调队列中 ```js setTime(function() {console.log("setTimeout()函数")}) console.log("setTimeout()之后") //setTimeout()之后 //setTimeout()函数 //setTimeout()是异步回调函数,会放在回调队列中 ``` ##### 11.2.1.3 js的错误处理err 1. 常见内置错误 * ReferenceError 引用错误,引用了不存在的变量 * TypeError 数据类型不正确错误 类型中没有某个函数或属性 let a; a.xxx(); * RangeError 超出范围 无限递归 * SyntaxError 语法错误 2. 错误处理 捕获错误:try。。。cathch。。。 ```js try{ let a; console.log(a.xx); }catch(error){ console.log(error.message); console.log(error.statck) } ``` 抛出错误:throw error ```js function something(){ if (Date.now()%2===1){ console.log("当前时间为奇数,可以执行任务") }else{ throw new Error("当前时间为偶数,无法执行任务") } } try{ something(); }catch(error){ console.log(error.message) } ``` 3. 错误对象 message 错误信息 stack 函数调用栈记录信息 #### 11.2.2 promise的理解和使用 ##### 11.2.2.1 promise是什么 ###### 11.2.2.1.1 理解 1. 抽象表达:进行异步编程的新的解决方案 2. 具体表达: * 语法上:构造函数 * 功能:封装一个异步操作,并获取其结果 ###### 11.2.2.1.2 promise的状态改变 1. pending 变为resolved 2. pending 变为rejected 以上状态不可逆,每个promise只能又一次机会 ###### 11.2.2.1.3 基本流程 promise 进行异步操作,好状态then,坏状态then/ ###### 11.2.2.1.4 基本使用 ```js // 创建一个新的promise对象 const promise = new Promise((resolve,reject) =>{// 执行器对象,是一个同步回调 // 执行异步操作 const time = Date.now(); if(time%2===0){ // 成功嗲用resolve(value) resolve("成功的值:"+time)//括号内传值 }else{ // 失败调用reject(reason) reject("失败的值:"+time) } }) promise.then( value => {//接收得到成功的值value onResolved console.log("成功的回调:",value) }, reason => {//接收失败的值reason onRejected console.log("失败的回调:",reason) } ) ``` ##### 11.2.2.2 为什么使用promse 1. 指定回调函数的方式更加灵活: 纯回调函数:必须在启动异步人物前指定 promise:启动起步任务 =》返回promise对象 +〉 给promise对象绑定回调函数 2. 支持链式调用,可以解决回调地狱问题 回调地狱:回调函数嵌套, 链式调用:异常传透,多个错误可以卸载一个错误上 终极方法:async/await ##### 11.2.2.3 如何使用promise ```js const promise = new Promise(function(resolve,reject){ setTime(function() { resolve("成功的数据") reject("失败的数据")//只能执行一个, },1000) }).then( value => { console.log("onResolveed()",value) } ).catch( reason => { console.log("onRejected()",reason) } ) ``` ### 11.2.3 自定义promise ### 11.2.4 async 和 await ## 第12章 BOM ### 12.1 window对象 ```js window.innerWidth; window.innerHeight;//浏览器页面的大小,不包含开发者窗口 window.outerWidth; window.outerHeight;//浏览器窗口大小 ``` document.documentElement.clientWidth 和 document.documentElement.clientHeight 返回的布局视口的大小 ## 第14章 DOM DOM:文档对象模型 (Document Object Model),是HTML和XML文档的编程接口;DOM 表示由**多层节点**构成的文档,通过它开发者可以添加、删除和修改页面的各个部分。 ### 14.1 节点层级 文档元素:html元素,是文档的最外层元素 DOM 中总共有 **12 种**节点类型,这些 类型都继承一种基本类型。 #### 14.1.1 Node类型 DOM Level 1 描述了名为 Node的接口,这个接口是所有 DOM 节点类型都必须实现的。 Node接口 在 JavaScript 中被实现为 Node类型,在除 IE 之外的所有浏览器中都可以**直接访问**这个类型。 在 JavaScript 中,**所有节点类型都继承 Node类型**,因此所有类型都共享相同的基本属性和方法。 1. nodeName与nodeValue 在使用这两个属性前,最好先**检测节点类型** ```js if (someNode.nodeType == 1){ // 检测节点类型 value = someNode.nodeName; // 会显示元素的标签名 } ``` 对元素 而言,**nodeName始终等于元素的标签名**,而 **nodeValue 则始终为 null**。 2. 节点关系 每个节点都有一个 **childNodes** 属性,其中包含一个 **NodeList**的实例。(NodeList是类数组,不是Array) 访问节点的元素 ```js let firstChild = someNode.childNodes[0]; let secondChild = someNode.childNodes.item(1); let arrayOfNodes = Array.from(someNode.childNodes); //ES6 ``` **parentNode** :父元素 **previousSibling**:上一个节点 **nextSibling** :下一个节点 **firstChild**:第一个子节点 **lastChild** :最后一个子节点 ![截屏2021-08-22 上午3.17.45](./截屏2021-08-22 上午3.17.45.png) **hasChildNodes()**:判断是否有子节点 3. 操纵节点 **appendChild()**:在列表末尾添加节点,如果传入的是一个存在的节点,该节点会被移动到对象的子节点尾部,一个节点不会同时存在于文档中 **insertBefore()**:插入 ```js // 作为最后一个子节点插入 returnedNode = someNode.insertBefore(newNode, null); alert(newNode == someNode.lastChild); // true // 作为新的第一个子节点插入 returnedNode = someNode.insertBefore(newNode, someNode.firstChild); alert(returnedNode == newNode); // true alert(newNode == someNode.firstChild); // true // 插入最后一个子节点前面 returnedNode = someNode.insertBefore(newNode, someNode.lastChild); alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true ``` **replaceChild()**:节点替换 ```js let returnedNode = someNode.replaceChild(newNode, someNode.firstChild); ``` **removeChild()**删除节点 ```js // 删除第一个子节点 let formerFirstChild = someNode.removeChild(someNode.firstChild); // 删除最后一个子节点 let formerLastChild = someNode.removeChild(someNode.lastChild); ``` 4. 其他方法 **cloneNode()**:复制节点 ```js let deepList = myList.cloneNode(true); //深复制,复制节点和子节点数 alert(deepList.childNodes.length); // 3(IE9之前的版本)或7(其他浏览器) let shallowList = myList.cloneNode(false); 孤儿节点,没有子节点和父节点 alert(shallowList.childNodes.length); // 0 ``` #### 14.1.2 Document类型 document 是HTMLDocument的实例(HTMLDocument继承 Document),表示整个 HTML 页面。 document是 window 3 对象的属性,因此是一个全局对象。 document对象可用于**获取关于页面的信息**以及**操纵其外观和底层结构**。 1. 文档子节点 **documentElement**:始终指向 HTML 页面中的元素 ```js let html = document.documentElement; // 取得对的引用 alert(html === document.childNodes[0]); // true 11 alert(html === document.firstChild); // true ``` **body**:获取body ```js let body = document.body; // 取得对的引用 ``` **doctype**:标签 2. 文档信息 **title**:读写页面的标题,修改 title 属性并不会改变元素 **URL** :包含当前页面的完整 URL(地 址栏中的 URL),不可修改,修改无效 **domain** :包含页面的域名 **referrer** :包含链接到当前页面的那个页面的 URL,不可修改,修改无效 浏览器对 domain 属性还有一个限制,即这个属性一旦放松就不能再收紧。比如,把document.domain 设置为"wrox.com"之后,就不能再将其设置回"p2p.wrox.com",后者会导致错误,比如: ```js // 页面来自p2p.wrox.com document.domain = "wrox.com"; // 放松,成功 document.domain = "p2p.wrox.com"; // 收紧,错误! ``` 3. 定位元素 **getElementById()**:找到了返回这个元素,找不到返回null **getElementsByTagName("img")**://返回nodeList 要取得文档中的所有元素,可以给 getElementsByTagName()传入* ```js let allElements = document.getElementsByTagName("*"); ``` **getElementsByName()**: > 通过类名(getElementsByClassName) > 通过选择器获取一个元素(querySelector) > 通过选择器获取一组元素(querySelectorAll) 4. 特殊集合 **document.anchors** 包含文档中所有带 name属性的<a>元素。 **document.forms**包含文档中所有<form>元素(与 document.getElementsByTagName ("form") 返回的结果相同)。 **document.images**包含文档中所有<img>元素(与 document.getElementsByTagName ("img") 7 返回的结果相同)。 **document.links** 包含文档中所有带 href属性的<a>元素。 5. DOM兼容性检测 由于实现不一致,因此 hasFeature()的返回值并不可靠。目前这个方法已经被废弃,不再建议使 用。为了向后兼容,目前主流浏览器仍然支持这个方法,但无论检测什么都一律返回 true。 6. 文档写入 ```js // 禁用 最好做下备份 document.original.write = document.write 然后再 document.write = function() {} 因为不好用,还因为性能 ``` 在浏览器渲染页面之前,会去根据HTML标签解析DOM树。**如果解析器遇到了 `<script>` 标签,则会停止DOM的解析,优先执行脚本。**如果这个脚本动态插入了另一个脚本,则解析器会等待另一个脚本下载完成到执行完成,延后了页面渲染的时间。 #### 14.1.3 Element 类型