JavaScript高级程序设计

您所在的位置:网站首页 settimeout的this指向 JavaScript高级程序设计

JavaScript高级程序设计

#JavaScript高级程序设计| 来源: 网络整理| 查看: 265

什么是JavaScript JavaScript是一门用来与网页交互的脚本语言,包含以下三个组成部分: ECMAScript:由ECMA-262定义并提供其核心功能DOM(文档对象模型):提供与网页内容交互的方法的接口BOM(浏览器对象模型):提供与浏览器交互的方法和接口 HTML中的JavaScript < script >元素 属性 async:可选,表示应立即开始下载脚本,但不能阻止其他页面动作(异步执行)。只对外部脚本文件有效。charset:可选,字符集。crossorign:可选,配置相关请求的CORS(跨源资源共享)设置。默认不使用CORSdefer:可选,脚本立即下载,但推迟执行。仅对外部脚本文件有效。integrity:可选,允许比对接收到的资源和指定的加密签名以验证子资源的完整性。src:可选。type:可选,代表代码块中脚本语言的内容类型(也称MIME类型) 执行顺序 在不使用defer或者async的情况下,包含在 注意点 < script >中的代码尽量不要出现< /script >,浏览器会将其视为结束标志;如果一定要使用,使用转义字符,例:使用src属性的< script >标签元素不应该在< script >< /script >中再包含其他代码(也就是一个< script >标签,行内式和外部文件式只能选一个) 跨域 < script >元素可以包含来自外部域的JavaScript文件若src属性是一个指向不同于的url,则浏览器会向此指定路径发送一个GET请求,此初始请求不受浏览器同源策略的限制,但返回的JavaScript仍受限制好处:通过不同的域分发JavaScript(就是我们引入外部包的过程)可使用integrity属性进行防范 位置 通常将所有的JavaScript引用放在< body >元素中的页面内容后面 动态加载脚本 < noscript >元素 < noscript >可以是一种出错提示手段在以下任一条件被满足时,< noscript >中的内容就会被渲染 浏览器不支持脚本浏览器对脚本的支持被关闭 语言基础 语法 标识符 标识符是变量、函数、属性或函数参数的名称标识符的组成如下: 第一个字符必须是一个字母、下划线、或美元符号剩下的其他字符可以是字母、下划线、美元符号或数字 推荐使用驼峰大小写形式关键字、保留字、true、false和null不能作为标识符 关键字 breakdointypeofcaseelseinstanceofvarcatchexportnewvoidclassextendsreturnwhileconstfinallysuperwithcontinueforswitchyielddefaultifthrowthisfunctiondebuggerdeleteimporttry 保留字 始终保留严格模式下保留模块代码中保留enumimplementspackageawaitpublicinterfaceprotectedprivatestaticlet 变量 可以保存任意类型的数据,每个变量都是一个保存任意值的占位符变量有三个:var、let和constconst优先,let次之,不使用var var 不初始化的情况下,变量会保存一个特殊值undefined使用var操作符定义的变量会成为包含它的函数的局部变量在函数内部定义变量时省略var,可以创建一个全局变量(严格模式下会报错,且不推荐这么做)var声明提升: 使用var声明变量时,变量会发生变量提升所谓提升,是把所有变量声明提升到函数作用域的顶部 可以使用var声明同一个变量用var在全局作用域中声明的变量会成为window对象的属性具体用例: // var的作用域 // var声明提升 let let声明的范围是块作用域let不允许在同一个块作用域中出现冗余声明let声明的变量不会在作用域中提升用let在全局作用域中声明的变量不会成为window对象的属性具体用例: // 混用var与let // for循环中的let声明 const const的行为与let基本相同用const声明变量的同时必须初始化变量,且该变量不允许进行修改如果const变量引用的是一个对象,修改该对象内部的属性方法是允许的如果想让整个对象(包括属性方法)不能修改,可以使用Object.freeze()具体用例: // for-of 和 for-in // Object.freeze() 再给属性赋值时不会报错 但会赋值失败 const o1 = { age: 13, }; const o2 = Object.freeze({ age: 14, }); o1.age = 0; o2.age = 0; o2.name = `xiaoming`; console.log(`${o1.age} ${o2.age} ${02.name}`); // 0 14 undefined 数据类型 6种简单数据类型:Undefined、Null、Boolean、Number、String、Symbol1种复杂数据类型:Obiect typeof操作符

使用typeof返回的字符串及其意义 | 字符串 | 意义 | | — | — | | “undefined” | 值未定义 | | “boolean” | 值为布尔值 | | “string” | 值为字符串 | | “number” | 值为数值 | | “object” | 值为对象或null | | “function” | 值为函数 | | “symbol” | 值为符号 |

具体用例:

