当前位置: 首页 > news >正文

有没有便宜做网站的 我要做个浏览不良网页的危害

有没有便宜做网站的 我要做个,浏览不良网页的危害,dede网站mip,百度餐饮网站建设一、ES6 新特性#xff08;2015#xff09; 1. let和const 在ES6中#xff0c;新增了let和const关键字#xff0c;其中 let 主要用来声明变量#xff0c;而 const 通常用来声明常量。let、const相对于var关键字有以下特点#xff1a; 特性varletconst变量提升✔️全局…         一、ES6 新特性2015 1. let和const 在ES6中新增了let和const关键字其中 let 主要用来声明变量而 const 通常用来声明常量。let、const相对于var关键字有以下特点 特性varletconst变量提升✔️××全局变量✔️××重复声明✔️××重新赋值✔️✔️×暂时性死区×✔️✔️块作用域×✔️✔️只声明不初始化✔️✔️× 这里主要介绍其中的四点 1重新赋值 const 关键字声明的变量是“不可修改”的。其实const 保证的并不是变量的值不能改动而是变量指向的那个内存地址不能改动。对于基本类型的数据数值、字符串、布尔值其值就保存在变量指向的那个内存地址因此等同于常量。但对于引用类型的数据主要是对象和数组变量指向数据的内存地址保存的只是一个指针const只能保证这个指针是不变的至于它指向的数据结构就不可控制了。 2块级作用域 在引入let和const之前是不存在块级作用域的说法的这也就导致了很多问题比如内层变量会覆盖外层的同名变量 var a  1; if (true) {var a  2; }console.log(a); // 输出结果2循环变量会泄漏为全局变量 var arr  [1, 2, 3]; for (var i  0; i  arr.length; i) {console.log(arr[i]);  // 输出结果1 2 3 }console.log(i); // 输出结果3而通过let和const定义的变量存在块级作用域就不会产生上述问题 let a  1; if (true) {let a  2; }console.log(a); // 输出结果1const arr  [1, 2, 3]; for (let i  0; i  arr.length; i) {console.log(arr[i]);  // 输出结果1 2 3 }console.log(i); // Uncaught ReferenceError: i is not defined3变量提升 我们知道在ES6之前是存在变量提升的所谓的变量提升就是变量可以在声明之前使用 console.log(a); // 输出结果undefined var a  1;变量提升的本质是JavaScript引擎在执行代码之前会对代码进行编译分析这个阶段会将检测到的变量和函数声明添加到 JavaScript 引擎中名为 Lexical Environment 的内存中并赋予一个初始化值 undefined。然后再进入代码执行阶段。所以在代码执行之前JS 引擎就已经知道声明的变量和函数。 这种现象就不太符合我们的直觉所以在ES6中let和const关键字限制了变量提升let 定义的变量添加到 Lexical Environment 后不再进行初始化为 undefined 操作JS 引擎只会在执行到词法声明和赋值时才进行初始化。而在变量创建到真正初始化之间的时间跨度内它们无法访问或使用ES6 将其称之为暂时性死区 // 暂时性死区 开始 a  hello;     //  Uncaught ReferenceError: Cannot access a before initializationlet a;    //  暂时性死区 结束 console.log(a);  // undefined4重复声明 在ES6之前var关键字声明的变量对于一个作用域内变量的重复声明是没有限制的甚至可以声明与参数同名变量以下两个函数都不会报错 function funcA() {var a  1;var a  2; }function funcB(args) {var args  1;  }而let修复了这种不严谨的设计 function funcA() {let a  1;let a  2;  // Uncaught SyntaxError: Identifier a has already been declared }function funcB(args) {let args  1;  // Uncaught SyntaxError: Identifier args has already been declared }现在我们项目中已经完全放弃了var而使用let来定义变量使用const来定义常量。在ESlint开启了如下规则 no-var: 0;2. 解构赋值 ES6中还引入了解构赋值的概念解构赋值遵循“模式匹配”即只要等号两边的模式相等左边的变量就会被赋予对应的值。不同类型数据的解构方式不同下面就分别来看看不同类型数据的解构方式。 平时在开发中我主要会用到对象的解构赋值比如在React中解构porps值等使用解构赋值来获取父组件传来的值在React Hooks中的useState使用到了数组的解构赋值 1数组解构 具有 Iterator 接口的数据结构都可以采用数组形式的解构赋值。 const [foo, [[bar], baz]]  [1, [[2], 3]]; console.log(foo, bar, baz) // 输出结果1 2 3这里ES6实现了对数组的结构并依次赋值变量foo、bar、baz。数组的解构赋值按照位置将值与变量对应。 数组还可以实现不完全解构只解构部分内容 const [x, y]  [1, 2, 3];   // 提取前两个值 const [, y, z]  [1, 2, 3]  // 提取后两个值 const [x, , z]  [1, 2, 3]  // 提取第一三个值如果解构时对应的位置没有值就会将变量赋值为undefined const [x, y, z]  [1, 2];  console.log(z  // 输出结果undefined数组解构赋值可以使用rest操作符来捕获剩余项 const [x, ...y]  [1, 2, 3];    console.log(x);  // 输出结果1 console.log(y);  // 输出结果[2, 3]在解构时还支持使用默认值当对应的值为undefined时才会使用默认值 const [x, y, z  3]  [1, 2];  console.log(z  // 输出结果32对象解构 对象的解构赋值的本质其实是先找到同名的属性在赋值给对应的变量 let { foo, bar }  { foo: aaa, bar: bbb }; console.log(foo, bar); // 输出结果aaa bbb需要注意的是在JavaScript中对象的属性是没有顺序的。所以在解构赋值时变量必须与属性同名才能去取到值。 对象的解构赋值也是支持默认值的当定义的变量在对象中不存在时其默认值才会生效 let { foo, bar, baz  ccc}  { foo: aaa, bar: bbb, baz: null }; console.log(foo, bar, baz); // 输出结果aaa bbb nulllet { foo, bar, baz  ccc}  { foo: aaa, bar: bbb }; console.log(foo, bar, baz); // 输出结果aaa bbb ccc可以看到只有定义的变量是严格的undefined时它的默认值才会生效。 除此之外我们还需要注意不能给已声明的变量进行赋值因为当缺少 let、const、var 关键词时将会把 {baz} 理解为代码块从而导致语法错误所以下面代码会报错 let baz; { baz }  { foo: aaa, bar: bbb, baz: ccc };可以使用括号包裹整个解构赋值语句来解决上述问题 let baz; ({ baz }  { foo: aaa, bar: bbb, baz: ccc }); console.log(baz)在对象的解构赋值中可以将现有对象的方法赋值给某个变量比如 let { log, sin, cos }  Math; log(12)  // 输出结果2.4849066497880004 sin(1)   // 输出结果0.8414709848078965 cos(1)   // 输出结果0.54030230586813983其他解构赋值 剩下的几种解构赋值目前我在项目中应用的较少来简单看一下。 字符串解构 字符串解构规则只要等号右边的值不是对象或数组就先将其转为类数组对象在进行解构 const [a, b, c, d, e]  hello; console.log(a, b, c, d, e)  // 输出结果h e l l o类数组对象有 length 属性因此可以给这个属性进行解构赋值 let {length}  hello;    // 输出结果5由于字符串都是一个常量所以我们通常是知道它的值是什么的所以很少会使用变量的解构赋值。 数值和布尔值解构赋值 对数值和布尔值进行解构时它们将会先被转为对象然后再应用解构语法 let {toString: s}  123; s  Number.prototype.toString // 输出结果truelet {toString: s}  true; s  Boolean.prototype.toString // 输出结果true 注意null和undefined不能转换为对象所以如果右边是这两个值就会报错。 函数参数解构赋值 函数参数表面上是一个数组在传入参数的那一刻就会被解构为x和y。 function add([x, y]){return x  y; } add([1, 2]);   // 3除此之外我们还可以解构函数的返回值 function example() {return [1, 2, 3]; } let [a, b, c]  example();3. 模板字符串 传统的JavaScript语言中输出模板经常使用的是字符串拼接的形式这样写相当繁琐在ES6中引入了模板字符串的概念来解决以上问题。 模板字符串是增强版的字符串用反引号来标识他可以用来定义单行字符串也可以定义多行字符串或者在字符串中嵌入变量。 // 字符串中嵌入变量 let name  Bob, time  today; Hello ${name}, how are you ${time}?// 字符串中调用函数${fn()} 在平时的开发中除了上面代码中的应用很多地方会用到模板字符串比如拼接一个DOM串在Emotion/styled中定义DOM结构等都会用到模板字符串。不过在模板字符串中定义DOM元素就不会有代码提示了。 在使用模板字符串时需要注意以下几点 如果在字符串中使用反引号需要使用\来转义 如果在多行字符串中有空格和缩进那么它们都会被保留在输出中 模板字符串中嵌入变量需要将变量名写在${}之中 模板字符串中可以放任意的表达式也可以进行运算以及引用对象的属性甚至可以调用函数 如果模板字符中的变量没有声明会报错。 4. 函数默认参数 在ES6之前函数是不支持默认参数的ES6实现了对此的支持并且只有不传入参数时才会触发默认值 function getPoint(x  0, y  0) {console.log(x, y); }getPoint(1, 2);   // 1  2 getPoint()        // 0  0  getPoint(1)       // 1  0当使用函数默认值时需要注意以下几点 1函数length属性值 函数length属性通常用来表示函数参数的个数当引入函数默认值之后length表示的就是第一个有默认值参数之前的普通参数个数 const funcA  function(x, y) {}; console.log(funcA.length);  // 输出结果2 const funcB  function(x, y  1) {}; console.log(funcB.length);  // 输出结果1const funcC  function(x  1, y) {}; console.log(funcC.length);  // 输出结果 0 2参数作用域 当给函数的参数设置了默认值之后参数在被初始化时将形成一个独立作用域初始化完成后作用域消解 let x  1;function func(x, y  x) {console.log(y); }func(2);  这里最终会打印出2。在函数调用时参数 x, y 将形成一个独立的作用域所以参数中的y会等于第一个参数中的x而不是上面定义的1。 5. 箭头函数 ES6中引入了箭头函数用来简化函数的定义 const counter  (x, y)  x  y;相对于普通函数箭头函数有以下特点 1更加简洁 如果没有参数就直接写一个空括号即可 如果只有一个参数可以省去参数的括号 如果有多个参数用逗号分割 如果函数体的返回值只有一句可以省略大括号 // 1. 不传入参数 const funcA  ()  console.log(funcA); // 等价于 const funcA  function() {console.log(funcA); } // 2. 传入参数 const funcB  (x, y)  x  y; // 等价于 const funcB  function(x, y) {return x  y; } // 3. 单个参数的简化 const funcC  (x)  x; // 对于单个参数可以去掉 ()简化为 const funcC  x  x; // 等价于 const funcC  function(x) {return x; }// 4. 上述代码函数体只有单条语句如果有多条需要使用 {} const funcD  (x, y)  { console.log(x, y); return x  y; } // 等价于 const funcD  function(x, y) {console.log(x, y);return x  y; }2不绑定 this 箭头函数不会创建自己的this 所以它没有自己的this它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了之后不会改变。 var id  GLOBAL; var obj  {id: OBJ,a: function(){console.log(this.id);},b: ()  {console.log(this.id);} }; obj.a();    // OBJ obj.b();    // GLOBAL new obj.a()  // undefined new obj.b()  // Uncaught TypeError: obj.b is not a constructor对象obj的方法b是使用箭头函数定义的这个函数中的this就永远指向它定义时所处的全局执行环境中的this即便这个函数是作为对象obj的方法调用this依旧指向Window对象。需要注意定义对象的大括号{}是无法形成一个单独的执行环境的它依旧是处于全局执行环境中。 同样使用call()、apply()、bind()等方法也不能改变箭头函数中this的指向 var id  Global; let fun1  ()  {console.log(this.id) }; fun1();                     // Global fun1.call({id: Obj});     // Global fun1.apply({id: Obj});    // Global fun1.bind({id: Obj})();   // Global3不可作为构造函数 构造函数 new 操作符的执行步骤如下 创建一个对象 将构造函数的作用域赋给新对象也就是将对象的__proto__属性指向构造函数的prototype属性 指向构造函数中的代码构造函数中的this指向该对象也就是为这个对象添加属性和方法 返回新的对象 实际上第二步就是将函数中的this指向该对象。但是由于箭头函数时没有自己的this的且this指向外层的执行环境且不能改变指向所以不能当做构造函数使用。 4不绑定 arguments 箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。 6. 扩展运算符 扩展运算符...  就像是rest参数的逆运算将一个数组转为用逗号分隔的参数序列对数组进行解包。 spread 扩展运算符有以下用途 1将数组转化为用逗号分隔的参数序列 function  test(a,b,c){console.log(a); // 1console.log(b); // 2console.log(c); // 3 }var arr  [1, 2, 3]; test(...arr); 2将一个数组拼接到另一个数组 var arr1  [1, 2, 3,4]; var arr2  [...arr1, 4, 5, 6]; console.log(arr2);  // [1, 2, 3, 4, 4, 5, 6] 3将字符串转为逗号分隔的数组 var strJavaScript; var arr [...str]; console.log(arr); // [J, a, v, a, S, c, r, i, p, t]7. Symbol ES6中引入了一个新的基本数据类型Symbol表示独一无二的值。它是一种类似于字符串的数据类型它的特点如下 Symbol的值是唯一的用来解决命名冲突的问题 Symbol值不能与其他类型数据进行运算 Symbol定义的对象属性不能使用for...in遍历循环但是可以使用Reflect.ownKeys 来获取对象的所有键名 let s1  Symbol(); console.log(typeof s1); // symbollet s2  Symbol(hello); let s3  Symbol(hello); console.log(s2  s3); // false基于以上特性Symbol 属性类型比较适合用于两类场景中常量值和对象属性。 1避免常量值重复 getValue 函数会根据传入字符串参数 key 执行对应代码逻辑 function getValue(key) {switch(key){case A:...case B:...} } getValue(B);这段代码对调用者而言非常不友好因为代码中使用了魔术字符串Magic string指的是在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值导致调用 getValue 函数时需要查看函数代码才能找到参数 key 的可选值。所以可以将参数 key 的值以常量的方式声明 const KEY  {alibaba: A,baidu: B, } function getValue(key) {switch(key){case KEY.alibaba:...case KEY.baidu:...} } getValue(KEY.baidu);但这样也并非完美假设现在要在 KEY 常量中加入一个 key根据对应的规则很有可能会出现值重复的情况 const KEY  {alibaba: A,baidu: B,tencent: B }这就会出现问题 getValue(KEY.baidu) // 等同于 getValue(KEY.tencent)所以在这种场景下更适合使用 Symbol不需要关心值本身只关心值的唯一性 const KEY  {alibaba: Symbol(),baidu: Symbol(),tencent: Symbol() }2避免对象属性覆盖 函数 fn 需要对传入的对象参数添加一个临时属性 user但可能该对象参数中已经有这个属性了如果直接赋值就会覆盖之前的值。此时就可以使用 Symbol 来避免这个问题。创建一个 Symbol 数据类型的变量然后将该变量作为对象参数的属性进行赋值和读取这样就能避免覆盖的情况 function fn(o) { // {user: {id: xx, name: yy}}const s  Symbol()o[s]  zzz }8. 集合 Set ES6提供了新的数据结构Set集合。它类似于数组但是成员的值都是唯一的集合实现了iterator接口所以可以使用扩展运算符和 for…of 进行遍历。 Set的属性和方法 属性和方法概述size返回集合的元素个数add增加一个新的元素返回当前的集合delete删除元素返回布尔值has检查集合中是否包含某元素返回布尔值clear清空集合返回undefined //创建一个空集合 let s  new Set(); //创建一个非空集合 let s1  new Set([1,2,3,1,2,3]); //返回集合的元素个数 console.log(s1.size);       // 3 //添加新元素 console.log(s1.add(4));     // {1,2,3,4} //删除元素 console.log(s1.delete(1));  //true //检测是否存在某个值 console.log(s1.has(2));     // true //清空集合 console.log(s1.clear());    //undefined由于集合中元素的唯一性所以在实际应用中可以使用set来实现数组去重 let arr  [1,2,3,2,1] Array.from(new Set(arr))  // {1, 2, 3}这里使用了Array.form()方法来将数组集合转化为数组。 可以通过set来求两个数组的交集和并集 // 模拟求交集  let intersection  new Set([...set1].filter(x  set2.has(x)));// 模拟求差集 let difference  new Set([...set1].filter(x  !set2.has(x))); 用以下方法可以进行数组与集合的相互转化 // Set集合转化为数组 const arr  [...mySet] const arr  Array.from(mySet)// 数组转化为Set集合 const mySet  new Set(arr)9. Map ES6提供了Map数据结构它类似于对象也是键值队的集合但是它的键值的范围不限于字符串可以是任何类型包括对象的值也就是说 Object 结构提供了“ 字符串—值” 的对应 Map 结构提供了“ 值—值” 的对应 是一种更完善的 Hash 结构实现。如果需要“ 键值对” 的数据结构 Map 比 Object 更合适。Map也实现了iterator接口所以可以使用扩展运算符和 for…of 进行遍历。 Map的属性和方法 属性和方法概述size返回Map的元素个数set增加一个新的元素返回当前的Mapget返回键名对象的键值has检查Map中是否包含某元素返回布尔值clear清空Map返回undefined //创建一个空 map let m  new Map(); //创建一个非空 map let m2  new Map([[name, hello], ]); //获取映射元素的个数 console.log(m2.size);          // 1 //添加映射值 console.log(m2.set(age, 6)); // {name  hello, age  6} //获取映射值 console.log(m2.get(age));    // 6 //检测是否有该映射 console.log(m2.has(age));    // true //清除 console.log(m2.clear());       // undefined需要注意 只有对同一个对象的引用 Map 结构才将其视为同一个键 let map  new Map();  map.set([a], 555);  map.get([a]) // undefined上面代码的set和get方法 表面是针对同一个键 但实际上这是两个值 内存地址是不一样的 因此get方法无法读取该键 所以会返回undefined。 由上可知 Map 的键实际上是跟内存地址绑定的 只要内存地址不一样 就视为两个键。这就解决了同名属性碰撞 clash 的问题在扩展库时 如果使用对象作为键名 就不用担心自己的属性与原来的属性同名。 如果 Map 的键是一个简单类型的值 数字、 字符串、 布尔值 则只要两个值严格相等 Map 将其视为一个键 包括0和 - 0。另外 虽然NaN不严格相等于自身 但 Map 将其视为同一个键。 let map  new Map();  map.set(NaN, 123);  map.get(NaN) // 123  map.set(-0, 123);  map.get(0) // 123 10. 模块化 ES6中首次引入模块化开发规范ES Module让Javascript首次支持原生模块化开发。ES Module把一个文件当作一个模块每个模块有自己的独立作用域那如何把每个模块联系起来呢核心点就是模块的导入与导出。 1export 导出模块 正常导出 // 方式一 export var first  test; export function func() {return true; }// 方式二 var first  test; var second  test; function func() {return true; } export {first, second, func};as关键字: var first  test; export {first as second};as关键字可以重命名暴露出的变量或方法经过重命名后同一变量可以多次暴露出去。 export default export default会导出默认输出即用户不需要知道模块中输出的名字在导入的时候为其指定任意名字。 // 导出 export default function () {console.log(foo); } // 导入 import customName from ./export-default;注意 导入默认模块时不需要大括号导出默认的变量或方法可以有名字但是对外无效。export default只能使用一次。 2import 导入模块 正常导入 import {firstName, lastName, year} from ./profile; 复制代码导入模块位置可以是相对路径也可以是绝对路径.js可以省略如果不带路径只是模块名则需要通过配置文件告诉引擎查找的位置。 as关键字 import { lastName as surname } from ./profile;import 命令会被提升到模块头部所以写的位置不是那么重要但是不能使用表达式和变量来进行导入。 加载整个模块无输出 import lodash; //仅仅是加载而已无法使用加载整个模块有输出 import * as circle from ./circle; console.log(圆面积  circle.area(4)); console.log(圆周长  circle.circumference(14));注意 import * 会忽略default输出 3导入导出复合用法 先导入后导出 export { foo, bar } from my_module; // 等同于 import { foo, bar } from my_module; export { foo, boo};整体先导入再输出以及default // 整体输出 export * from my_module; // 导出default正如前面所说export default 其实导出的是default变量 export { default } from foo; // 具名接口改default export { es6 as default } from ./someModule;4模块的继承 export * from circle; export var e  2.71828182846; export default function(x) {return Math.exp(x); }注意 export * 会忽略default。 11. 字符串方法 1includes() includes()该方法用于判断字符串是否包含指定的子字符串。如果找到匹配的字符串则返回 true否则返回 false。该方法的语法如下 string.includes(searchvalue, start)该方法有两个参数 searchvalue必需要查找的字符串 start可选设置从那个位置开始查找默认为 0。 let str  Hello world!;str.includes(o)  // 输出结果true str.includes(z)  // 输出结果false str.includes(e, 2)  // 输出结果false2startsWith() startsWith()该方法用于检测字符串是否以指定的子字符串开始。如果是以指定的子字符串开头返回 true否则 false。其语法和上面的includes()方法一样。 let str  Hello world!;str.startsWith(Hello) // 输出结果true str.startsWith(Helle) // 输出结果false str.startsWith(wo, 6) // 输出结果true3endsWith() endsWith()该方法用来判断当前字符串是否是以指定的子字符串结尾。如果传入的子字符串在搜索字符串的末尾则返回 true否则将返回 false。其语法如下 string.endsWith(searchvalue, length)该方法有两个参数 searchvalue必需要搜索的子字符串 length设置字符串的长度默认值为原始字符串长度 string.length。 let str  Hello world!;str.endsWith(!)       // 输出结果true str.endsWith(llo)     // 输出结果false str.endsWith(llo, 5)  // 输出结果true可以看到当第二个参数设置为5时就会从字符串的前5个字符中进行检索所以会返回true。 4repeat() repeat() 方法返回一个新字符串表示将原字符串重复n次 x.repeat(3)     // 输出结果xxx hello.repeat(2) // 输出结果hellohello na.repeat(0)    // 输出结果如果参数是小数会向下取整 na.repeat(2.9) // 输出结果nana如果参数是负数或者Infinity会报错 na.repeat(Infinity)   // RangeError na.repeat(-1)         // RangeError如果参数是 0 到-1 之间的小数则等同于 0这是因为会先进行取整运算。0 到-1 之间的小数取整以后等于-0repeat视同为 0。 na.repeat(-0.9)   // 输出结果如果参数是NaN就等同于 0 na.repeat(NaN)    // 输出结果如果repeat的参数是字符串则会先转换成数字。 na.repeat(na)   // 输出结果 na.repeat(3)    // 输出结果nanana12. 数组方法 1reduce() reduce() 方法对数组中的每个元素执行一个reducer函数(升序执行)将其结果汇总为单个返回值。其使用语法如下 arr.reduce(callback,[initialValue])reduce 为数组中的每一个元素依次执行回调函数不包括数组中被删除或从未被赋值的元素接受四个参数初始值或者上一次回调函数的返回值当前元素值当前索引调用 reduce 的数组。 (1) callback 执行数组中每个值的函数包含四个参数 previousValue 上一次调用回调返回的值或者是提供的初始值initialValue currentValue 数组中当前被处理的元素 index 当前元素在数组中的索引 array 调用 reduce 的数组 (2) initialValue 作为第一次调用 callback 的第一个参数。 let arr  [1, 2, 3, 4] let sum  arr.reduce((prev, cur, index, arr)  {console.log(prev, cur, index);return prev  cur; }) console.log(arr, sum);输出结果如下 1 2 1 3 3 2 6 4 3 [1, 2, 3, 4] 10再来加一个初始值看看 let arr  [1, 2, 3, 4] let sum  arr.reduce((prev, cur, index, arr)  {console.log(prev, cur, index);return prev  cur; }, 5) console.log(arr, sum);输出结果如下 5 1 0 6 2 1 8 3 2 11 4 3 [1, 2, 3, 4] 15通过上面例子可以得出结论如果没有提供initialValuereduce 会从索引1的地方开始执行 callback 方法跳过第一个索引。如果提供initialValue从索引0开始。 注意该方法如果添加初始值就会改变原数组将这个初始值放在数组的最后一位。 2filter() filter()方法用于过滤数组满足条件的元素会被返回。它的参数是一个回调函数所有数组元素依次执行该函数返回结果为true的元素会被返回。该方法会返回一个新的数组不会改变原数组。 let arr  [1, 2, 3, 4, 5] arr.filter(item  item  2)  // 结果[3, 4, 5]可以使用filter()方法来移除数组中的undefined、null、NAN等值 let arr  [1, undefined, 2, null, 3, false, , 4, 0] arr.filter(Boolean) // 结果[1, 2, 3, 4]3Array.from Array.from 的设计初衷是快速基于其他对象创建新数组准确来说就是从一个类似数组的可迭代对象中创建一个新的数组实例。其实只要一个对象有迭代器Array.from 就能把它变成一个数组注意该方法会返回一个的数组不会改变原对象。 从语法上看Array.from 有 3 个参数 类似数组的对象必选 加工函数新生成的数组会经过该函数的加工再返回 this 作用域表示加工函数执行时 this 的值。 这三个参数里面第一个参数是必选的后两个参数都是可选的 var obj  {0: a, 1: b, 2:c, length: 3};Array.from(obj, function(value, index){console.log(value, index, this, arguments.length);return value.repeat(3);   //必须指定返回值否则返回 undefined }, obj);结果如图 以上结果表明通过 Array.from 这个方法可以自定义加工函数的处理方式从而返回想要得到的值如果不确定返回值则会返回 undefined最终生成的是一个包含若干个 undefined 元素的空数组。 实际上如果这里不指定 this加工函数就可以是一个箭头函数。上述代码可以简写为以下形式。 Array.from(obj, (value)  value.repeat(3)); //  控制台打印 (3) [aaa, bbb, ccc]除了上述 obj 对象以外拥有迭代器的对象还包括 String、Set、Map 等Array.from 都可以进行处理 // String Array.from(abc);                             // [a, b, c] // Set Array.from(new Set([abc, def]));           // [abc, def] // Map Array.from(new Map([[1, ab], [2, de]]));   // [[1, ab], [2, de]]1fill() 使用fill()方法可以向一个已有数组中插入全部或部分相同的值开始索引用于指定开始填充的位置它是可选的。如果不提供结束索引则一直填充到数组末尾。如果是负值则将从负值加上数组的长度而得到的值开始。该方法的语法如下 array.fill(value, start, end)其参数如下 value必需。填充的值 start可选。开始填充位置 end可选。停止填充位置 (默认为 array.length)。 使用示例如下 const arr  [0, 0, 0, 0, 0];// 用5填充整个数组 arr.fill(5); console.log(arr); // [5, 5, 5, 5, 5] arr.fill(0);      // 重置// 用5填充索引大于等于3的元素 arr.fill(5, 3); console.log(arr); // [0, 0, 0, 5, 5] arr.fill(0);      // 重置// 用5填充索引大于等于1且小于等于3的元素 arr.fill(5, 3); console.log(arr); // [0, 5, 5, 0, 0] arr.fill(0);      // 重置// 用5填充索引大于等于-1的元素 arr.fill(5, -1); console.log(arr); // [0, 0, 0, 0, 5] arr.fill(0);      // 重置二、ES7 新特性2016 1. Array.includes() includes() 方法用来判断一个数组是否包含一个指定的值如果包含则返回 true否则返回false。该方法不会改变原数组。其语法如下 arr.includes(searchElement, fromIndex)该方法有两个参数 searchElement必须需要查找的元素值。 fromIndex可选从fromIndex 索引处开始查找目标值。如果为负值则按升序从 array.length fromIndex 的索引开始搜 即使从末尾开始往前跳 fromIndex 的绝对值个索引然后往后搜寻。默认为 0。 [1, 2, 3].includes(2);  //  true [1, 2, 3].includes(4);  //  false [1, 2, 3].includes(3, 3);  // false [1, 2, 3].includes(3, -1); // true在 ES7 之前通常使用 indexOf 来判断数组中是否包含某个指定值。但 indexOf 在语义上不够明确直观同时 indexOf 内部使用 来判等所以存在对 NaN 的误判includes 则修复了这个问题 [1, 2, NaN].indexOf(NaN);   // -1 [1, 2, NaN].includes(NaN);  //  true注意使用includes()比较字符串和字符时区分大小写。 2. 指数操作符 ES7 还引入了指数操作符 用来更为方便的进行指数计算它与 Math.pow() 等效 Math.pow(2, 10));  // 1024 2**10;           // 1024三、ES8 新特性2017 1. padStart()和padEnd() padStart()和padEnd()方法用于补齐字符串的长度。如果某个字符串不够指定长度会在头部或尾部补全。 1padStart() padStart()用于头部补全。该方法有两个参数其中第一个参数是一个数字表示字符串补齐之后的长度第二个参数是用来补全的字符串。 如果原字符串的长度等于或大于指定的最小长度则返回原字符串 x.padStart(1, ab) // x如果用来补全的字符串与原字符串两者的长度之和超过了指定的最小长度则会截去超出位数的补全字符串 x.padStart(5, ab) // ababx x.padStart(4, ab) // abax如果省略第二个参数默认使用空格补全长度 x.padStart(4, ab) // a   padStart()的常见用途是为数值补全指定位数笔者最近做的一个需求就是将返回的页数补齐为三位比如第1页就显示为001就可以使用该方法来操作 1.padStart(3, 0)   // 输出结果 001 15.padStart(3, 0)  // 输出结果 0152padEnd() padEnd()用于尾部补全。该方法也是接收两个参数第一个参数是字符串补全生效的最大长度第二个参数是用来补全的字符串 x.padEnd(5, ab) // xabab x.padEnd(4, ab) // xaba2. Object.values()和Object.entries() 在ES5中就引入了Object.keys方法在ES8中引入了跟Object.keys配套的Object.values和Object.entries作为遍历一个对象的补充手段供for...of循环使用。它们都用来遍历对象它会返回一个由给定对象的自身可枚举属性不含继承的和Symbol属性组成的数组数组元素的排列顺序和正常循环遍历该对象时返回的顺序一致这个三个元素返回的值分别如下 Object.keys()返回包含对象键名的数组 Object.values()返回包含对象键值的数组 Object.entries()返回包含对象键名和键值的数组。 let obj  { id: 1, name: hello, age: 18  }; console.log(Object.keys(obj));   // 输出结果: [id, name, age] console.log(Object.values(obj)); // 输出结果: [1, hello, 18] console.log(Object.entries(obj));   // 输出结果: [[id, 1], [name, hello], [age, 18]注意 Object.keys()方法返回的数组中的值都是字符串也就是说不是字符串的key值会转化为字符串。 结果数组中的属性值都是对象本身可枚举的属性不包括继承来的属性。 3. 函数扩展 ES2017 规定函数的参数列表的结尾可以为逗号 function person( name, age, sex, ) {}该特性的主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。 2filter() filter()方法用于过滤数组满足条件的元素会被返回。它的参数是一个回调函数所有数组元素依次执行该函数返回结果为true的元素会被返回。该方法会返回一个新的数组不会改变原数组。 let arr  [1, 2, 3, 4, 5] arr.filter(item  item  2)  // 结果[3, 4, 5]可以使用filter()方法来移除数组中的undefined、null、NAN等值 let arr  [1, undefined, 2, null, 3, false, , 4, 0] arr.filter(Boolean) // 结果[1, 2, 3, 4]3Array.from Array.from 的设计初衷是快速基于其他对象创建新数组准确来说就是从一个类似数组的可迭代对象中创建一个新的数组实例。其实只要一个对象有迭代器Array.from 就能把它变成一个数组注意该方法会返回一个的数组不会改变原对象。 从语法上看Array.from 有 3 个参数 类似数组的对象必选 加工函数新生成的数组会经过该函数的加工再返回 this 作用域表示加工函数执行时 this 的值。 这三个参数里面第一个参数是必选的后两个参数都是可选的 var obj  {0: a, 1: b, 2:c, length: 3};Array.from(obj, function(value, index){console.log(value, index, this, arguments.length);return value.repeat(3);   //必须指定返回值否则返回 undefined }, obj);结果如图 以上结果表明通过 Array.from 这个方法可以自定义加工函数的处理方式从而返回想要得到的值如果不确定返回值则会返回 undefined最终生成的是一个包含若干个 undefined 元素的空数组。 实际上如果这里不指定 this加工函数就可以是一个箭头函数。上述代码可以简写为以下形式。 Array.from(obj, (value)  value.repeat(3)); //  控制台打印 (3) [aaa, bbb, ccc]除了上述 obj 对象以外拥有迭代器的对象还包括 String、Set、Map 等Array.from 都可以进行处理 // String Array.from(abc);                             // [a, b, c] // Set Array.from(new Set([abc, def]));           // [abc, def] // Map Array.from(new Map([[1, ab], [2, de]]));   // [[1, ab], [2, de]]1fill() 使用fill()方法可以向一个已有数组中插入全部或部分相同的值开始索引用于指定开始填充的位置它是可选的。如果不提供结束索引则一直填充到数组末尾。如果是负值则将从负值加上数组的长度而得到的值开始。该方法的语法如下 array.fill(value, start, end)其参数如下 value必需。填充的值 start可选。开始填充位置 end可选。停止填充位置 (默认为 array.length)。 使用示例如下 const arr  [0, 0, 0, 0, 0];// 用5填充整个数组 arr.fill(5); console.log(arr); // [5, 5, 5, 5, 5] arr.fill(0);      // 重置// 用5填充索引大于等于3的元素 arr.fill(5, 3); console.log(arr); // [0, 0, 0, 5, 5] arr.fill(0);      // 重置// 用5填充索引大于等于1且小于等于3的元素 arr.fill(5, 3); console.log(arr); // [0, 5, 5, 0, 0] arr.fill(0);      // 重置// 用5填充索引大于等于-1的元素 arr.fill(5, -1); console.log(arr); // [0, 0, 0, 0, 5] arr.fill(0);      // 重置二、ES7 新特性2016 1. Array.includes() includes() 方法用来判断一个数组是否包含一个指定的值如果包含则返回 true否则返回false。该方法不会改变原数组。其语法如下 arr.includes(searchElement, fromIndex)该方法有两个参数 searchElement必须需要查找的元素值。 fromIndex可选从fromIndex 索引处开始查找目标值。如果为负值则按升序从 array.length fromIndex 的索引开始搜 即使从末尾开始往前跳 fromIndex 的绝对值个索引然后往后搜寻。默认为 0。 [1, 2, 3].includes(2);  //  true [1, 2, 3].includes(4);  //  false [1, 2, 3].includes(3, 3);  // false [1, 2, 3].includes(3, -1); // true在 ES7 之前通常使用 indexOf 来判断数组中是否包含某个指定值。但 indexOf 在语义上不够明确直观同时 indexOf 内部使用 来判等所以存在对 NaN 的误判includes 则修复了这个问题 [1, 2, NaN].indexOf(NaN);   // -1 [1, 2, NaN].includes(NaN);  //  true注意使用includes()比较字符串和字符时区分大小写。 2. 指数操作符 ES7 还引入了指数操作符 用来更为方便的进行指数计算它与 Math.pow() 等效 Math.pow(2, 10));  // 1024 2**10;           // 1024三、ES8 新特性2017 1. padStart()和padEnd() padStart()和padEnd()方法用于补齐字符串的长度。如果某个字符串不够指定长度会在头部或尾部补全。 1padStart() padStart()用于头部补全。该方法有两个参数其中第一个参数是一个数字表示字符串补齐之后的长度第二个参数是用来补全的字符串。 如果原字符串的长度等于或大于指定的最小长度则返回原字符串 x.padStart(1, ab) // x如果用来补全的字符串与原字符串两者的长度之和超过了指定的最小长度则会截去超出位数的补全字符串 x.padStart(5, ab) // ababx x.padStart(4, ab) // abax如果省略第二个参数默认使用空格补全长度 x.padStart(4, ab) // a   padStart()的常见用途是为数值补全指定位数笔者最近做的一个需求就是将返回的页数补齐为三位比如第1页就显示为001就可以使用该方法来操作 1.padStart(3, 0)   // 输出结果 001 15.padStart(3, 0)  // 输出结果 0152padEnd() padEnd()用于尾部补全。该方法也是接收两个参数第一个参数是字符串补全生效的最大长度第二个参数是用来补全的字符串 x.padEnd(5, ab) // xabab x.padEnd(4, ab) // xaba2. Object.values()和Object.entries() 在ES5中就引入了Object.keys方法在ES8中引入了跟Object.keys配套的Object.values和Object.entries作为遍历一个对象的补充手段供for...of循环使用。它们都用来遍历对象它会返回一个由给定对象的自身可枚举属性不含继承的和Symbol属性组成的数组数组元素的排列顺序和正常循环遍历该对象时返回的顺序一致这个三个元素返回的值分别如下 Object.keys()返回包含对象键名的数组 Object.values()返回包含对象键值的数组 Object.entries()返回包含对象键名和键值的数组。 let obj  { id: 1, name: hello, age: 18  }; console.log(Object.keys(obj));   // 输出结果: [id, name, age] console.log(Object.values(obj)); // 输出结果: [1, hello, 18] console.log(Object.entries(obj));   // 输出结果: [[id, 1], [name, hello], [age, 18]注意 Object.keys()方法返回的数组中的值都是字符串也就是说不是字符串的key值会转化为字符串。 结果数组中的属性值都是对象本身可枚举的属性不包括继承来的属性。 3. 函数扩展 ES2017 规定函数的参数列表的结尾可以为逗号 function person( name, age, sex, ) {}该特性的主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。 let dog  Symbol(dog);  // dog 为描述 在 ES2019 之前获取一个 Symbol 值的描述需要通过 String 方法 或 toString 方法 String(dog);              // Symbol(dog)  dog.toString();           // Symbol(dog) ES2019 补充了属性 description用来直接访问描述 dog.description;  // dog5. toString() ES2019 对函数的 toString() 方法进行了扩展以前这个方法只会输出函数代码但会省略注释和空格。ES2019 的 toString()则会保留注释、空格等即输出的是原始代码 function sayHi() {/* dog */console.log(wangwang); }sayHi.toString();  // 将输出和上面一样的原始代码6. catch 在 ES2019 以前catch 会带有参数但是很多时候 catch 块是多余的。而现在可以不带参数 // ES2019 之前 try {... } catch(error) {... }// ES2019 之后 try {... } catch {... }六、ES11 新特性2020 1. BigInt 在 JavaScript 中数值类型 Number 是 64 位浮点数所以计算精度和表示范围都有一定限制。ES2020 新增了 BigInt 数据类型这也是 JavaScript 引入的第八种基本类型。BigInt 可以表示任意大的整数。其语法如下 BigInt(value);其中 value 是创建对象的数值。可以是字符串或者整数。 在 JavaScript 中Number 基本类型可以精确表示的最大整数是253。因此早期会有这样的问题 let max  Number.MAX_SAFE_INTEGER;    // 最大安全整数let max1  max  1 let max2  max  2max1  max2   // true有了BigInt之后这个问题就不复存在了 let max  BigInt(Number.MAX_SAFE_INTEGER);let max1  max  1n let max2  max  2nmax1  max2   // false可以通过typeof操作符来判断变量是否为BigInt类型返回字符串bigint typeof 1n  bigint; // true  typeof BigInt(1)  bigint; // true 还可以通过Object.prototype.toString方法来判断变量是否为BigInt类型返回字符串[object BigInt] Object.prototype.toString.call(10n)  [object BigInt];    // true注意BigInt 和 Number 不是严格相等的但是宽松相等 10n  10 // false  10n  10  // true Number 和 BigInt 可以进行比较 1n  2;    // true  2n  1;    // true  2  2;     // false  2n  2;    // false  2n  2;   // true2. 空值合并运算符?? 在编写代码时如果某个属性不为 null 和 undefined那么就获取该属性如果该属性为 null 或 undefined则取一个默认值 const name  dogName ? dogName : default; 可以通过 || 来简化 const name   dogName || default; 但是 || 的写法存在一定的缺陷当 dogName 为 0 或 false 的时候也会走到 default 的逻辑。所以 ES2020 引入了 ?? 运算符。只有 ?? 左边为 null 或 undefined时才返回右边的值 const dogName  false;  const name   dogName ?? default;  // name  false;3. 可选链操作符?. 在开发过程中我们经常需要获取深层次属性例如 system.user.addr.province.name。但在获取 name 这个属性前需要一步步的判断前面的属性是否存在否则并会报错 const name  (system  system.user  system.user.addr  system.user.addr.province  system.user.addr.province.name) || default;为了简化上述过程ES2020 引入了「链判断运算符」?.可选链操作符( ?. )允许读取位于连接对象链深处的属性的值而不必明确验证链中的每个引用是否有效。?. 操作符的功能类似于 . 链式操作符不同之处在于在引用为null 或 undefined 的情况下不会引起错误该表达式短路返回值是 undefined。与函数调用一起使用时如果给定的函数不存在则返回 undefined。 const name  system?.user?.addr?.province?.name || default;当尝试访问可能不存在的对象属性时可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时如果不能确定哪些属性必定存在可选链操作符也是很有帮助的。 可选链有以下三种形式 a?.[x] // 等同于 a  null ? undefined : a[x]a?.b() // 等同于 a  null ? undefined : a.b()a?.() // 等同于 a  null ? undefined : a()在使用TypeScript开发时这个操作符可以解决很多问题。 4. Promise.allSettled Promise.allSettled 的参数接受一个 Promise 的数组返回一个新的 Promise。唯一的不同在于执行完之后不会失败也就是说当 Promise.allSettled 全部处理完成后我们可以拿到每个 Promise 的状态而不管其是否处理成功。 下面使用 allSettled 实现的一段代码 const resolved  Promise.resolve(2); const rejected  Promise.reject(-1); const allSettledPromise  Promise.allSettled([resolved, rejected]); allSettledPromise.then(function (results) {console.log(results); }); // 返回结果 // [ //    { status: fulfilled, value: 2 }, //    { status: rejected, reason: -1 } // ]可以看到Promise.allSettled 最后返回的是一个数组记录传进来的参数中每个 Promise 的返回值这就是和 all 方法不太一样的地方。你也可以根据 all 方法提供的业务场景的代码进行改造其实也能知道多个请求发出去之后Promise 最后返回的是每个参数的最终状态。 5. String.matchAll() matchAll() 是新增的字符串方法它返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。因为返回的是遍历器所以通常使用for...of循环取出。 for (const match of abcabc.matchAll(/a/g)) {console.log(match) } //[a, index: 0, input: abcabc, groups: undefined] //[a, index: 3, input: abcabc, groups: undefined]需要注意该方法的第一个参数是一个正则表达式对象如果传的参数不是一个正则表达式对象则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp 。另外RegExp必须是设置了全局模式g的形式否则会抛出异常 TypeError。 七、ES12 新特性2021 1. String.replaceAll() replaceAll()方法会返回一个全新的字符串所有符合匹配规则的字符都将被替换掉替换规则可以是字符串或者正则表达式。 let string  hello world, hello ES12 string.replace(/hello/g,hi)    // hi world, hi ES12 string.replaceAll(hello,hi)  // hi world, hi ES12注意的是replaceAll 在使用正则表达式的时候如果非全局匹配/g会抛出异常 let string  hello world, hello ES12 string.replaceAll(/hello/,hi)  // Uncaught TypeError: String.prototype.replaceAll called with a non-global2. 数字分隔符 数字分隔符可以在数字之间创建可视化分隔符通过 _下划线来分割数字使数字更具可读性可以放在数字内的任何地方 const money  1_000_000_000 //等价于 const money  1000000000该新特性同样支持在八进制数中使用 const number  0o123_456 //等价于 const number  0o1234563. Promise.any Promise.any是是 ES2021 新增的特性它接收一个 Promise 可迭代对象例如数组只要其中的一个 promise 成功就返回那个已经成功的 promise 如果可迭代对象中没有一个 promise 成功即所有的 promises 都失败/拒绝就返回一个失败的 promise 和 AggregateError 类型的实例它是 Error 的一个子类用于把单一的错误集合在一起 const promises  [Promise.reject(ERROR A),Promise.reject(ERROR B),Promise.resolve(result), ]Promise.any(promises).then((value)  {console.log(value: , value) }).catch((err)  {console.log(err: , err) })// 输出结果value:  result如果所有传入的 promises 都失败 const promises  [Promise.reject(ERROR A),Promise.reject(ERROR B),Promise.reject(ERROR C), ]Promise.any(promises).then((value)  {console.log(value, value) }).catch((err)  {console.log(err, err)console.log(err.message)console.log(err.name)console.log(err.errors) })输出结果 errAggregateError: All promises were rejected All promises were rejected AggregateError [ERROR A, ERROR B, ERROR C]4. 逻辑赋值操作符 ES12中新增了几个逻辑赋值操作符可以用来简化一些表达式 // 等同于 a  a || b a || b; // 等同于 c  c  d c  d; // 等同于 e  e ?? f e ?? f;八、ES13 新特性2022 1. Object.hasOwn() 在ES2022之前可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。 Object.hasOwn 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法 const example  {property: 123 };console.log(Object.prototype.hasOwnProperty.call(example, property)); console.log(Object.hasOwn(example, property));2. at() at() 是一个数组方法用于通过给定索引来获取数组元素。当给定索引为正时这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时就会从数组的最后一项开始检索 const array  [0,1,2,3,4,5];console.log(array[array.length-1]);  // 5 console.log(array.at(-1));  // 5console.log(array[array.lenght-2]);  // 4 console.log(array.at(-2));  // 4除了数组字符串也可以使用at()方法进行索引 const str  hello world;console.log(str[str.length - 1]);  // d console.log(str.at(-1));  // d3. error.cause 在 ECMAScript 2022 规范中new Error() 中可以指定导致它的原因 function readFiles(filePaths) {return filePaths.map((filePath)  {try {// ···} catch (error) {throw new Error(While processing ${filePath},{cause: error});}}); }4. Top-level Await 在ES2017中引入了 async 函数和 await 关键字以简化 Promise 的使用但是 await 关键字只能在 async 函数内部使用。尝试在异步函数之外使用 await 就会报错SyntaxError - SyntaxError: await is only valid in async function。 顶层 await 允许我们在 async 函数外面使用 await 关键字。它允许模块充当大型异步函数通过顶层 await这些 ECMAScript 模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。 由于 await 仅在 async 函数中可用因此模块可以通过将代码包装在 async 函数中来在代码中包含 await // a.jsimport fetch  from node-fetch;let users;export const fetchUsers  async ()  {const resp  await fetch(https://jsonplaceholder.typicode.com/users);users   resp.json();}fetchUsers();export { users };// usingAwait.jsimport {users} from ./a.js;console.log(users: , users);console.log(usingAwait module);我们还可以立即调用顶层async函数IIAFE import fetch  from node-fetch;(async ()  {const resp  await fetch(https://jsonplaceholder.typicode.com/users);users  resp.json();})();export { users };这样会有一个缺点直接导入的 users 是 undefined需要在异步执行完成之后才能访问它 // usingAwait.js import {users} from ./a.js;console.log(users:, users); // undefinedsetTimeout(()  {console.log(users:, users); }, 100);console.log(usingAwait module);当然这种方法并不安全因为如果异步函数执行花费的时间超过100毫秒 它就不会起作用了users 仍然是 undefined。 另一个方法是导出一个 promise让导入模块知道数据已经准备好了 //a.js import fetch  from node-fetch; export default (async ()  {const resp  await fetch(https://jsonplaceholder.typicode.com/users);users  resp.json(); })(); export { users };//usingAwait.js import promise, {users} from ./a.js; promise.then(()  { console.log(usingAwait module);setTimeout(()  console.log(users:, users), 100);  });虽然这种方法似乎是给出了预期的结果但是有一定的局限性导入模块必须了解这种模式才能正确使用它。 而顶层await就可以解决这些问题 // a.jsconst resp  await fetch(https://jsonplaceholder.typicode.com/users);const users  resp.json();export { users};// usingAwait.jsimport {users} from ./a.mjs;console.log(users);console.log(usingAwait module);顶级 await 在以下场景中将非常有用 动态加载模块 const strings  await import(/i18n/${navigator.language});资源初始化 const connection  await dbConnector();依赖回退 let translations; try {translations  await import(https://app.fr.json); } catch {translations  await import(https://fallback.en.json); }九、ES14 新特性2023 1. 从尾到头搜索数组 概述 在 JavaScript 中通过 find() 和 findIndex()  查找数组中的值是一种常见做法。不过这些方法从数组的开始进行遍历 const array  [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];array.find(elem  elem.v  3); // {v: 4} array.findIndex(elem  elem.v  3); // 3如果要从数组的末尾开始遍历就必须反转数组并使用上述方法。这样做就需要一个额外的数组操作。findLast() 和 findLastIndex() 的就解决了这一问题。提出这两个方法的一个重要原因就是语义。 使用 它们的用法和find()、findIndex()类似唯一不同的是它们是 从后向前 遍历数组这两个方法适用于数组和类数组。 findLast() 会返回第一个查找到的元素如果没有找到就会返回 undefined findLastIndex() 会返回第一个查找到的元素的索引。如果没有找到就会返回 -1 const array  [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];array.findLast(elem  elem.v  3); // {v: 5} array.findLastIndex(elem  elem.v  3); // 4 array.findLastIndex(elem  elem.v  5); // undefined2. 通过副本更改数组 通过副本更改数组的方法有四个 Array.prototype.toReversed() Array.prototype.toSorted() Array.prototype.toSpliced() Array.prototype.with() 我们知道大多数的数组方法都是非破坏性的也就是不会改变原数组比如 filter() 方法 const arr  [a, b, b, a]; const result  arr.filter(x  x ! b); console.log(result); // [a, a]当然也有一些是破坏性的方法它们在执行时会改变原数组比如 sort() 方法 const arr  [c, a, b]; const result  arr.sort(); console.log(result); // [a, b, c]在数组的方法中下面的方法是具有破坏性的 reverse() sort() splice() 如果想要这些数组方法应用于数组而不改变它可以使用下面任意一种形式 const sorted1  arr.slice().sort(); const sorted2  [...arr].sort(); const sorted3  Array.from(arr).sort();可以看到我们首先需要创建数组的副本再对这个副本进行修改。因此就引入了这三个方法的非破坏性版本因此不需要手动创建副本再进行操作 reverse() 的非破坏性版本toReversed() sort() 非破坏性版本toSorted(compareFn) splice() 非破坏性版本toSpliced(start, deleteCount, ...items) 这些函数属性引入到了 Array.prototype Array.prototype.toReversed() - Array Array.prototype.toSorted(compareFn) - Array Array.prototype.toSpliced(start, deleteCount, ...items) - Array Array.prototype.with(index, value) - Array 除此之外还有了一个新的非破坏性方法with()。该方法会以非破坏性的方式替换给定 index 处的数组元素即 arr[index]value 的非破坏性版本。 所有这些方法都将保持目标数组不变并返回它的副本并执行更改。这些方法适用于数组也适用于类型化数组即以下类的实例 Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array BigInt64Array BigUint64Array TypedArray是一种通用的固定长度缓冲区类型允许读取缓冲区中的二进制数据。其在WEBGL规范中被引入用于解决Javascript处理二进制数据的问题。类型化数组也是数组只不过其元素被设置为特定类型的值。 类型化数组的核心就是一个名为 ArrayBuffer 的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的就是为了将来使用而分配一定数量的字节。 这些方法也适用于元组元组相当于不可变的数组。它们拥有数组的所有方法——除了破坏性的方法。因此将后者的非破坏性版本添加到数组对元组是有帮助的这意味着我们可以使用相同的方法来非破坏性地更改数组和元组。 Array.prototype.toReversed() toReversed() 是 reverse() 方法的非破坏性版本 const arr  [a, b, c]; const result  arr.toReversed(); console.log(result); // [c, b, a] console.log(arr);    // [a, b, c]Array.prototype.toSorted() toSorted() 是 sort() 方法的非破坏性版本 const arr  [c, a, b]; const result  arr.toSorted(); console.log(result);  // [a, b, c] console.log(arr);     // [c, a, b]Array.prototype.toSpliced() splice() 方法比其他几种方法都复杂其使用形式splice(start, deleteCount, ...items)。该方法会从从 start 索引处开始删除 deleteCount个元素然后在 start 索引处开始插入item 中的元素最后返回已经删除的元素。 toSpliced 是 splice() 方法的非破坏性版本它会返回更新后的数组原数组不会变化并且无法再得到已经删除的元素 const arr  [a, b, c, d]; const result  arr.toSpliced(1, 2, X); console.log(result); // [a, X, d] console.log(arr);    // [a, b, c, d]Array.prototype.with() .with()方法的使用形式.with(index, value)它是 arr[index] value 的非破坏性版本 const arr  [a, b, c]; const result  arr.with(1, X); console.log(result);  // [a, X, c] console.log(arr);     // [a, b, c]
http://www.pierceye.com/news/863614/

相关文章:

  • 音乐网站设计规划书wordpress爬取文章插件
  • 长沙哪家制作网站好医院网站建设方案需要多少钱
  • 建网站 xyz企业网站wordpress
  • 网站建站工作室做网站收录
  • 广州市天河区工程建设监督网站网站维护的注意事项
  • 人才网站建站织梦网站模块
  • 网站区分治疗早射最有效的方法是什么
  • 市体育局网站 两学一做ip详细地址查询工具
  • 网站找图片做海报侵权开发一个oa系统多少钱
  • 网站下载链接打不开外国网站建站
  • 与众不同的网站风控网站开发
  • 哈尔滨的网站设计门户网站建设招投标
  • 如何用wordpress制作网站运城公司做网站
  • 园林景观网站模板做企业商城网站
  • 医疗网站优化怎么做农村自建房设计图效果图
  • 网站建哪家好佛山seo网站排名
  • 做珠宝网站价格多少html5手机网站开发经验
  • dede网站后台合肥网站建设行情
  • 做动漫网站侵权吗途牛网网站建设评价
  • 域名到期对网站的影响做美缝在哪个网站接单
  • 网站建设技术网站刚做网站和搜出来的不一样
  • 营销型网站建设集装箱液袋如何做做网站
  • 刘晓忠 网站建设手机网站绑定域名是什么意思
  • 东莞网站建设 包装材料汅app下载大全2022
  • 湖南平台网站建设找哪家设计师培训班多少钱
  • 网站代码素材重庆渝发建设有限公司官网
  • 网站标题能改吗加强档案网站建设
  • 2016网站设计龙岩微信网站建设
  • 梅州建站规划网站建设从零到精通.pdf
  • 商业机构的网址网站关键词优化费用