// typeof 操作符 undefined类型 undefine类型只有一个值,就是特殊值undefined变量声明了但是没有赋值是,变量的值为undefined(明确是空对象指针null和为初始变量的区别)对未声明的变量,只能执行一个有用的操作,就是对它调用typeof,返回值为undefined具体事例: // 包含undefined值的变量与未定义变量的区别 // 明确检测undefined这个字面值 Null类型 Null类型只有一个值,即特殊值null在定义将来要保存对象值的变量时,建议用null初始化具体事例: // null与undefined表面相等 // 明确检测null这个字面值 Boolean类型 Boolean类型有两个字面值,true和false所有其他ECMAScript类型的值都有相应的布尔值的等价形式可使用Boolean([任意类型的数据])转型函数将其他值转换为布尔值不同类型与布尔值的转换规则 | 数据类型 | 转换为true的值 | 转换为false的值 | | — | — | — | | Boolean | true | false | | String | 非空字符串 | “”(空字符串) | | Number | 非零数值(包括无穷值) | 0、NaN | | Object | 任意对象 | null | | Undefined | N/A(不存在) | undefined | Number类型 进制 八进制需要前缀0(零),但如果字面量中的数字超出了应有的范围,会将整个数字视为十进制数(如079)十六进制需要前缀0x使用八进制和十六进制的格式创建的数值在所有数学操作中均视为十进制数值 浮点值 小数点前可以没有整数若小数点后面没有数字,数值自动转化为整数若数值本身就是整数。只是小数点后面跟着零,自动转化为整数永远不要测试某个特定的浮点值,理由如下: 值的范围 Number.MIN_VALUE 最小数值Number.MAX_VALUE 最大数值超过了最大值,会被转换为+Infinity超过了最小值,会被转化为-InfinityisInfinity():确定一个值是不是有限大 NaN(Not a Number) 用来表示本来要返回数值的操作失败了(而不是抛出错误)0、-0、+0相除会返回NaN若分子是非0值,分母是有符号0或者无符号0,则会返回Infinity或-InfinityisNaN()函数: 参数:接受一个任意数据类型的参数功能:判断参数是否“不是数值”返回值:布尔值原理:任何不能转换为数值的值都会导致这个函数返回true 具体用例: // 0、+0、-0 相除 // infinity情况 // isNaN() 函数 数值转换 Number()函数 具体用例: // 布尔值 // 数值 // null // undefined // 字符串 // 对象 parseInt()函数 需要得到整数时优先使用parseInt()函数参数: 第一个参数:需要被转换的数据第二个参数:可选,指定进制数,默认为10 具体用例: // 空字符串 // 数字+其他字符 组成的字符串 // 其他进制数 parseFloat()函数 工作方式与parseInt()类似具体用例: // 第一次出现的小数点是有效的,第二次出现的小数点是无效的 // 只解析十进制数,不能指定底数 // 始终忽略字符串开头的0 // 若字符串表示整数(没有小数点或者小数点后面只有0,则返回整数) string类型 可以使用单引号(‘’)、双引号(“”)、反引号(``)表示 字符字面量 \n换行\t制表\b退格\r回车\f换页\\反斜杠\’单引号\"双引号\`反引号\xnn以十六进制编码nn表示的字符(n是十六进制数字0~F)\unnnn以十六进制编码nnnn表示的Unicode字符 注意:转义序列表示一个字符(在计算的时候算一个)string.length :length属性用于获取字符串的长度 字符串的特点 这里的字符串是不可变的,要修改必须某个变量的字符串值必须先销毁原字符串,再保存新变量 转换为字符串: toString()方法: 可用于数值、布尔值、对象和字符串不可用于null和undefined参数:仅在对数值进行转换时接受参数,参数为转换的进制数 String()转型函数,规则如下: 若值有toString()方法,则调用此方法并返回结果若值为null,返回“null”若值为undefined,返回“undefined” 具体用例: // toString() // String() 模板字面量(反引号) 模板字面量在定义模板时特别有用(???)模板字面量会保持反引号内部的空格具体用例: // 定义模板 HTML模板 可以安全地插入到HTML中 // 保持内部空格 字符串插值 通过${[JavaScript表达式]}来实现插入值会使用toString()强制转换为字符串插值表达式中可以调用函数和方法插值表达式中可以插入自己之前的值具体用例: // toString()转换 // 表达式中调用函数和方法 // 表达式中插入自己之前的值 模板字面量标签函数 模板字面量支持定义标签函数(???)通过标签函数可以自定义插值行为标签函数会接收被插值记号分隔后的模板和对每个表达式求值的结果具体用例: // 标签函数 原始字符串 使用 String.Raw字符串内容 可以直接获取原始的模板字面量,而不是被转移后的字符表示 Symbol类型(符号) 符号是原始值,且符号实例是唯一、不可变的。用途:确保对象属性使用唯一标识符,不会发生属性冲突的危险具体用例: // typeof操作符 // 创建Symbol(`字符串参数`)实例并将其用作对象的新属性 即使参数相同,Symbol也是不同的 // 全局符号注册表 Symbol.for(`字符串参数`)方法 常用内置符号: 这些内置符号最重要的用途之一是重新定义它们,从而改变原生结构的行为所有内置符号属性都是不可写、不可枚举、不可配置的 // Symbol.asyncIterator // 一个方法,该方法返回对象默认的AsyncIterator。由for-await-of使用。实现了异步迭代器API的函数 // Symbol.hasInstance // 一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由操作符instanceof操作符使用。 // instanceof操作符可以用来确定一个对象实例的原型链上是否有原型 // Symbol.isConcatSpreadable // 一个布尔值,如果是true或真值,类数组对象会被打平到数组实例,用Array.prototype.concat()打平 // 如果是false或假值,整个对象被追加到数组末尾 // Symbol.iterator // 一个方法,该方法返回对象默认的迭代器。由for-of语句使用。这个符号实现了迭代器API的函数 // Symbolmatch // 一个正则表达式方法,该方法用正则表达式去匹配字符串。由String.prototype.match()方法使用 // Symbol.replace // 一个正则表达式方法,该方法替换一个字符串中的匹配字符串。由String.prototype.replace()方法使用 // Symbol.search // 一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由String.prototype.search()使用 // Symbol.species // 一个函数值,该函数作为创建派生对象的构造函数 // Symbol.split // 一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split()使用 // Symbol.toPrimitive // 一个方法,该方法将对象转换为相应的原始值。由ToPrimitive抽象对象使用 // Symbol.toStringTag // 一个字符串,该字符串用于创建对象默认字符串描述。由内置方法Object.prototype.toString()使用 Object类型 对象是一组数据和功能的集合体对象通过new操作符后跟对象类型的名称来创建Object是派生其他对象的基类,Object类型的所有属性和方法在派生的对象上同样存在Object实例的属性和方法: | constructor | 用于创建当前对象的函数 | | — | — | | hasOwnProperty(PropertyName) | 用于判断当前对象实例上是否存在给定的属性。PropertyName是字符串 | | isPrototypeOf(object) | 用于判断当前对象是否为另一个对象的原型 | | propertyIsEnumerable(PropertyName) | 用于判断给定的属性是否可以使用for-in语句枚举。PropertyName是字符串 | | toLocaleString() | 返回对象的字符串表示,该字符串反映对象所在的本地化执行环境 | | toString() | 返回对象的字符串表示 | | valueOf() | 返回对象对应的字符串、数值或布尔值表示 | 操作符 在应用给对象时,操作符通常会调用valueOf()和\或toString()方法来取的可以计算的值 一元操作符 递增/递减操作符(++/–):类比于c语言的递增递减操作符一元加和减:若应用于非数值,则相当于执行了Number()转型函数具体用例: let s = 1.1; console.log(--s); // 0.10000000000000009 位操作符 位操作作用于32位的整数,但ECMAScript中数值以IEEE 754 64位格式存储前32位表示整数值,第32位表示符号(从左到右,为从后到前)正值以真正的二进制格式存储,负值以补码的形式存储NaN和Infinity在位操作中会被当成0处理位操作符应用于非数值,自动使用Number()函数转换该值 按位非(~) 一个操作数作用:返回数值的一补数(二进制数直接取反),在十进制中相当于取反并加1 按位与(&) 两个操作数作用:将两个数的二进制表示对齐,执行”与“操作进行合并 按位或(|) 两个操作数作用:将两个数的二进制表示对齐,执行“或”操作进行合并 按位异或(^) 两个操作数作用:将两个数的二进制表示对齐,执行“异或”操作进行合并 左移(b:a是被右移的数,b是右移的位数作用:按照指定的位数将数值的所有位向右移动,同时保留符号位因右移而在左边空出来的位置用0填充 无符号右移(>>>) a>>>b:a是被右移的数,b是右移的位数作用:按照指定的位数将数值的所有位向右移动,不管是不是符号位(因此负数左移后结果很大)因右移而在左边空出来的位置用0填充 布尔操作符 逻辑非(!) 返回值:布尔值 逻辑与(&&) 可应用于任何类型的操作数&&是一个短路操作符:a&&b,若a对应的布尔值为true,则a&&b的结果是b;若a对应的布尔值为false,则a&&b的结果是false,第二个值就不管了如果有一个操作数是null,返回null如果有一个操作数是NaN,返回NaN如果有一个操作数是undefined,返回undefined 逻辑或(||) 可应用于任何类型的操作数||是一个短路操作符:a||b,若a对应的布尔值为false,则a||b的结果是b;若a对应的布尔值为true,则a||b的结果是true,第二个值就不管了如果有一个操作数是null,返回null如果有一个操作数是NaN,返回NaN如果有一个操作数是undefined,返回undefined典型应用: // 第一个不是null或者undefined 第二个值就不管了 let myObject = preferredObject || backupObject 乘性操作符 乘法操作符(*) 可应用于任何类型的操作数任一操作符是NaN,返回NaNInfinity*0=NaNInfinity*Infinity=Infinity 除法操作符(/) 可应用于任何类型的操作数任一操作符是NaN,返回NaNInfinity/Infinity=NaN0/0=NaN 取模操作符(%) 可应用于任何类型的操作数任一操作符是NaN,返回NaNInfinity%c=NaNc%0=NaNInfinity%Infinity=NaN 指数操作符(**) 加性操作符 加法操作符(+) 用于数值求和: 任一操作符是NaN,返回NaNInfinity+(-Infinity)=NaN+0+(+0)=+0-0+(+0)=+0-0+(-0)=-0 字符串拼接 只要有一个操作数是字符串,就会将另一个操作数转换为字符串,并将两者拼接若任一操作数是对象、布尔值或数值,则调用toString()转换对于undefined和null,调用String()转换为“undefined”和“null” 减法操作符(-) 任一操作符是NaN,返回NaNInfinity-Infinity=NaN+0-(+0)=+0-0-(+0)=-0-0-(-0)=+0(无论是加还是减,都只有-0-0=-0) 关系操作符(< 、>、=) 任一操作符是NaN,返回false结果为布尔值若有任一操作数是字符串、对象或者是布尔值,最终都是数值的比较若两个对象都是字符串,会逐个比较它们的字符编码 相等操作符 等于和不等于(==、!=) 任一操作符是NaN,相等操作返回false,不相等操作返回true(NaN==NaN 是false)结果为布尔值任一操作数是数值、布尔值,或者一个操作数是对象,另一个不是,都会转换为数值进行比较两个操作数都是对象,比较它俩是否都指向同一个对象null==undefined 是truenull和undefined不能转换成其他类型再比较 全等和不全等(=、!) 任一操作符是NaN,相等操作返回false,不相等操作返回true(NaN===NaN 是false)结果为布尔值在比较是不转换操作数null===undefined是false 条件操作符 具体用例: let max = (num > num2) ? num1 : num2; // 若(num > num2)为true (num > num2) ? num1 : num2===num1 // 若(num > num2)为false (num > num2) ? num1 : num2===num2 赋值操作符 逗号操作符 可以用于在一条语句中执行多个操作在赋值时用逗号操作符分隔值,最终会返回表达式的最后一个值 语句 if语句、do-while语句、while语句、for语句、break语句、continue语句与c语言几乎一样 for-in语句 一种严格的迭代语句,用于枚举对象中的非符号键属性因为对象的属性是无序的,所以for-in语句不能保证返回对象属性的顺序具体用例: // 语法:for(property in expression) statement let o = { name: `xiaoming`, age: 11, height: 180, weight: 150, }; for (const propName in o) { console.log(`${propName}`); } // name // age // height // weight for-of语句 一种严格的迭代语句,用于遍历可迭代对象的元素for-of循环会按照可迭代对象的next()方法产生值的顺序迭代元素具体用例: // 语法:for(property of expression) statement for (const el of[1, 4, 3, 2]) { console.log(el); } // 1 // 4 // 3 // 2 标签语句 break语句和continue语句都可以与标签语句一起使用,返回代码中的特定位置(可以很方便地退出多层循环)具体用例: // break let num = 0; for (let i = 0; i if (i == 5 && j == 5) { break; } num++; } } console.log(num);// 95 // break+标签 如果不用标签 会退出一层循环 这个标签在循环最外层 因此退出到了最外层循环 num = 0; outermost: for (let i = 0; i if (i == 5 && j == 5) { break outermost; } num++; } } console.log(num);// 55 // continue num = 0; for (let i = 0; i if (i == 5 && j == 5) { continue; } num++; } } console.log(num); // continue+标签 如果不用标签,会结束本轮j循环进入j+1循环 // 用了标签,结束本轮i循环,进入i+1循环 num = 0; outermost2: for (let i = 0; i if (i == 5 && j == 5) { continue outermost2; } num++; } } console.log(num); switch语句 switch语句与c语言的类似判断条件:switch的参数与条件相等的情况下进入该语句但是,switch语句可以用于所有的变量类型(字符串、变量都是可以的)条件的值可以是常量,变量或者是表达式switch语句在比较条件的值时会使用全等操作符具体用例: // 案例中switch语句是布尔值 条件是表达式 若条件为true 与switch参数相等 就进入语句 let num = 25; switch (true) { case num console.log(pos); position.push(pos); pos = stringValue.indexOf(`a`, pos + 1);// 往后一位 } console.log(position); // [ 3, 9, 18, 35, 39, 43 ] 字符串迭代 具体用例: for (const c of `abcd`){ console.log(c); } // a // b // c // d 大小写转换 String.toLowerCase()String.toLocaleLowerCase()String.toUpperCase()String.toLocaleCase() 字符串模式匹配 String.match():根据参数匹配字符串并返回数组String.replace():参数1表示匹配模式,参数2表示替换字符串String.split():参数1是分隔符,参数2是数组大小,按照参数1将字符串分隔并存放到数组中 单例内置对象 Global 在全局作用域中定义的变量和函数都会变成Global对象的属性 Math 数学特殊值 Math.E自然对数的基数e的值Math.LN1010为底的自然对数Math.LN22为底的自然对数Math.LOG2E以2为底e的对数Math.LOG10E以10为底e的对数Math.PIpi的值Math.SQRT1_21/2的平方根Math.SQRT22的平方根 min()和max()方法 均可以接受任意多的参数,确定这组数的最小值或者最大值 舍入方法 Math.ceil():向上舍入Math.floor():向下舍入Math.round():四舍五入Math.fround():返回数值最接近的单精度(32位)浮点值表示 random()方法 random()方法返回一个0~1范围内的随机数[0,1}随机数生成: // 函数 功能:生成指定范围的随机数 function selectFrom(lowerValue, upperValue) { let choices = upperValue - lowerValue + 1; return Math.floor(Math.random() * choices + lowerValue); } 若为了加密需要而生成随机数,使用window.crypto.getGandomValues() 其他方法 Math.abs(x)绝对值Math.exp(x)e的x次幂Math.expm1(x)e的x次幂-1Math.log(x)x的自然对数Math.log1p(x)x的自然对数+1Math.pow(x,power)x的power次幂Math.hypot(…nums)每个数平方和的平方根Math.clz32(x)32位整数x的前置0的数量Math.sign(x)x的符号Math.trunc(x)x的整数部分Math.sqrt(x)平方根Math.cbrt(x)立方根Math.acos(x)反余弦Math.acosh(x)反双曲余弦Math.asin(x)反正弦Math.asinh(x)反双曲正弦Math.atan(x)反正切Math.atanh(x)反双曲正切Math.atan2(x)y/x的反正切Math.cos(x)余弦Math.sin(x)正弦Math.tan(x)正切 集合引用类型 Object 在对象字面量中,属性名可以是字符串或者数值(数值会自动转换为字符串)对象字面量很适合用于给函数传递大量参数,可选参数用对象来封装,必选参数使用命名参数一般通过点语法使用对象的参数和方法使用中括号访问属性的场景: 想要通过变量访问属性的时候属性命中包含非字母数字字符的时候 Array 数组中每个槽位都可以存储任意类型的数据数组是动态大小的,会随着数据添加而自动增长在实践中要避免使用数组空位。如果确实需要空位,可以显式地使用undefined替代使用Array.length属性可以很方便地向数组末尾添加元素Array.isArray()用于确定一个值是否为数组 创建数组 使用new使用数组字面量Array.from(): 将类数组结构转换为数组实例第一个参数是一个类数组对象,即任何可迭代结构(字符串,集合,映射,数组,arguments,某些自定义对象)第二个参数可选,为映射函数参数,用于直接增强新数组的值(不必再创建中间数组,新数组元素与可迭代结构相应元素一一对应)第三个参数可选:用于指定映射函数中this的值,但这个重写的this在箭头函数中不适用 Array.of():将一组参数转换为数组实例具体用例: const a1 = [1, 2, 3, 4]; // 使用映射函数参数,直接增强新数组的值 const a2 = Array.from(a1, x => x - Math.pow(x, 2)); // 指定this的值 const a3 = Array.from(a1, function(x) { return x ** this.exponent; }, { exponent: 2 }); console.log(a2); // [ 0, -2, -6, -12 ] console.log(a3); // [ 1, 4, 9, 16 ] 迭代器方法 Array.keys():返回数组索引的迭代器Array.values():返回数组元素的迭代器Array.entries():返回索引/值对的迭代器 let a = [1, 2, 3, 4, 5]; let aEntries = Array.from(a.entries()); let aKeys = Array.from(a.keys()); let aValues = Array.from(a.values()); console.log(aEntries); // [ [ 0, 1 ], [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ] console.log(aKeys); // [ 0, 1, 2, 3, 4 ] console.log(aValues); // [ 1, 2, 3, 4, 5 ] // 利用解构拆分键值对 for (const [idx, element] of a.entries()) { console.log(`${idx}:${element}`); } // 0:1 // 1:2 // 2:3 // 3:4 // 4:5 复制和填充 Array.fill(参数1,[start],[end]) 参数1是填充物填充范围为[start,end)(注意start和end都可以是负数,若是负值,则用数组长度加上负值)静默忽略超出数组边界、零长度及方向相反的索引范围 Array.copyWithin(insert,start,end) 从一个数组中[start,end)的范围进行复制,插入到另一个数组的insert位置静默忽略超出数组边界、零长度及方向相反的索引范围 转换方法 Array.toString() Array.toLocaleString() 对每个字符调用Array.toString()或者Array.toLocaleString()方法,拼接而成新字符串 Array.join(参数) 接受一个参数,参数表示字符串分隔符;若不传入参数或者传入undefined,默认逗号进行分隔返回包含所有项的字符串 栈方法与队列方法 栈:先进后出(LIFO,Last-In-First-Out),最先添加的项先被删除队列:先进先出(FIFO,First-In-First-Out),最后添加的先被删除Array.push():接受任意数量的参数,添加到数组末尾,返回数组的最新长度Array.pop():删除数组的最后一项,返回被删除的项Array.shift():删除数组的第一项并返回它Array.unshift():在数组开头增添任意多元素,返回数组的新长度以上四个方法均改变了原数组 排序方法 Array.reverse() 将数组反向排列 Array.sort() sort()方法对数组中元素调用String()方法转型后再进行比较(比较个位数可以,但是多位数不行)sort()方法可以接受一个比较参数,用于判断哪个值应该排在前面(如果第一个参数应该排在第二个参数前面,就返回负值) let values = [0, 4, 33, -9, -9, 0]; // 反向排序 使用箭头函数+条件操作符简化代码 values.sort((a, b) => a b ? -1 : 0); console.log(values); // [ 33, 4, 0, 0, -9, -9 ] // 正向排序 values.sort((a, b) => a > b ? 1 : a name: `xiaoming`, age: 11, }, { name: `xiaowang`, age: 27, }, ]; console.log(people.findIndex((element, index, array) => element.age element.age item prev + cur)); // 10 定型数组 ArrayBuffer() ArrayBuffer是一个构造函数,可用于在内存中分配特定数量的内存空间,ArrayBuffer已经创建就不能调整大小参数,一个参数,为创建的arrayBuffer的大小ArrayBuffer会将所有的二进制位初始化为0要读取或者写入ArrayBuffer,必须通过视图 定型数组 定型数组是一种ArrayBuffer视图,但并不是唯一一种定型数组的类型以Array 来进行划分(如Int8Array是一种类型) ElementType ElementType字节说明范围Int818位有符号整数-128-127Uint818位无符号整数0-255Int16216位有符号整数-32768-32767Uint16216位无符号整数0-65535Int32432位有符号整数-2147483648-2147483647Uint32432位无符号整数0-4294967295Float32432位IEEE-754浮点数-3.4e+38~+3.4e+38Float64864位IEEE-754浮点数-1.7e+308~+1.7e+308 定型数组行为 定型数组大部分的方法都可以照搬普通数组,但要记住,直接修改原数组的方法不可以用到定型数组上面 新增方法 set() 从提供的数组或定型数组中把值复制到当前定型数组中指定的索引位置参数:第一个参数必选,为提供的数组或者定型数组;第二个参数可选,偏移量,默认为0 subarray(): 基于从原始定型数组中复制的值返回一个新的定型数组复制时开始和结束的索引都是可选的 下溢和上溢 定型数组的下溢和上溢不会影响到其他索引可以理解为每一个位置都是固定的,溢出的部分按照二进制固定位切除 Map 一种新的集合类型 创建,增添,查询,删除 Map.set():增添键值对,参数为一个键值对,返回新的集合Map.get():通过键名进行查询,返回对应的值(严格对象相等)Map.has():通过键名进行查询,返回布尔值(严格对象相等)Map.delete:通过键名进行对应键值对的删除(严格对象相等)Map.clear():清除这个映射中的所有键值对Map.size:获取该映射中键值对的数量 顺序与迭代 entries()方法,keys()方法,values()方法:分别返回以插入顺序生成的键值对,键,值的迭代器通过for-of进行迭代就好了 优点 Map的内存占用更小Map插入速度较Object快一点Map的删除操作较快 WeakMap 弱映射行为模式方法都与Map差不多WeakMap的键只能是Object类型或者是继承自Object的类型,值的类型没有限制若没有指向键这个对象的引用,这个对象键会被自动回收WeakMap的键值对不可迭代应用场景:保存关联元数据 保存DOM节点元数据,当对应的DOM 节点被删除后,若没有其他的关联,该键就会被销毁 Set Set在大部分方面与Map类似(就把它当做键值都是同一个东西的Map好了)与Map不同的是,使用add()方法增添元素定义一个实用的Set函数库(函数定义书上有,但是看不懂) WeakSet WeakSet与Set的关系跟WeakMap与Map的关系是一样的 迭代与扩展操作 扩展操作符(…),扩展操作符对可迭代对象实行的是浅复制(意味着只会复制对象的引用) 迭代器与生成器 迭代 迭代器是一个可以由任意对象实现的接口,支持连续获取对象产出的每一个值任何实现Iterable接口的对象都有一个Symbol.iterator属性,这个属性引用默认迭代器默认迭代器是一个工厂函数,调用之后会返回一个实现Iterator接口的对象迭代器必须通过连续调用next()方法才可以连续获取值,这个方法返回一个IteratorObject。 这个对象包含一个done属性和一个value属性。前者是一个布尔值,表示是否还有更多的值可以访问后者包含迭代器返回的当前值这个方法可以通过手动调用next()方法进行消费,也可以通过原生消费,比如for-of循环来自动消费 提前终止迭代器: 可选的return()方法可以提前终止迭代器for-of循环通过break,continue,return,或throw提前退出迭代解构操作并未消费所有值的时候提前退出迭代后面两个是表层的实现,a是迭代器内部的方法,是实现b,c的前提 const a = [1, 2, 3, "4", { b: 5, c: 6 }]; const a1 = [1]; // 调用数组的[Symbol.iterator]属性这个函数 返回一个迭代器 let iter = a[Symbol.iterator](); let iter2 = iter[Symbol.iterator](); // 迭代器与迭代器的迭代器全等 console.log(iter2 === iter); // true // 通过break停止了迭代 但迭代器本身是没有停止的 for (const i of iter) { console.log(i); if (i > 2) { console.log(`stop or not?`); break; } } // 1 // 2 // 3 // stop or not? for (const i of iter) { console.log(i); } // 4 // { b: 5, c: 6 } 生成器 生成器是一种特殊的函数,调用之后返回一个生成器对象生成器对象实现了Iterator接口,可以用在任何消费可迭代对象的地方生成器的独特之处在于支持yield关键字,这个关键字能够暂停生成器函数使用yield关键字之后还可以通过next()方法接收输入和产生输出(next是生成器函数继续运转) // 创建一个生成器 function* generatorFn(initial) { console.log(initial); console.log(yield); } // 实例化一个生成器对象 const generatorObject = generatorFn('foo'); // 第一个next()方法启动生成器对象,此传入值无效 generatorObject.next(`zro`);// foo generatorObject.next(`abb`);// abb // 因为最后没有代码了,再调用next()也没有用了 generatorObject.next(`cdd`); yield*可以将跟在它后面的可迭代对象序列化为一连串值 function* generatorFn() { // 使用yield可以很方便地进行迭代 yield*[1, 2, 3]; } const g = generatorFn(); for (const k of g) { console.log(k); } // 1 // 2 // 3 使用yield*实现递归算法 function* generatorFn(n) { if (n > 0) { yield* generatorFn(n - 1); yield n - 1; } } const g = generatorFn(3); for (const k of g) { console.log(k); } // 0 // 1 // 2 value属性是生成器的返回值,默认为undefined,可以通过生成器的返回指定 function* generatorFn() { // 注意这个是生成器的返回 后面还有一个生成器的方法return() 是用来终止生成器的 return `foo`; } const g = generatorFn(); console.log(g.next()); // { value: 'foo', done: true } 提前终止生成器 return()方法用于提前终止生成器,进入了关闭状态之后就无法恢复了throw()方法在暂停的时候会将错误注入到生成器对象中。 如果错误未被处理,生成器就会被关闭。如果错误被处理了,生成器会跳过对应的yield,但可以恢复执行 function* generatorFn() { for (const x of[1, 2, 3]) { try { yield x; } catch (e) {} } } const g = generatorFn(); console.log(g.next()); // 这个结果与我想象中不太一样 调用throw()方法后2还是被返回了 我不理解 // 并且如果调用throw()方法 生成器内部不处理的话 编辑器是会报错的 // 如果再第一次调用next()方法之前就使用throw()方法的话 也会报错 因为此时生成器还没有启动 console.log(g.throw()); console.log(g.next()); // { value: 1, done: false } // { value: 2, done: false } // { value: 3, done: false } 对象、类与面向对象编程 对象 属性的类型 数据属性 数据属性包含一个保存数据值的位置数据属性有四个特性: [[Configurable]]:属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它修改为访问器属性。默认为true[[Enumerable]]:表示属性是否可枚举(是否可以通过for-in循环返回)。默认为true[[Writable]]:表示属性值([[Value]])是否可修改。默认为true[[Value]]:属性值。默认为undefined let person = {}; Object.defineProperty(person, `age`, { configurable: false, enumerable: true, writable: true, value: 3, }); console.log(person.age); Object.defineProperty(person, `name`, { value: 2, }); console.log(person.age); 访问器属性 访问器属性不包含数据值访问器属性有四个特性: [[Configurable]]:属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它修改为访问器属性。默认为true[[Enumerable]]:表示属性是否可枚举(是否可以通过for-in循环返回)。默认为true[[Get]]:获取函数,在读取访问器属性时,会调用获取函数,并返回一个有效值。默认为undefined[[Set]]:设置函数,在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。默认为undefined 获取函数和设置函数不一定都要定义。只定义获取函数意味着属性是只读的,尝试修改属性会被忽略 对属性的操作 定义和修改属性 Object.defineProperty()方法用于一次设置一个属性Object.defineProperties()方法可用于一次设置多个属性三个参数:要给其添加属性的对象、属性的名称、一个描述符对象(描述符对象上的属性可以包括四个特性,根据有没有数据位置来分辨数据属性和访问器属性) 读取属性特性 Object.getOwnPropertyDescriptor()方法 获取指定属性的属性描述符两个参数:属性所在的对象、属性名返回值:一个对象 Object.getOwnPropertyDescriptor()方法 获取某个对象全部属性的描述符一个参数:某个对象返回值:一个对象 合并对象 Object.assign()方法参数:一个目标对象、一个或多个源对象原理:将源对象中可枚举属性浅复制到目标对象(从源对象的[[Get]]取得属性的值,使用目标函数对象上的[[Set]]设置属性的值。 对象标识及相等判定 Object.is()方法参数:两个用于比较的对象若想检查超过两个值,函数如下: // 函数 用于比较一个或多个值是否相等 function recursivelyCheckEqual(x, ...rest) { return Object.is(x, rest[0]) && (rest.length return this.name_; }, // 由name属性的get函数进行简单的获取 没有这个get函数并不影响设置this.name_的值 // 再由name属性的set函数设置this.name_ // 这样调用sayName()的时候this.name_就是`Matt`了 set name(name) { this.name_ = name; }, // 这里使用可计算属性 [methodKey]() { console.log(`My name is ${this.name_}`); } }; person.name = `Matt`; // get和set都是访问器属性name的函数 person.sayName(); // My name is Matt 对象解构 对象解构是使用与对象匹配的结构来实现对象属性赋值使用解构,可以在一个类似对象字面量的结构中,声明多个变量,同时执行多个赋值操作,如果想让变量直接使用属性的名称,可以使用简写语法(类似于属性值简写,调换过来了)如果引用的属性不存在,则该变量的值是undefined可以在解构赋值的同时定义默认值解构会把源数据结构转换为对象(导致原始值会被当成对象,也就是原始包装类型的属性和方法是可以使用的) 嵌套解构 解构对于嵌套的属性或赋值目标没有限制 let person = { name: `Matt`, age: 16, job: { title: `software engineer`, } }; let personCopy = {}; // 注意:这里是把一个对象的引用直接赋值给personCopy了 也就是只传了地址 一改全改 // 个人认为这里本质上是用一个对象作为过渡 // 但是我不明白为什么personCopy.name可以使用 不是没有定义吗 // 还是说 这一整块放进去括号里面 就默认声明了? ({ name: personCopy.name, age: personCopy.age, job: personCopy.job } = person); console.log(personCopy); 创建对象 构造函数模式 使用new操作符调用构造函数创建对象实例 在内存中创建一个新对象在这个对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性构造函数内部的this指向新对象执行构造函数内部代码(给新对象加属性和方法)若构造函数返回非空对象O,则返回非空对象O;否则,返回刚创建的新对象 构造函数的问题: 若方法函数在构造函数内部声明,则不同实例上的函数虽然同名但不相等若方法函数统一放在外面声明,则自定义类型引用的代码不能很好地聚在一起 // 构造器函数 function Person(name, age, job) { console.log(this); this.name = name; this.age = age; this.job = job; this.sayNmae = function() { console.log(this.name); } } // 实例化对象 类型为Person let person1 = new Person(`kitty`, `22`, `worker`); // Person {} // 若直接调用函数 内部this指向为window Person(`hello`, `3`, `play game`); // 指向window 原型模式 每个函数都会创建一个prototype属性,这个属性是一个对象,包含由特定引用类型的实例共享的属性和方法(这个就是原型)默认情况下,所有原型对象自动获得一个名为constructor的属性,指向与之关联的构造函数每次调用构造函数创建一个新实例,这个实例内部的[[Prototype]]指针就会被赋值为构造函数的原型对象实例与构造函数原型之间有直接联系(prototype指向原型),原型与构造器之间有直接联系(constructor指向构造函数,prototype指向原型)方法: Function.prototype.isPrototypeOf(Object) 确定两个对象时间是否是对应的原型与实例Function.prototype是原型Object是对象实例 Object.getPrototypeOf(Object) 获取对象实例的原型这是一个Object类型的方法参数Object是对象实例 Object.create(Function.prototype) 创建一个新对象,同时为其指定原型参数为指定的原型,是一个对象返回值:对象 属性查找机制 在通过对象访问属性的时候,会按照属性名进行查找搜索开始于对象本身,找到返回,找不到下一步搜索进入原型(进入prototype属性)进行查找,找到返回 遮蔽效果 给对象实例添加一个属性,这个属性会遮蔽原型上的同名属性(即你引用这个属性名,得到的是你自己给对象实例设置的那个属性数据)使用delete操作符可以完全删除实例上的属性,取消遮蔽效果 in操作符 单独使用: in操作符会在可以通过对象访问指定属性时返回truehasOwnPrototype()方法会在属性存在于调用它的对象实例上时返回true // 函数 作用 确定某个属性是否存在于实例对象原型上 // Object是实例对象 name是属性名 为字符串 function hasPrototypeProperty(Object, name) { return !Object.hasOwnProperty(name) && (name in Object); } function Person() { Person.prototype.name = 'Mit'; Person.prototype.age = 13; } const person = new Person; console.log(hasPrototypeProperty(person, `name`)); // true person.name = `Code`; console.log(hasPrototypeProperty(person, `name`)); // false for (k in person) { console.log(k); } for-in 可以通过对象访问且可以被枚举的属性会全部返回,遮蔽原型中不可枚举属性的实例属性也会被返回 属性枚举 for-inObject.keys()方法:接受一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组Object.getOwnPrototypeNames():接受一个对象作为参数,列出所有实例属性,无论是否可枚举Object.getOwnPrototypeStmbol():针对符号,与上面类似2、3、4的枚举顺序是确定的,先以升序枚举数值键,再插入顺序枚举字符串和符号建 对象迭代 Object.values():接收一个对象,返回对象值的数组Object.entries():接收一个对象,返回键值对的数组注意: 非字符串属性会被转换为字符串输出对执行对象进行的是浅复制符号属性会被忽略 其他原型语法 直接通过一个包含所有属性和方法的对象字面量来重写原型 原型的动态性 任何时候对原型所做的修改也会在实例上面反映出来前提是你这个实例确确实实是指向这个原型(就是说你不是先创建实例再重写原型) 原型的问题 原型的问题主要是共享性大家都是可以通过属性名访问原型的,数据一个改了就相当于全部改了 继承 原型链是主要继承方式 原型链 任何函数的默认原型都是一个Object的实例属性可以沿着原型链一直向上查找直到Object原型与继承关系: instanceof操作符:如果一个实例的原型链中出现过相应的构造函数,则返回trueisPrototypeOf()方法,只要原型链中包含这个原型,就会返回true 以对象字面量创建原型的方法会破坏之前的原型链,因为这相当于重写了原型链原型链的问题:原型链中包含引用值的时候,数据就共享了,大家都可以改原型链本链:

具体实例: // 4. 在SuperType的方法上面寻找 找不到 function SuperType() { this.property = true; } // 5. 在SuperType的prototype的方法上面寻找 找到了 返回值是this.prototype 此值为true SuperType.prototype.getSuperValue = function() { return this.property; }; // 1. 在SubType的方法上面寻找 找不到 function SubType() { this.subProperty = false; } // 3. SubType的prototype定义在SuperType上面 SubType.prototype = new SuperType(); // 2. 在SubType的prototype的方法上面寻找 找不到 SubType.prototype.getSubValue = function() { return this.subProperty; }; let instance = new SubType(); // 目的:寻找getSuperValue方法 console.log(instance.getSuperValue()); // true 盗用构造函数 在子类构造函数中调用父类构造函数优点:可以在子类构造函数中向父类构造函数传参缺陷:必须在构造函数中定义方法,因此函数不能重用 组合继承 综合了原型链和盗用构造函数,通过原型链实现方法,且通过盗用构造函数让每实例都有自己的属性具体用例: // 组合继承实现了数据的私有以及方法的共享 function SuperType(name) { this.name = name; this.color = [`black`, `blue`, `white`]; } // 这是原型链 定义方法 SuperType.prototype.sayName = function() { console.log(this.name); }; // 这是盗用构造函数 定义属性 function SubType(name, age) { SuperType.call(this, name); // 增添新数据 this.age = age; } SubType.prototype = new SuperType(); // 向自己的原型链增添新方法 SubType.prototype.sayAge = function() { console.log(this.age); } // 创建SubType的实例1 const instance1 = new SubType(`Marry`, 19); instance1.color.shift(); console.log(instance1.color); // [ 'blue', 'white' ] instance1.sayAge(); // 19 instance1.sayName(); // Marry // 创建SubType的实例2 const instance2 = new SubType(`Tracy`, 23); instance2.color.push(`gray`); // 实例1与实例2的数据并不会互相影响 console.log(instance2.color); // [ 'black', 'blue', 'white', 'gray' ] // 但实例1、实例2和原型链上的方法是共享的 instance2.sayAge(); // 23 instance2.sayName(); // Tracy 原型式继承 原型式继承适用于不需要单独创建构造函数,但仍需要在对象间共享信息的场合原理:通过原型链来共享信息Object.create()方法: 此方法将原型式继承概念化两个参数:作为新对象原型的对象、给新对象定义额外属性的对象(可选)以同一原型创建的对象实例间可以信息共享 具体用例 // 这个object()函数会创建一个临时的构造函数,将传入的对象赋值给这个构造函数的原型, // 然后返回这个临时类型的一个实例 // 本质上,object()是对传入的对象进行了一次浅复制 function object(o) { function F() {} F.prototype = o; return new F; } 寄生式继承 创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象 寄生式组合继承 组合继承存在效率问题:父类构造函数始终会被调用两次寄生式组合继承可以算是目前引用类型继承的最佳模式具体用例: // 寄生式的核心函数 function inheritPrototype(SubType, SuperType) { const prototype = new Object(SuperType.prototype); // 创建对象 prototype.constructor = SubType; // 增强对象 SubType.prototype = prototype; // 赋值对象 } function SuperType(name) { this.name = name; this.color = [`black`, `blue`, `white`]; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } // SubType.prototype = new SuperType(); 这一行被取代了 // 原本是直接创建一个SuperType实例作为SubType.prototype inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); } const instance1 = new SubType(`Marry`, 19); instance1.color.shift(); console.log(instance1.color); // [ 'blue', 'white' ] instance1.sayAge(); // 19 instance1.sayName(); // Marry const instance2 = new SubType(`Tracy`, 23); instance2.color.push(`gray`); console.log(instance2.color); // [ 'black', 'blue', 'white', 'gray' ] instance2.sayAge(); // 23 instance2.sayName(); // Tracy 类 类定义 类的背后使用的是原型和构造函数的概念类受块作用域限制默认情况下,类中的代码都在严格模式下执行 类构造函数 实例化 constructor关键字用于在类定义内部创建类的构造函数在使用new操作符创建类的新实例时,会调用constructor方法类实例化时传入的参数会用作类构造函数的参数默认情况下,类构造函数在执行后会返回this对象(就是类本身);但是如果让类构造函数返回别的对象,那实例的类型就改变了调用类构造函数必须使用new 把类当成特殊函数 类标识符有prototype属性,这个原型也有一个constructor属性指向类自身类中定义的constructor方法不会被当成构造函数(new的是谁谁就是构造函数,new 类,类是构造函数;new constructor,constructor是构造函数类可以作为参数传递 实例、原型和类成员 实例成员 每个实例都对应一个唯一的成员对象,这意味着所有的成员都不会在原型上共享 原型方法与访问器 添加到this的所有内容都会存在于不同的实例上面,在类块中定义的所有内容都会定义在类的原型上不能再类块中给原型添加原始值或对象作为成员数据,但是可以添加方法类定义支持获取和设置访问器具体用例: class Person { constructor() { this.locate = () => console.log(`instance`); } locate() { console.log(`prototype`); } } const p = new Person(); p.locate(); // instance Person.prototype.locate(); // prototype 静态类方法 通常用于执行不特定于实例的操作,也不要求存在类的实例静态成员每个类上只能有一个使用static关键字作为前缀 非函数原型和类成员 类定义不显式支持在原型或类上添加成员数据但在类定义外部,可以手动添加 迭代器与生成器方法 类定义语法支持在原型和类本身上定义生成器语法可以通过添加一个默认的迭代器([Symbol.iterator]),将类实例变成可迭代对象 继承 继承基础 类继承背后是原型链使用extends关键字,可以继承任何拥有[[Construct]]和原型的对象派生类(继承者)都会通过原型链访问到类和原型上定义的方法,this的值指向调用相应方法的实例或类 super关键字 派生类的方法可以通过super关键字引用它们的原型且仅限于类构造函数、实例方法和静态方法内部在类构造函数中使用super可以调用父类构造函数在静态方法中可以通过super调用继承的类上定义的静态方法注意: super只能在派生类构造函数和静态方法中使用不能单独引用super关键字调用super()会调用父类构造函数,并将返回的实例赋值给this在类构造函数中,不能在调用super之前引用this如果再派生类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象 抽象基类 抽象基类是指可供其他类继承,但本身不会被实例化的类 // 抽象基类 class Vehicle { constructor() { console.log(new.target); if (new.target === Vehicle) { throw new Error(`Vehicle cannot be directly instantiated`); } } } // 派生类 class Bus extends Vehicle {}; new Bus(); // [class Bus extends Vehicle] // new Vehicle(); // Error: Vehicle cannot be directly instantiated 继承内置类型 有些内置类型的方法会返回新实例。默认情况下,返回实例的类型与原始实例的类型是一致的如果想覆盖这个行为,可以覆盖Symbol.species访问器,这个访问器决定在创建返回的实例时使用的类 类混入 如果只需要混入多个对象的属性,可以使用Object.assign()方法混入模式可以通过在一个表示式中连缀多个混入元素来实现,这个表达式最终会解析为一个可以被继承的类具体用例: class Vehicle {} let FooMixin = (Superclass) => class extends Superclass { // 这里面放自己需要的代码 foo() { console.log(`foo`); } }; let BarMixin = (Superclass) => class extends Superclass { bar() { console.log(`bar`); } }; let BazMixin = (Superclass) => class extends Superclass { baz() { console.log(`baz`); } }; // 通过这个辅助函数 将嵌套调用展开 不过这个函数我看不懂 function mix(BaseClass, ...Mixins) { return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass); } class Bus extends mix(Vehicle, FooMixin, BarMixin, BazMixin) {} let b = new Bus(); b.foo(); // foo b.bar(); // bar b.baz(); // baz 现在更提倡组合模式 把方法提取到独立的类和辅助对象中,然后把它们组合起来,但不使用继承这样在代码设计中可以提供很大的灵活性 函数 箭头函数 箭头函数不能使用argements、super和new.target箭头函数不能用作构造函数箭头函数没有prototype属性 函数名 函数名是指向函数的指针,所以一个函数可以有多个名称使用不带括号的函数名会访问函数指针,而不会执行函数 参数 理解参数 JS中函数既不关心传入参数的个数,也不关心这些参数的数据类型JS的函数参数在内部表现为一个数组argements argements对象是一个类数组对象,存储着传进来的每个参数值argements.length是它的长度argements对象的值会自动同步到对应的命名参数;但是argements跟命名参数的值在内存中是分开的,只是会保持同步而已 默认参数 在ES6之后,可以显式定义默认参数,只要在函数定义中的参数后面用=就可以为参数赋一个默认值给函数传undefined相当于没有传值,不过这样可以利用多个独立的默认值argements对象的值不反映参数的默认值,只反映传给函数的参数默认参数值可以是调用函数返回的值函数的默认参数只有在函数被调用的时候才会求值,不会在函数定义时求值默认参数会按照定义它们的顺序依次被初始化后定义的默认值可以引用先定义的参数,前面定义的参数不能引用后面定义的参数 参数扩展与收集 扩展操作符既可以用于调用函数时传参,也可以用于定义函数参数扩展参数:对可迭代对象应用扩展操作符,并且将其作为一个参数传入,可以将可迭代对象拆分,并将迭代返回的每个值单独传入收集参数: 在构思函数定义时,可以使用扩展操作符把不同长度的独立参数组合为一个数组收集参数的操作只能放到形参列表的最后 函数声明与函数表达式 函数声明会被提升,但是函数表达式不会 函数作为值 函数名在JavaScript中就是变量,所以函数可以用在任何可以使用变量的地方函数可以作为参数传给另一个函数,也可以在一个函数中返回另一个函数 函数内部 argements callee:是argements对象的一个属性,是一个指向argements对象所在函数的指针使用argements.callee可以很方便地将函数逻辑与函数名解耦但是,在严格模式下访问argements.callee会报错,因此不推荐使用,可以使用命名函数表达式解决耦合问题 this 在标准函数中,this指向把函数当成方法调用的上下文对象在箭头函数中,this指向定义箭头函数的上下文,可以使用箭头函数解决事件回调或定时器回调函数的问题 function King() { console.log(this); // King {} 这是一个普通函数 且不是以方法的形式调用 this就指向函数本身 this.name = 'xiaoming'; setTimeout(() => { console.log(this); // King { name: 'xiaoming' } 这是一个箭头函数 定义在定时器里面 // 但上下文据此推测是该函数 console.log(this.name); // xiaoming }, 5000); } function Queen() { console.log(this); // Queen {} this.name = `xiaohong`; setTimeout(function() { // 这是一个普通函数 调用该函数的是定时器 因此该函数内部的this指向定时器 console.log(this); // Timeout { // _idleTimeout: 10000, // _idlePrev: null, // _idleNext: null, // _idleStart: 51, // _onTimeout: [Function (anonymous)], // _timerArgs: undefined, // _repeat: null, // _destroyed: false, // [Symbol(refed)]: true, // [Symbol(kHasPrimitive)]: false, // [Symbol(asyncId)]: 9, // [Symbol(triggerId)]: 1 // } console.log(this.name); // undefined }, 10000) } new King(); new Queen(); new.target 用于检测函数是否使用了new关键字如果函数正常调用,new.target的值是undefined;入股使用new关键字调用,则new.target将指向被调用的构造函数 函数的属性和方法 属性 length:保存函数定义的命名参数的个数prototype:保存了引用类型所有实例方法,此属性是不可枚举的 方法 apply(): 作用:以指定的this值来调用函数(设置函数调用是函数体内的this对象的值)两个参数:函数内的this值,一个参数数组(可以是Array实例、argements对象) call(): 作用:以指定的this值来调用函数(设置函数调用是函数体内的this对象的值)两个参数:函数内的this值,第二个参数是逐个传递给该方法的参数 bind(): 创建一个新的函数实例,其this值会被绑定到传给bind()对象的值 递归 善于使用命名函数表达式将函数逻辑与函数名解耦 // 这里是先执行函数命名表达式 因为它在括号里面 // 执行函数命名表达式的结果是返回一个函数的地址 跟函数表达式声明一样 // 其实这里括号不要也可以 const factorial = (function f(num) { if (num return num * f(num - 1); } }); // 首先 factorial是动不了的 其次动了也没有意义 // 要减少耦合 只是说赋值给别人后 别人也可以用 const anotherFactorial = factorial; console.log(factorial); // [Function: f] console.log(anotherFactorial(3)); 尾调用优化 尾调用优化的条件 代码在严格模式下执行外部函数的返回值是对尾调用函数的调用尾调用函数返回后不需要执行额外的逻辑尾调用函数不是引用外部函数作用域中自由变量的闭包斐波那契数列用例: // 优化后 `use strict`; // 基础框架 function fib(n) { return fibImpl(0, 1, n); } function fibImpl(a, b, n) { if (n === 0) { return a; } return fibImpl(b, a + b, n - 1); } console.log(fib(1000)); // 优化前 function fib(n) { if (n // 私有变量和私有函数 let privateVariable = 10; function privateFunction() { return false; } // 公有方法 此函数是与MyObject的活动对象关联的 // 在实例上调用此方法 使privateVariable自增 且调用privateFunction函数 // 实现了 公有方法访问私有数据 this.publicMethod = function() { privateVariable++; return privateFunction(); }; } // 这是一个匿名函数 且此函数表达式立刻执行 (function() { // 私有变量和私有函数 let privateVariable = 10; function privateFunction() { return false; } // 创建对象 MyObject = function() {}; // 通过prototype共享方法 MyObject.prototype.publicMethod = function() { privateVariable++; return privateFunction(); }; })(); 模块模式 在模块模式中,单例对象作为一个模块,经过初始化可以包含某些私有数据,而这些数据又可以通过其暴露的公共方法来访问实质上是通过直接将公共接口分派给实例来实现共享 模块增强模式 适用于单例对象需要某个特定类型的实例,但又必须给它添加额外属性或方法的场景 // 此函数是立即调用并返回函数表达式的值给singleton // 通过singlrton就可以调用公有方法 进而使用私有数据 私有方法 let singleton = function() { // 私有变量 私有函数 let privateVariable = 10; function privateFunction() { return false; } // 创建对象 let object = new CustomType(); // 增添特权/公有属性和方法 // 定义了一个匿名函数 这是一个闭包函数 与singleton的活动对象关联 object.publicMethod = function() { privateVariable++; return privateFunction(); }; // 返回对象 这个就是哈数表达式的返回值 return object; }(); 期约与异步函数 同步与异步 同步行为对应内存中顺序执行的处理器指令异步执行类似于系统中断,即当前进程外部的实体可以触发代码执行 期约(Promise) 期约的主要功能是为异步代码提供了清晰的抽象。可以用期约表示异步执行的代码块,也可以用期约表示异步计算的值。在需要串行异步代码时,期约的价值最突出 期约基础 期约实例化 Promise(期约)是一种引用类型,可以通过new操作符来实例化创建期约时需要传入执行器(executor)函数作为参数 期约状态机 期约时有状态的对象,可能状态如下: 待定(pending)兑现/解决(fulfilled/resolves)拒绝(rejected) 无论落定为哪种状态,都是不可逆的期约状态切换为兑现,会产生一个私有的内部值(Value);期约的状态切换为拒绝,就会产生一个私有的内部理由(reason) 执行函数控制期约状态 执行器函数的职责: 初始化期约的异步行为控制状态的最终转换 通过执行函数的两个参数控制状态装换 resolve():切换状态为兑现reject():切换状态为拒绝 执行器函数是同步执行的定时退出功能: // 为避免期约卡在特定状态 可以设置定时退出功能 // 但是这玩意儿是会报错的 不好用 let p = new Promise((resolve, reject) => { setTimeout(reject, 5000); }); setTimeout(console.log, 0, p); setTimeout(console.log, 6000, p); Promise.resolve() 通过调用Promise.resolve()方法,可以实例化一个解决的期约实际上,使用这个方法,可以把任何一个值转换为一个期约解决期约的值是这个方法的第一个参数如果传入值本身是一个期约,那它的行为相当于一个空包装(什么都不做,也不改变状态) // 这两种行为是等价的 let p1 = new Promise((resolve, reject) => resolve()); let p2 = Promise.resolve(); Promise.reject() 会实例化一个拒绝的期约并抛出一个异步错误,这个拒绝的期约的理由就是传给Promise.reject()的第一个参数拒绝期约的错误是通过浏览器异步信息队列来进行处理的 期约的实例方法 Promise.prototype.then() 这个方法是为期约实例添加处理程序的主要方法参数: 最多两个,且两个都输可选的,onResolevd处理程序和onRejected处理程序,会在期约分别进入“兑现”和“拒绝”状态时执行传给then()的任何非函数类型参数都会被静默忽略如果只提供一个参数,另一个参数的位置上最好传入null 返回值: 一个新实例,该实例基于onResolved处理程序的返回值构建(用Promise.resolve()包装返回值并返回)若onResolved处理程序没有提供,则包装上一个期约解决之后的值抛出异常会返回拒绝的期约返回的错误对象会被包装在一个解决期约中返回 Promise.prototype.catch() 该方法用于给期约添加拒绝处理程序参数:一个,onRejected处理程序 7返回值:新的期约实例,返回情况与Promise.prototype.then()一样 Promise.prototype.finally()

该方法用于给期约添加onFinally处理程序,这个处理程序在期约状态改变时(转为解决或者拒绝)执行

此方法在大多数情况下表现为父期约的传递

非重入期约方法 当期约进入落定状态时(也就是生成实例的时候),与该状态相关的处理程序(就是这个期约的代码)仅仅会被排期,而非立刻执行处理程序会被推进信息队列,在当前线程上的同步代码执行完成后,处理程序才会执行 传递解决值和拒绝理由 在执行函数中,解决的值和拒绝的理由是分别作为resolve()和reject()的第一个参数往后传的然后,这些值又会传给它们·各自的处理程序,作为onResolved和onRejected的唯一参数 拒绝期约与拒绝状态处理 在期约的执行程序或处理程序中抛出错误会导致拒绝,对应的错误对象会成为拒绝的理由。期约可以以任何理由被拒绝,但最好使用统一的错误对象,这样可以让浏览器捕获错误对象中的栈追踪信息所有错误都是异步抛出且未处理的,并不会阻止运行时继续执行同步指令 期约连锁与期约合成 期约连锁 一个期约接一个期约地拼接让每个后续期约都等待之前的期约,也就是串行化异步任务期约连锁可以构建有向非循环图的结构: 实例方法添加的处理程序是有向定点,图中的每个节点都会等待前一个结点落定,图的方向就是期约的解决或拒绝顺序由于期约的处理程序是先添加到信息队列,然后才逐个执行,因此构成了层次遍历 具体事例: let p1 = new Promise((resolve, reject) => { console.log(`p1 executor`); setTimeout(resolve, 1000); }); p1.then(() => new Promise((resolve, reject) => { console.log(`p2 executor`); setTimeout(resolve, 1000); })) .then(() => new Promise((resolve, reject) => { console.log(`p3 executor`); setTimeout(resolve, 1000); })) .then(() => new Promise((resolve, reject) => { console.log(`p4 executor`); setTimeout(resolve, 1000); })); // 将生成期约的代码提取到一个工厂函数中 // 使用的时候只要修改工厂函数的名称与功能代码就可以了 function delayedResolve(str) { return new Promise((resolve, reject) => { console.log(str); setTimeout(resolve, 1000); }); } delayedResolve(`p1 executor`) .then(() => delayedResolve(`p2 executor`)) .then(() => delayedResolve(`p3 executor`)) .then(() => delayedResolve(`p4 executor`)); 期约合成 将多个期约组合为一个期约 Promise.all() 此静态方法创建的期约会在一组期约全部解决之后再解决(就是说只有一组期约全部解决了,返回的新期约才是解决的)接受一个可迭代对象,返回一个新期约如果所有期约全部解决了,则合成期约的解决值就是包含期约解决值的数组,按照迭代器顺序如果有期约拒绝,则第一个拒绝的期约的理由就是新合成期约拒绝的理由,之后再拒绝的期约不会影响合成期约拒绝的理由(但是拒绝期约该处理的还是会处理的,只是理由不拿出来用而已)具体实例: // Promise.all() let p1 = Promise.all([ Promise.resolve(3), Promise.resolve(4), Promise.resolve(5) ]).then((values) => setTimeout(console.log, 0, values)); // [ 3, 4, 5 ] let p2 = Promise.all([ Promise.resolve(3), Promise.resolve(4), Promise.reject(5) ]).then(null, (reason) => console.log(reason)); // 5 Promise.race() 此静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像(无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约(也是该解决的解决,该拒绝的拒绝,只是不显示出来而已)接受一个可迭代对象,返回一个新期约值具体示例: // Promise.race() let p3 = Promise.race([ Promise.resolve(3), Promise.resolve(4), Promise.resolve(5) ]).then((values) => setTimeout(console.log, 0, values)); // 3 let p4 = Promise.race([ Promise.reject(3), Promise.resolve(4), Promise.resolve(5) ]).catch((reason) => setTimeout(console.log, 0, reason)); // 3 串行期约合成 基于后续期约使用之前期约的返回值来串联期约是期约的基本功能(将期约合成起来,渐进地消费一个值)具体示例: function addTwo(x) { return x + 2; } function addThree(x) { return x + 3; } function addFive(x) { return x + 5; } // 利用扩展操作符进行参数收集 function compose(...fns) { // 返回一个箭头函数 箭头函数参数为x // 利用Array.reduce()方法进行归并 // 利用期约进行参数传递 return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x)) } let addTen = compose(addTwo, addThree, addFive); addTen(8).then(console.log); // 18 异步函数 async async关键字用在函数声明、函数表达式、箭头函数和方法上,使普通函数具有异步特征异步函数如果使用return返回了值(没有return则返回undefined),这个值会被Promise.resolve()包装成一个期约对象。异步函数始终返回期约对象 await 使用await可以暂停异步函数的执行,等待任务的解决。await会尝试“解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行await只能直接出现在异步函数的定义中(不能跟异步函数有隔阂了)await可以直接传递函数的返回值具体用例: async function foo() { console.log(2); console.log(await Promise.resolve(8)); console.log(9); } async function bar() { console.log(4); console.log(await Promise.resolve(6)); console.log(7); } console.log(1); foo(); console.log(3); bar(); // 1 // 2 // 3 // 4 // 8 // 9 // 6 // 7 异步函数策略 非阻塞暂停sleep // 非阻塞暂停sleep async function sleep(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); } async function foo() { const t0 = Date.now(); // 暂停执行1500ms await sleep(1500); console.log(Date.now() - t0);; } foo(); // 1509 如果顺序不是必须保证的,可以先一次性初始化所有的期约,然后分别等待它们的结果 BOM BOM提供了与网页无关的浏览器功能对象 window对象 视口位置 用户可以通过滚动在有限视口中查看文档质量文档相对于视口滚动距离的属性有两对,返回相等的值:window.pageXoffset/window.scrollX和window,pageYoffset/window.scrollY(分别是x轴方向上的滚动距离和y轴方向上的滚动距离)scroll()、scrollTo()、scrollBy() 接受表示相对视口距离的x和y坐标在前两个中表示要滚动到的坐标,在后一个表示滚动的距离(可以用来做返回顶部效果)这三个方法有一个属性behavior:auto表示正常滚动,smooth表示平滑滚动 window.scrollTo({ left: 0, top: 300, behavior: 'smooth' }); // 有一个问题是我在调试的时候 behavior设置为auto它就不移动了 导航与弹出窗口 window.open()用于导航到指定的URL,也可用于打开新的浏览器窗口四个参数:要加载的URL,目标窗口,特性字符串,表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值 定时器 setTimeout(): 用于指定在一定时间后执行某些代码两个参数:要执行的代码(字符串或函数),在实行回调前等待的时间(毫秒)返回值:一个表示该超时排期的数值ID clearTimeout(ID):根据ID来取消等待中的排期任务setInterval(): 用于指定每个一段时间执行相应的代码两个参数:要执行的代码(字符串或函数),把下一次执行定时代码的任务添加到队列要等待的时间(毫秒)注意:这里的间隔时间指的是向队列添加新任务之前等待的时间,浏览器是不关心这个任务什么时候执行或者执行要花多长时间的,因此,执行时间短,非阻塞的回调函数比较适合setInterval返回值:一个循环定时ID clearInterval(ID):根据ID来取消循环定时循环任务也是可以通过setTimeout()来设置的,并且最好通过setTimeout()来设置。因为若使用setInterval(),一个任务结束和下一个任务开始之间的时间间隔是无法保证的 let num = 0; let max = 10; let incrementNumber = function() { num++; // 如果还没有达到最大值,再设置一个超时任务 if (num console.log(`Done`); } } setTimeout(incrementNumber, 500); 系统对话框 alert():警示框,接受一个参数confirm(): 确认框接收一个参数,用户提示文本返回值:true表示用户点击了确认,false表示用户点击了撤销 prompt(): 提示框接收两个参数,要提示给用户的文本,文本框的默认值返回值:若用户单击OK,返回值是文本框的值;若用户单击Cancel,返回值是null alert(`alert`); confirm(`confirm`); prompt(`prompt`, `prompt`);

![image.png](https://img-blog.csdnimg.cn/img_convert/ae0d737818c59b6cb37ecce6131f70ea.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=127&id=Uiz9p&margin=[object Object]&name=image.png&originHeight=127&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2610&status=done&style=none&taskId=u116e502f-bacb-4027-b7a6-7e667d34838&title=&width=450)![image.png](https://img-blog.csdnimg.cn/img_convert/64b9c77918bf1fc6bec23b2333b3b38c.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=127&id=qrvhF&margin=[object Object]&name=image.png&originHeight=127&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=3341&status=done&style=none&taskId=u42e5c39a-37c7-493b-a99a-75f591c0b08&title=&width=450)![image.png](https://img-blog.csdnimg.cn/img_convert/8b5b5e71b3592bb86fb39b18e9853ac9.png#clientId=u03924aba-0597-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=165&id=Bh9C2&margin=[object Object]&name=image.png&originHeight=165&originWidth=450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4258&status=done&style=none&taskId=ub6bffc96-c2e7-4a08-8869-e493bef9a93&title=&width=450)

location对象 提供当前窗口中加载文档的信息,以及通用的导航功能它既是window属性,也是document属性 属性 location 对象属性返回值location.herf获取或者设置 整个URLlocation.host返回主机(域名)location.port返回端口号 如果未写返回 空字符串location.pathname返回路径location.search返回参数location.hash返回片段 #后面的内容 常见于链接 锚点 URLSearchParams URLSearchParams提供了一组标准API方法通过它们可以检查和修改查询字符串参数:一个查询字符串返回值:一个URLSearchParams类型的实例具体用例: // 这就是查询字符串 let qs = `?q=javascript&num=10`; // 通过new进行实例化 let searchParams = new URLSearchParams(qs); // toString() console.log(searchParams.toString()); // q=javascript&num=10 // has() console.log(searchParams.has(`num`)); // true // get() console.log(searchParams.get(`num`)); // 10 // set() searchParams.set(`page`, `3`); // q=javascript&num=10&page=3 console.log(searchParams.toString()); // delete() searchParams.delete(`q`); console.log(searchParams.toString()); // num=10&page=3 // 支持迭代 for (const param of searchParams) { console.log(param); // [ 'num', '10' ] // [ 'page', '3' ] } location.herf 可用于修改浏览器地址 navigator navigator对象的属性通常用于确定浏览器的类型 检测插件 plugins是navigator的一个属性返回值: name:插件名称description:插件介绍filename:插件的文件名length:由当前插件处理的MIME类型数量 处理注册程序 registerProtocolHandler()方法这个方法可以把一个网站注册为处理某种特定类型信息应用程序三个参数:要处理的协议,处理该协议的URL,应用名称 history对象 go(): 可以在用户历史记录中验任意方向导航,前进后退都可以一个参数,整数,表示前进后退多少步 back():后退一部forward():前进一步 DOM DOM(文档对象模型)是HTML和XML文档的编程接口。DOM表示有多层节点构成的文档,通过它开发者可以添加、删除和修改页面的各个部分DOM中总共有12种节点类型,这些类型都继承一种基本类型 节点层级 Node类型 所有界定啊类型都继承Node类型,因此所有类型都共享相同的基本属性和方法 节点关系 每个节点都有一个children属性,其中包含一个NodeList的实例NodeList是一个类数组对象,NodeList是实时的活动对象,DOM 结构的变化会自动地在NodeList中反映出来(直接用NodeList赋值的对象是实时的,但是Nodelist.length赋值的变量不是实时的)每个节点都有一个parentNode属性,指向其DOM树中的父节点previousSibling和nextSibling属性用于在同胞节点间移动firstChild和lastChild属性分别指向父节点中第一个和最后一个节点hasChildNode()方法用于确认父节点是否有子节点ownerDocument属性指向代表整个文档的文档节点 操纵节点 appendChild()方法,用于在childNode列表末尾添加节点(添加的节点会成为childNode中的最后一个,若添加的节点是原本的第一个节点,添加了之后就变成最后一个了)insertBefore()方法,两个参数,要出入的节点与参照节点replaceChild()方法,两个参数,要插入的节点和要替换的节点removeChild()方法,一个参数,要删除的节点cloneNode()方法,一个参数,true为深复制,默认为false,浅复制(深复制会复制节点及其整个子DOM树normalize()方法:检测这个节点的所有后代,删除空文本节点,合并同胞文本节点 Document类型 文档子节点与文档信息 文档节点document.documentElement指向< html >元素document.body指向bodydocument.doctype指向< !doctype >标签document.title指向< title >document.URL包含当前页面的完整URLdocument.domain包含包含页面的域名document.referee包含链接到当前页面的那个页面的URL 定位元素 getElementById()方法getElementsByTagName()方法:返回包含零个后多个元素的NodeList(实时更新的哦) 特殊集合 以下特殊集合都是HTMLCollection的实例,因此都是实时更新的document.anchors:带name的< a >元素document.forms:< form >元素document.images:< img >元素document.links:带href的< a >元素 // 文档中所有a标签 let as = document.anchors; let length = document.anchors.length; console.log(as, length); // HTMLCollection [a, aaa: a // 复制并添加a标签在文档中 let a2 = document.querySelectorAll(`a`); let a3 = a2[0].cloneNode(); let script = document.querySelector(`script`); script.appendChild(a3); console.log(as, length); // HTMLCollection(2) [a, a, aaa: a] 1 console.log(as, length); // HTMLCollection(3) [a, a, a, aaa: a] 1 // 确实会实时更新的 不必手动再获取一次 就连被赋值的变量都是如此 // 直接被HTMLCollection赋值的变量是会实时更新的 // 但是HTMLCollection的长度赋值的变量并不会实时更新 文档写入 document.write():接受一个字符串写入到文档中document.eriteln():接受一个字符串写入到文档中,并在字符串末尾追加一个换行符document.open():打开网页输出流document.close():关闭网页输出流 Element类型 取得属性与设置属性 getAttribute():主要用于取得自定义属性的值setAttribute():两个参数,属性名与属性值attributes属性:实时集合,保存节点的属性(键值对形式)removeAttribute():删除指定名字的属性 创建元素 document.createElement()方法:一个参数,标签名,创建该标签名的元素 Text类型 操作文本 appendData(text):向节点末尾添加文本textdeleteData(offset,count):从位置offset开始删除count个字符insertData(offset,text):在位置offset插入textreplaceData(offset,count,text)spliceText(offset):在位置offset拆分文本节点substringData(offset,count):提取文本 const div = document.createElement(`div`); const node1 = document.createTextNode(`hello `); const node2 = document.createTextNode(`world!`); const body = document.body; div.appendChild(node1); div.appendChild(node2); body.appendChild(div); // hello world! Comment类型 注释 DOM编程 动态脚本 hello world! // 传两个参数没什么意思的 因为script标签是插入到body的最后面才是最好的 // 或者插入到head中 然后在script代码中使用onload就好了 function loadScript(url, element) { let script = document.createElement(`script`); script.src = url; element.appendChild(script); } loadScript('./67-动态脚本.js', document.body); MutationObserver接口 使用MutationObserver可以观察整个文档、DOM树的一部分、或某个元素,此外还可以观察元素属性、子节点、文本,或者三者任意组合变化 基本用法

调用MutationObserver构造函数并传入一个回调函数来创建

Observer()方法,两个参数,要观察变化的DOM节点,一个MutationObserver对象(MutationObserver对象是一个键值对形式配置选项的字典,用于控制观察哪些方面的变化)

每个回调都会收到一个MUtationObserver实例的数组,表示哪里发生了变化,发生了什么变化

MutationObserver实例的属性: | target | 被修改影响的节点 | | — | — | | type | 字符串,变化的类型 | | oldValue | 变化之前的值 | | attributeName | 被修改属性的名字 | | attributeNamespace | 被修改属性的名字 | | addedNodes | 变化中添加的节点,默认为空NodeList | | removeNodes | 变化中删除的节点,默认为空NodeList | | previousSibling | 变化节点的前一个同胞节点 | | nextSibling | 变化节点的后一个同胞节点 |

disconnect()方法:提前终止执行回调,并且也会抛弃已经加入任务队列要异步执行的回调

多次调用observer()方法,可以复用一个MutationObserver对象观察多个不同的目标节点

MutationObserverInit与观察范围

MutationObserverInit对象用于控制对目标节点的观察范围,观察者可以观察的事件包括属性变化,文本变化和子节点变化

MutationserverInit对象的属性: | subtree | 布尔值,是否观察目标节点的子树 | | — | — | | attributes | 布尔值,是否观察目标节点的属性变化 | | atributeFilter | 字符串数组,要观察哪些属性变化 | | attributeOldValue | 布尔值,是否记录变化之前的属性值 | | charactertData | 布尔值,修改字符数据是否触发变化事件 | | characterDataOldValue | 布尔值,是否记录变化之前的字符数据 | | childList | 布尔值,修改目标节点的子节点是否触发变化事件 |

具体用例:

MutationObserver MutationObserver // 实例化MutationObserver对象 let observer = new MutationObserver((mutationRecords) => { console.log(mutationRecords); }) const div = document.querySelector(`div`); const p = document.querySelectorAll(`p`)[0]; const text = p.firstChild; observer.observe(div, { attributes: true, childList: true }); observer.observe(text, { characterData: true }); // 添加属性 div.setAttribute(`foo`, `bar`); // 增添节点 let p1 = p.cloneNode(true); div.appendChild(p1); // 改变文本 text.textContent = `delete`; // (3) [MutationRecord, MutationRecord, MutationRecord] 异步回调与记录队列 其核心是异步回调与记录队列模型takeRecords()方法可以清空队列,取出并返回其中的所有的MutationObserver实例 性能、内存与垃圾回收 MutationObserver实例对于目标节点之间的引用关系是非对称的。MutationObserver用于对要观察节点的弱引用,但是目标节点拥有对MutationObserver的强引用(节点没了MutationObserver没了,MutationObserver还在不阻止垃圾回收程序回收节点) DOM扩展 Selectors API querySelector()方法querySelectorAll()方法match()方法,接收一个CSS选择符参数,如果元素匹配则返回true 元素遍历 let parentElement = document.getElementById(`parent`); // firstElementChild 指向第一个element类型的子元素 let currentChildElement = document.parentElement.firstElementChild; // 若没有子元素 firstElementChild返回null 退出循环 while (currentChildElement) { // 这就是元素节点 做相应处理 processChild(currentChildElement); if (currentChildElement === parentElement.lastElementChild) { break; } else { currentChildElement = currentChildElement.nextElementSibling; } } HTML5 getElementsByClassName():返回值是NodeListclassList属性: add(value)方法contains(value)方法:表明指定value是否存在remove(value)方法:toggle(value)方法:如果类名列表中已经存在指定value,则删除;若不存在,则添加(这个属性可以挺方便地制造出切换效果) 焦点管理 document.activeElement:始终包含当前拥有焦点的DOM元素document.hasFocus()方法:表示文档是否用于焦点 HTMLDocument扩展 readyState属性 可以用于判断文档是否加载完毕值: loading:文档正在加载complete:文档加载完成 自定义数据属性 自定义属性要前缀data-元素的dataset属性可用于访问属性 插入标记 innerHTML today is a beautiful day play a game have a sleep make a study 插入 const content = document.querySelector(`.content`); const btn = document.querySelector(`button`); btn.addEventListener('click', () => { content.innerHTML = `

tomorrow is a beautiful day

`; })

![image.png](https://img-blog.csdnimg.cn/img_convert/432d32700aaa7486570255cc5ab08553.png#clientId=u4050800a-b296-4&crop=0&crop=0.031&crop=1&crop=1&from=paste&height=161&id=u46ca2441&margin=[object Object]&name=image.png&originHeight=161&originWidth=214&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4366&status=done&style=none&taskId=uf9ab9aa5-3277-498a-8f3a-1a3f0b9358f&title=&width=214)![image.png](https://img-blog.csdnimg.cn/img_convert/7aef6273e9621e470e2a209a2bd82fed.png#clientId=u4050800a-b296-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=154&id=uff92c1bf&margin=[object Object]&name=image.png&originHeight=154&originWidth=229&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2052&status=done&style=none&taskId=uf0ab5fb2-9496-47e1-ad04-bd7ca683462&title=&width=229)

outerHTML:与innerHTML类似,不同点是outerHTML会连自己一起展示出来,改变的时候自己也会改变insertAdjacentHTML() 和 insertAdjacentText() 两个参数:要插入的标记位置和要插入的HTML或文本第一个参数的选择: beforebeginafterbeginbeforeendafterend 内存与性能问题: 如果被移除的子树元素中之前有关联的事件处理程序或其他JavaScript对象(作为元素属性),那它们之间的绑定关系会滞留在内存中因此,最好手动删除要被替换的元素上关联的事件处理程序和JavaScript对象在循环使用innerHtml或者outerHTML的时候,最好不要在循环中替换元素,而是循环创建好要使用的HTML,再一次性插入到文本中,像这样: const ul = document.querySelector(`ul`); const values = [1, 2, 3, 4, 5]; let itemsHtml = ``; for (const value of values) { itemsHtml += `${value}`; } ul.innerHTML = itemsHtml; scrollIntoView() 存在于所有HTML元素上可以滚动浏览器窗口或容器元素以便包含元素进入视口参数如下: alignToTop:布尔值。true表示窗口滚动后元素的顶部与视口顶部对齐,false表示窗口滚动后元素的底部与视口底部对齐scrollIntoViewOptions是一个选项对象 behavior:定义过渡动画,可取值smooth、autoblock:定义垂直方向的对齐,可取值start、center、end、nearestinline:定义水平方向的对齐,可取值start、center、end、nearest 这个方法可用于在页面上发生某个事件时引起用户的关注 专有扩展 children:只包含Element类型的子节点 DOM2和DOM3 Node的变化 isSameNode()和isEqualNode() isSameNode()是相同节点;isEqualNode是相等节点相同是引用同一个对象,相等是类型、属性,子树等全部相等 setUserData()用于给节点追加数据 三个参数,键、值、处理函数处理函数会在包含数据的节点被复制、删除、重命名或导入其他文档的时候执行,可以在这个时候呀决定如何处理用户数据处理函数接收五个数据: 表示操作类型的数值(1:复制,2:导入,3:删除)数据的键数据的值源节点目标节点 元素尺寸 偏移尺寸:包含边框

![偏移尺寸.png](https://img-blog.csdnimg.cn/img_convert/f2b9f188f52f2bf7ed0d8984fad686ef.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u08f50add&margin=[object Object]&name=偏移尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=262045&status=done&style=none&taskId=u65fe5e7d-643d-4565-8c3d-10b047aefc5&title=)

客户端尺寸:不包含边框

![客户端尺寸.png](https://img-blog.csdnimg.cn/img_convert/0066b2da19d68f1ccba07924ca3e1d94.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=ue9ef6819&margin=[object Object]&name=客户端尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=414750&status=done&style=none&taskId=u7789e14f-f37b-45b2-af37-c353648ae29&title=)

滚动尺寸:不包含边框

![滚动尺寸.png](https://img-blog.csdnimg.cn/img_convert/46792ea4165572bbc12fd2342e5508bb.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=uf47727b5&margin=[object Object]&name=滚动尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=427437&status=done&style=none&taskId=u0064e2db-0a3c-406c-a862-37797d81ea3&title=)

元素尺寸:

![元素尺寸.png](https://img-blog.csdnimg.cn/img_convert/0cc55dd3b61f86e75166b689446fb553.png#clientId=u84139f95-f897-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=udf91eaa7&margin=[object Object]&name=元素尺寸.png&originHeight=2480&originWidth=3508&originalType=binary&ratio=1&rotation=0&showTitle=false&size=189138&status=done&style=none&taskId=ubac1ba90-d709-47f9-971d-aa2344fb592&title=)

遍历 NodeIterator 从某个起点开始执行对DOM结构的深度优先遍历(从左到右,从上到下)四个参数: root:作为遍历根节点的节点whatToShow:数值代码,表示应该访问哪些节点filter:NodeFilter对象或函数,表示应该是否接收或跳过特定节点entityReferenceExpansion:布尔值 NodeIterator方法: nextNode()previousNode() Hello world! List item 1 List item 2 List item 3 const div = document.querySelector(`div`); // 设置过滤器 仅过滤li标签1 const filter = function(node) { return node.tagName.toLowerCase() == `li` ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; }; // 创建实例 指定数值代码为SHOW_ELEMENT 过滤器filter const iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false); let node = iterator.nextNode(); while (node != null) { // 打印标签名 console.log(node.tagName); node = iterator.nextNode(); } TreeWalker TreeWalker方法: nextNode()previousNode()parentNode()firstNode()lastNode()nextSibling()previousSibling() 参数与NodeIterator一样TreeWalker可以在DOM数中任意游走 范围 使用范围可以非常细腻地对DOM树进行操作范围的主要价值在于它可以在不破坏文档结构的情况下添加、修改、删除内容范围可用于设置高亮 helloworld! let p1 = document.getElementById(`p1`), helloNode = p1.firstChild.firstChild, worldNode = p1.lastChild, // 实例化一个范围 range = document.createRange(); // 使range包含helloNode range.selectNode(helloNode); const span = document.createElement(`span`); span.style.backgroundColor = `yellow`; // 提取出范围的内容 // 在原始文档中范围之前所在的位置插入给定的节点 // 将范围对应文档片段的内容添加给定节点 range.surroundContents(span); console.log(p1.innerHTML); // helloworld!

![image.png](https://img-blog.csdnimg.cn/img_convert/cfe71562fa2670e79429af8b14166d57.png#clientId=u2ff13313-ac9a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=36&id=u6c66b62f&margin=[object Object]&name=image.png&originHeight=36&originWidth=110&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1059&status=done&style=none&taskId=u1c4d4784-7ff5-4c32-b891-56d84c6f49e&title=&width=110)

事件 JavaScript和HTML的交互是通过事件实现的。事件代表文档或浏览器窗口中某个有意义的时刻,可以使用仅在事件发生时执行的监听器(处理程序)订阅事件 事件流 当你点击一个按钮时,你不仅点击了这个按钮,还点击了它的容器以及整个页面事件流描述了页面接收事件的顺序事件冒泡: 事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)现代浏览器中的事件会一直冒泡到window对象 事件捕获: 最不具体的节点最先收到事件,最具体的节点在最后收到事件所有的浏览器都是从window对象开始捕获事件事件捕获是为了在事件到达最终目标前拦截事件 DOM事件流: 事件流的三个阶段:事件捕获、到达目标、事件冒泡事件捕获最先发生,为提前拦截事件提供了可能最后一个阶段是冒泡,最迟要在这个阶段响应事件现代浏览器会在捕获阶段在目标事件上触发事件,最终结果是事件目标上有两个机会来处理事件 事件处理程序 为响应事件而调用的函数被称为事件处理函数事件处理函数的名称以on开头 DOM2 DOM0和DOM2事件处理程序都是在冒泡阶段调用事件处理程序DOM2中对同一个目标多个事件处理程序以添加顺序来触发addEventListener()removeEventListener()注意:想要移除事件,必须知道事件处理程序的名字(即函数名),因此,使用匿名函数注册的事件处理程序是无法移除的 事件对象 在DOM中发生事件时,所有相关信息会被收集并存储在一个名为event的对象中event对象是传给事件处理函数的唯一参数bubblescancelablecurrentTargetdefaultPreventeddataileventPhasepreventDefault()stopImmediatePropagation()stopPropagation()targettrustedtypeView click const btn = document.querySelector(`button`); btn.onclick = function() { console.log(window.event); }; btn.addEventListener(`click`, (event) => { console.log(event.preventDefault); console.log(event.stopImmediatePropagation); console.log(event.stopPropagation); }); 事件类型 用户界面事件 loadunload 焦点事件 focusblur 鼠标和滚轮事件 clickdblclick:双击主键mousedownmouseupmouseenter:鼠标光标从元素外部转移到元素内部mouseleavemousemovemouseout:鼠标光标从元素外部转移到元素内部时触发mouseover:鼠标光标从元素内部转移到元素外部 客户端坐标 clientYclientX 页面坐标 pageXpageY 屏幕坐标 screenXscreenY 修饰键 DOM规定了4个属性来表示这几个修饰键的状态:shiftKey、ctrlKey、altKey、metaKey在各自的修饰键按下时包含布尔值true 相关元素 event对象的realtedTarget属性提供相关元素的信息这个属性只有在mouseover和mouseout事件发生时才包含值 额外事件信息 event对象的detail属性,用以给出关于事件的更多信息 键盘与键盘输入事件 keydown:按下键盘上的某个键textInput:按下键盘上的某个键并产生字符时触发keyup,释放键盘上的某个键时触发 键码 event的keyCode属性中的一个键码,对应键盘上特定的一个键 HTML5事件 contextmenu事件:用于表示何时该显示上下文菜单,从而允许开发者取消默认的上下文菜单并提供自定义菜单beforeunload事件:会在window上触发,用意是给开发者提供阻止页面被卸载的机会 #myMenu { position: absolute; visibility: hidden; border: 1px solid rgba(0, 0, 0, .3); } #myMenu li { list-style: none; } right click or ctrl+click me to get a custom context menu. 百度 b站 掘金 mdn window.addEventListener(`load`, (event) => { const div = document.querySelector(`#myDiv`); // 给指定元素注册contextmenu事件 这是本来的菜单事件 div.addEventListener(`contextmenu`, (event) => { // 取消默认的菜单事件冒泡 event.preventDefault(); // 显示自定义的菜单 const menu = document.querySelector(`#myMenu`); // 使菜单跟随鼠标 menu.style.left = `${event.clientX}px`; menu.style.top = `${event.clientY}px`; menu.style.visibility = `visible`; }); // 给文档注册点击事件 使左键单击时自定义菜单消失 document.addEventListener(`click`, (event) => { document.querySelector(`#myMenu`).style.visibility = `hidden`; }); }); 内存与性能 页面中事件处理程序的数量与页面的整体性能直接相关。每个函数都是对象,都占用内存空间,对象越多,性能越差为指定事件处理程序所需访问DOM的次数会先期造成整个页面交互的延迟 事件委托 使用一个事件处理程序来管理一种类型的事件使用事件委托,只要给所有元素的共同祖先节点添加一个事件处理程序,就可以解决问题优点如下: document对象随时可用节省花在设置页面处理程序上的时间减少页面所需的内存 最适合事件委托的事件包括:click、mousedown、mouseup、keydown、keypress 删除事件处理程序 如果知道某个元素会被删除,那么最好在删除它之前手动删除它的事件处理程序

原文链接:https://blog.csdn.net/hello_helloworld/article/details/126633743

2022年最受欢迎的8大编程语言

为什么都说程序员找不到女朋友,但是身边程序猿的却没一个单身的

30岁以上你还死磕技术,别说拿高薪,可能你连饭碗都会保不住

程序员被开除,老板:“有你参与的项目全黄了!”

笑话:一个测试工程师走进一家酒吧

趣图:程序员头疼的4种原因

笑话:面试官:请拿出一段体现你水平的代码。我: sudo rm -rf /*面试官:这体现了你哪方面能力?

网友说:做开发,不被领导喜欢怎么办?

前端精选:在 WPF 中实现融合效果

网友说:我奉劝各位,一定不能在职场透露自己的家庭条件



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3