ES6新特性
ES6
一、ES6相关介绍
1.1、什么是ES6
1995年的美国,有一家名为
netscape
(网景)的公司打造了一款主要用于check验证的脚本语言,而恰在此时,Sun
公司的java
语言火的一塌糊涂,netscape
公司为蹭其热度,便将该脚本语言命名为JavaScript
。不料JavaScript
居然被越来越多的人使用,后效仿大秦的货币统一政策将其提交给国际标准组织ECMA
。该组织发布的标准被称做
ECMAScript
。 2015年6月发布的版本称为ECMAScript2015
,简称ES6
。从ES6
开始,该组织每年会发布一个版本,版本号比年份最后一位大1,至今最新版本为ES12
。ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准
ES6
是一大盒语法糖,解决了以前ES5
很多难受的地方。
Babel
:Babel
是一个广泛使用的ES6
转码器,可以将ES6
代码转为ES5
代码,从而在现有环境执行。这意味着,你可以用ES6
的方式编写程序,又不用担心现有环境是否支持。
二、关键字扩展
2.1、let和块级作用域
2.1.1、ES5
没有块级作用域
在
ES5
中,JS
的作用域分为全局作用域和局部作用域。通常是用函数区分的,函数内部属于局部作用域。
ES5
只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
-
内层变量可能会覆盖外层变量。
var userName = "zhangsan"; if(true){ var userName ="lisi"; } console.log(userName);// lisi
-
用来计数的循环变量泄露为全局变量。
// for(var i=0;i<10;i++){ // // } // console.log(i);// 10
2.1.2、块级作用域
-
在
ES6
中新增了块级作用域的概念,使用{}扩起来的区域叫做块级作用域{ // 块级作用域 }
-
let关键字声明变量,实际上为 JavaScript 新增了块级作用域。
{ let userName = "zhangsan"; userName = "lisi"; console.log(userName);// lisi } console.log(userName);
-
块作用域由 { } 包裹,if语句和for语句里面的{ }也属于块作用域。
if(true){ let age = 100; } for(let i=0;i<10;i++){ console.log(i); } // console.log(age);// ferenceError: age is not defined console.log(i);// ferenceError: i is not defined
-
在块内使用let声明的变量,只会在当前的块内有效。
{ let a =1; a = 2; console.log(a); } console.log(a);// 异常
2.1.3、let关键字
ES6
新增了let
命令,用来声明变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效,也就是增加了块级作用域。
- 使用块级作用域(let定义的变量属于块级作用域) 防止全局变量污染
let age = 1; { let age = 2; } console.log(age);// 1
- 块级作用域可以任意嵌套
{ let a = 1; { let a = 2; { let a = 3; console.log(a);// 3 } console.log(a);// 2 } console.log(a);// 1 }
- for循环的计数器,就很合适使用let命令
var btns = document.querySelectorAll("button"); for(let i=0;i<btns.length;i++){ btns[i].onclick = function(){ console.log(i); } }
- 变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
var btns = document.querySelectorAll("button"); { let i = 0; btns[i].onclick = function(){ console.log(i); } } { let i = 1; btns[i].onclick = function(){ console.log(i); } } { let i = 2; btns[i].onclick = function(){ console.log(i); } } { let i = 3; btns[i].onclick = function(){ console.log(i); } } { let i = 4; btns[i].onclick = function(){ console.log(i); } }
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
- 练习1
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6]();
- 练习2
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6]();
2.1.4、let关键字特点
- let命令不存在变量提升
console.log(a);// ferenceError: Cannot access 'a' before initialization let a = 1; console.log(a);// 1 /***************************************************/ function fn(){ console.log(userName); } // fn();// 异常 let userName = "lisi"; fn();// 正常
和var不同的还有,let命令不存在变量提升,所以声明前调用变量,都会报错,这就涉及到一个概念——暂时性死区。
暂时性死区即:区块中存在
let
或const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
- 不允许重复声明
let 只能声明一次而var 可以声明多次。
console.log(1); let a = 1; let a = 2; console.log(a);// 异常
- 块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式不再必要了
// 匿名函数立即调用: // (function (){ // var a = 1; // console.log(a); // })(); // console.log(a);// ferenceError: a is not defined // 块级作用域: { let a = 1; console.log(a); }
- let 是在代码块内有效,var 是在全局范围内有效
{ var a = 1; let b = 2; } console.log(a);// 1 console.log(b);// 异常
- 不影响作用域链
let与var都拥有作用域链。
作用域链: 如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
{ let a = 300; { let a = 200; { let a = 100; console.log(a);// 100 } console.log(a);// 200 } console.log(a);// 300 }
- 不再是顶层全局对象的属性
使用var定义的全局变量相当于直接挂载在window对象上, 而let不会。
let b = 2; console.log(window.b);// undefined console.log(this.b);// undefined
2.2、const
关键字
注意:
变量:数据可以变化。在执行过程当中,有一些数据会使用多次,根据条件会变化,一般定义为变量。
常量:不会变化的数据,有些时候有的数据是不允许修改的,所以需要定义常量。
- 声明一定要赋初始值:一旦声明变量,就必须立即初始化,不能留到以后赋值
const
声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。const userName;// 语法错误,必须要设置初始值
- 值不允许修改
const userName = "zhangsan"; userName = "lisi";// 异常
const 其实保证的不是变量的值不变,而是保证变量指向的内存地址不允许改动。所以 使用 const 定义的对象或者数组,其实是可变的。
const obj = { num:100 } obj.num = 200; console.log(obj.num);// 200 const arr = [1,2,3]; arr[0] = 20; console.log(arr);// [20,2,3]
const
只在声明所在的块级作用域内有效。(与let相同){ const a = 1; console.log(a); } console.log(a)// 异常:ferenceError: a is not defined
const
命令声明的常量也是不会提升(与let相同)console.log(a);// 异常:ferenceError: Cannot access 'a' before initialization const a = 100;
const
不可重复声明(与let相同)// 语法异常:ntaxError: Identifier 'age' has already been declared const age = 100; +
- 不再是顶层全局对象的属性(与let相同)
let命令、
const
命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6
开始,全局变量将逐步与顶层对象的属性脱钩。
const使用的两点建议:
1、被多次使用且不允许更改的数据建议通过const定义;
2、项目全局常量建议大写,单词之间用-分隔;
3、如果不清楚要使用let还是const,那么就用const。如果后面发生值的改变,那么再将const改成let.
4、以后不允许使用val
2.3 、块级作用域的函数声明
函数声明一般常用的是两种,一种是function声明,一种是函数表达式。
建议函数在顶层作用域和函数作用域之中声明,尽量避免在块级作用域声明。
如果确实需要,也应该写成函数表达式,而不是函数声明语句。
三、变量的解构赋值
let a = 1;
cosnt b = 2;
const obj = {
}
3.1、什么是变量的解构赋值
ES6
允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构赋值本质就是赋值:把结构解散重构然后赋值。
解构赋值是对赋值运算符=的一种扩展。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
3.2、引入
在
ES5
中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码 ;// 同质化代码 // function fn(obj){ // console.log(obj.a); // console.log(obj.b); // console.log(obj.c); // } // fn({a:1,b:2,c:3}); // 数组 const arr = [1,2,3,4]; const a = arr[0]; const b = arr[1]; const c = arr[2]; const a = arr[3];
3.3、解构赋值语法
let a = 1;
解构的目标 = 解构源;(目标指的是定义的常量或变量,解析源一般指的是数组或对象)
解构目标:定义的常量或变量
解构源:待解构的数组或对象
3.4、对象解构赋值
- 对象解构赋值基本语法
对象的语法形式是在一个赋值操作符= 右边放置一个对象字面量
- 顺序不用一一对应
// 未使用解构赋值: // const obj = {a:1,b:2,c:3}; // const a = obj.a; // const b = obj.b; // const c = obj.c; // 语法糖: // 将对象下的a,b,c属性的值赋值给常量a,b,c // const {a, b, c} = {a: 1, b: 2, c: 3}; // console.log(a,b,c);// 1 2 3
- = 右侧可以是一个常或变量
// const {c,a,b} = {a:1,b:2,c:3}; // console.log(a,b,c);// 1 2 3 const obj = {userName:"zhangsan",age:12}; const {userName,age} = obj; console.log(userName,age);// zhangsan 12
- 嵌套对象解构
解构嵌套对象仍然与对象字面量的语法相似,可以将对象拆解以获取想要的信息
const obj = { type:1, userInfo:{ userName:"zhangsan", age:12 } }; // const {type,userInfo} = obj; // console.log(type,userInfo); // console.log(userInfo === obj.userInfo);// true // obj对象下的属性type值赋值给常量type // const {type} = obj; // obj对象下的属性type,userInfo赋值给常量type,userInfo // const {type,userInfo} = obj; // 目标:obj对象下的属性type,赋值给常量type,obj对象下的userInfo内的属性userName,age赋值给常量userName,age // 将obj下的属性userInfo赋值给常量info // const {type,userInfo:info} = obj; // console.log(info); // 将obj下的属性userInfo再次解析赋值,赋值给常量userName,age const {type,userInfo:{userName,age}} = obj; console.log(type,userName,age);
- 可忽略解构源的属性
// 忽略了解构源中的a,c属性 const {b} = {a:1,b:2,c:3}; console.log(b);
- 剩余运算符
// const {a,b,...suibian} = {a:1,b:2,c:3,d:4}; // console.log(a,b,suibian);// 1 2 {c:3,d:4} // const {userName,...obj} = {userName:"lisi",age:100}; // console.log(userName,obj);// lisi {age:100} // 以下写法即实现了一个对象的复制: // const {...obj} = {userName:"lisi",age:100}; // console.log(obj);// lisi {userName:"lisi",age:100} // 如果解析源是一个简单对象,那么是深复制 // const info = {userName:"lisi",age:100}; // const {...obj} = info; // console.log(obj === info);// false // obj.userName = "zhangsan"; // console.log(info.userName);// lisi // 如果解析源是一个复杂对象,那么是浅复制(因为只会深复制一层) // const info = { // userName:"zhangsan", // age:12, // my:{ // a:1, // b:2 // } // } // const {...obj} = info; // // 相当于: // // const obj = { // // userName:info.userName, // // age:info.age, // // my:info.my // // } // console.log(obj === info);// false // console.log(obj.my === info.my);// true // obj.my.a = 100; // console.log(info.my.a);// 100 // 剩余运算符只允许写在{}的最后边:以下会有异常:ntaxError: Rest element must be last element (a const {a,...my,b} = {a:1,b:2,c:3}; console.log(a,b,my);
- 不完全解构:变量名称在对象中不存在
使用解构赋值表达式时,如果指定的变量名称在对象中不存在,那么这个变量会被赋值为undefined
const {a,b,c,d,suibian} = {a:1,b:2,c:3,d:4}; console.log(a,b,c,d,suibian);// 1 2 3 4 undefined
- 解构默认值(常用)
当指定的属性不存在时,可以定义一个默认值:在属性名称后添加一个等号(=)和相应的默认值即可
// const {a,b,c} = {a:1,b:2}; // console.log(a,b,c);// 1 2 undefined // const {a,b,c=300} = {a:1,b:2}; // console.log(a,b,c);// 1 2 300 const {a,b,c=300} = {a:1,b:2,c:3}; console.log(a,b,c);// 1 2 3
- 为非同名局部变量赋值 ,可避免命名冲突
如果希望使用不同命名的局部变量来存储对象属性的值,
ES6
中的一个扩展语法可以满足需求,这个语法与完整的对象字面量属性初始化程序的很像。// 定义的常量的名字与=右侧的属性名相同。 // const {a,b,c} = {a:1,b:2,c:3}; const a = 1; let b = 2; const c = 3; const obj = { a:10, b:20, c:30 } const {a:aa,b:bb,c:suibian} = obj; console.log(a,b,c,aa,bb,suibian);// 1 2 3 10 20 30
- 函数传参数
解构赋值表达式的值与表达式右侧(也就是=右侧)的值相等,如此一来,在任何可以使用值的地方都可以使用解构赋值表达式
// function fn({a,b,c}){ // console.log(a,b,c);// 1 2 3 // } // fn({a:1,b:2,c:3}) // function fn({a,...suibian}){ // console.log(a,suibian);// 1 {b:2,c:3} // } // fn({a:1,b:2,c:3}) // function fn({a, b, c:cc, d = 100}) { // console.log(a, b, cc, d);// 1 2 3 100 // } // // fn({a: 1, b: 2, c: 3})
- 补充
// undefined无法解构赋值 // let obj; // const {a, b, c} = obj; // 以下无法解构赋值 // function fn({a,b,c}){ // console.log(a,b,c); // } // fn(); // 解决: // 如果未传递参数,默认将{}进行解构赋值。 function fn({a=10,b=20,c=30}={}){ console.log(a,b,c); } fn();
3.5、数组解构赋值
- 基本使用
与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性 。
// const arr = [1,2,3,4]; // const a = arr[0]; // const b = arr[1]; // const c = arr[2]; // const d = arr[3]; const [a,b,c,d] = [1,2,3,4]; console.log(a,b,c,d);// 1 2 3 4 // 必须一一对应 const [c,a,b,d] = [1,2,3,4]; console.log(a,b,c,d);// 2 3 1 4
- 忽略元素
在解构模式中,可以直接省略元素,只为感兴趣的元素提供变量名 。
const [,,,a,b,c] = [1,2,3,4,5,6]; console.log(a,b,c);// 4 5 6
- 赋值上下文 :对之前定义的变量进行修改(重置)操作。
数组解构也可用于赋值上下文
// 数组: // let a = 1; // let b = 2; // [a,b] = [10,20,30,40]; // console.log(a,b);// 10 20 // 对象: let a = 1; let b = 2; ({a,b}= {a:100,b:200}); console.log(a,b);// 100 200
- 变量交换
数组解构语法还有一个独特的用例:交换两个变量的值。在排序算法中,值交换是一个非常常见的操作,如果要在
ES5
中交换两个变量的值,则须引入第三个临时变量// 交换变量,不允许使用第三方变量 // let a = 1; // let b = 2; // a = a + b;// 3 // b = a - b;// 1 // a = a - b;// 2 // console.log(a,b) let a = 1; let b = 2; [a,b] = [b,a]; console.log(a,b);
- 默认值
也可以在数组解构赋值表达式中为数组中的任意位置添加默认值,当指定位置的属性不存在或其值为undefined时使用默认值
// const [a,b,c,d,e] = [1,2,3,4]; // console.log(a,b,c,d,e);// 1 2 3 4 undefined // const [a,b,c,d,e=100] = [1,2,3,4]; // console.log(a,b,c,d,e);// 1 2 3 4 100 const [a,b,c,d,e=100] = [1,2,3,4,5]; console.log(a,b,c,d,e);// 1 2 3 4 5
- 嵌套数组解构
嵌套数组解构与嵌套对象解构的语法类似,在原有的数组模式中插入另一个数组模式,即可将解构过程深入到下一个层级
// const [a,b,c,d,e] = [1,2,[3,4],5,6] // console.log(a,b,c,d,e);// 1 2 [3,4] 5 6 const [a,b,[c,cc,ccc=100],d,e] = [1,2,[3,4],5,6] console.log(a,b,c,d,e,cc,ccc);// 1 2 3 5 6 4 100
- 不定元素
函数具有不定参数,而在数组解构语法中有一个相似的概念——不定元素。在数组中,可以通过…语法将数组中的其余元素赋值给一个特定的变量
// const [a,b,...c] = [1,2,3,4]; // console.log(a,b,c);// 1 2 [3,4] // 注意:...只允许写在最后,以下有异常 // const [a,...c,f] = [1,2,3,4]; // const [...arr] = [1,2,3,4]; // console.log(arr);// [1,2,3,4] // const arr = [1,2,3,4]; // const [...arr2] = arr; // 上方代码相当于: // const arr2 = []; // arr2.push(arr[0]); // arr2.push(arr[1]); // arr2.push(arr[2]); // arr2.push(arr[3]); // console.log(arr2 === arr);// false // 针对于简单数组而言是深复制 // const arr = [1,2,3]; // const [...arr2] = arr; // console.log(arr === arr2);// false // arr2[0] = 100; // console.log(arr); // 什对于复杂数组而言是浅复制 const arr = [1,2,[3,4]]; const [...arr2] = arr; console.log(arr === arr2);// false console.log(arr[2] === arr2[2]);// true arr2[2][0] = 300; console.log(arr);// [1,2,[300,4]]
- 数组复制
在
ES5
中,开发者们经常使用concat()
方法来克隆数组// const arr = [1,2,3]; // const arr2 = [4,5,6]; // const arr3 = [7,8,9] // console.log(arr.concat(arr2,arr3));// [1,2,3,4,5,6,7,8,9] // console.log(arr); const arr = [1,2,3]; const arr2 = arr.concat(); console.log(arr2===arr);
3.6、混合解构(复杂解构)
可以混合使用对象解构和数组解构来创建更多复杂的表达式,如此一来,可以从任何混杂着对象和数组的数据解构中提取想要的信息
面试题:
//复杂解构 let wangfei = { name: '王菲', age: 18, songs: ['红豆', '流年', '暧昧', '传奇'], history: [ {name: '窦唯'}, {name: '李亚鹏'}, {name: '谢霆锋'} ] }; const {name,age,songs:[one,two,three,four],history:[{name:name1},{name:name2},{name:name3}]} = wangfei; console.log(name,age,one,two,three,four,name1,name2,name3);// 王菲 18 红豆 流年 暧昧 传奇 窦唯 李亚鹏 谢霆锋 // const [aa,bb,cc,{userName,age:myAge}] = [1,2,3,{userName:"wangwu",age:12}]; // console.log(aa,bb,cc,userName,myAge);
3.7、 解构传递参数
- 解构赋值可以应用在函数参数的传递过程中。
// function fn(obj){ // // 同质化: // console.log(obj.a); // console.log(obj.b); // console.log(obj.c); // console.log(obj.a) // } // fn({a:1,b:2,c:3}); // function fn({c,b,a,f=100}){ // console.log(c,b,a,f);// 3 2 1 100 // } // fn({a:1,b:2,c:3}) // function fn([a,b,c,f=10]){ // console.log(c,b,a,f);// 300 200 100 10 // } // fn([100,200,300]) // const {0:a,1:b,2:c} = [1,2,3]; // console.log(a,b,c);
- 如果调用函数时不提供被解构的参数会导致程序抛出错误
// typeError: Cannot destructure property 'a' of 'undefined' as it is undefined. // function fn({a,b,c}){ // console.log(a,b,c);// 1 2 3 // } // fn(); // 解决: // function fn({a,b,c}={}){ // console.log(a,b,c);// undefined undefined undefined // } // fn(); function fn([a,b,c]=[]){ console.log(a,b,c);// undefined undefined undefined } fn();
3.8、解构返回结果
- 函数的多个返回值获取
{ // 1 function fn(){ return [1,2] } // const arr = fn(); // console.log(arr,arr[0],arr[0],arr[1],arr[1]);// [1,2] const [a,b] = fn(); console.log(a,a,a,a,a,a,b,b,b,b,b,b); } { // 2 function fn(){ return { n:100, m:90 } } const {n,m} = fn(); console.log(n,m);// 100 90 }
3.9、字符串解构
- 字符串也可以解构赋值。这是因为,字符串被转换成了一个类似数组的对象
// const str = "我爱你中国,我亲爱的母亲,我为你流泪也为你自豪!"; // const [a,b,c,d,e,f,g,...m] = str; // console.log(a,b,c,d,e,f,g,m); // const [a,b,c,d,e,f,g,...m] = new String(str); // console.log(a,b,c,d,e,f,g,m); // const {0:aa,1:bb} = new String(str); // console.log(aa,bb); // 可以解构原型链中的属性 // function Box(){ // this.num = 100; // } // Box.prototype.a = 200; // Box.prototype.run = function(){ // // }; // const {num,a,run} = new Box(); // console.log(num,a,run);// 100 200 f const str = "我爱你中国,我亲爱的母亲,我为你流泪也为你自豪!"; const {length,slice} = str; console.log(length,slice); // console.log(str.slice(2)); // console.log(slice.call("abc",2));
3.10、数值和布尔值解构
- 解构赋值时,如果等号右边是数值和布尔值,则会先转为对象
// 数字 // let num = 100; // const {toString,toFixed} = num;// new Number(num) // console.log(toString,toFixed); // console.log(100.123.toFixed(2)) // console.log(toFixed.call(100.125,2)) // 布尔 const {toString} = true; console.log(true.toString()); console.log(toString.call(true)) console.log(toString.call(false)) console.log(toString.call(1))
四、字符串的扩展
4.1、模板字符串
传统的 JavaScript 语言,输出模板通常要拼接字符串
- 模板字符串(template string)是增强版的字符串,用反引号(`)标识。可以嵌套变量,可以换行,可以包含单引号和双引号。
{ let a = "aaaaaa"; let b = 'bbbbbb'; let c = `cccccc`; console.log(a,b,c);// aaaaaa bbbbbb cccccc let str = "我想告诉你\"我现在过的很好\""; console.log(str); let str2 = '我想问问你\'你现在过的还好吗?\''; console.log(str2); // 可以包含单引号和双引号。 let str3 = `'我现在过的很好!'"也祝你过的很好!"`; console.log(str3); // 可以换行 let str4 = ` 床前明月光, 疑是地上霜 `; console.log(str4); }
它可以当作普通字符串使用,也可以用来定义多行字符串。模板字符串中嵌入变量,需要将变量名写在
${}
之中。
- 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let firstName = "张"; let lastName = "培跃"; let age = 18; let str = "师法魔级超"; console.log("大家好!我的名字叫"+firstName+lastName+",我今年"+age); console.log(`大家好!我的名字叫${firstName}${lastName},我今年${age}`); console.log(`年龄${age}为${age<=18?'少年':'未知'}`); console.log(`${str}反转过来以后是:${str.split('').reverse().join('')}`)
- 模板字符串之中还能调用函数。
function fn(){ return 1000; } // 插值表达式${} console.log(`函数可以在插值表达式中调用${fn()}`) console.log(`1-${11}`) console.log(`1-${"asdfadsf"}`) console.log(`1-${true}`) console.log(`1-${false}`) console.log(`1-${undefined}`) console.log(`1-${null}`) console.log(`1-${{a:1,b:2,c:3}}`);// 会将对象转为字符串 console.log(`1-${[1,2,3]}`);// 将[]省略,会将数组转为字符串 console.log([1,2,3].toString());
- 模板字符串应用
const root = document.querySelector("#root"); // 1 // data.forEach(function (info) { // root.innerHTML += "<div><h3>"+info.nm+"</h3><img height='200' src='"+info.img+"'/></div><hr/>" // }) // 2 // let str = ""; // data.forEach(function (info) { // str += "<div><h3>"+info.nm+"</h3><img height='200' src='"+info.img+"'/></div><hr/>" // }) // root.innerHTML = str; // 3 root.innerHTML = data.map(function(info){ return (` <div> <h3>${info.nm}|${info.mk}</h3> <img height="200" src="${info.img}" alt=""> <hr/> </div> `); }).join("");
4.2、字符串的新增方法
去空格
trim()
:删除字符串两端的空白符(常用)
trimStart()
去除首部的空格
trimEnd()
去除尾部的空格const inp = document.querySelector("input"); inp.onkeyup = function(e){ if(e.keyCode === 13){ console.log("内容:","青龙"+e.target.value+"白虎") // - `trim()`:删除字符串两端的空白符(常用) console.log("trim:","青龙"+e.target.value.trim()+"白虎") // - `trimStart() `去除首部的空格 console.log("trimStart:","青龙"+e.target.value.trimStart()+"白虎") // - `trimEnd()` 去除尾部的空格 console.log("trimEnd:","青龙"+e.target.value.trimEnd()+"白虎") } }
判断
startsWith()
;判断开头有没有包含某个字符串let str = "abc一会下课了,要吃什么呢?" console.log(str.startsWith("abc"));// true console.log(str.startsWith("ab"));// true console.log(str.startsWith("a"));// true console.log(str.startsWith("abc一"));// true console.log(str.startsWith("一"));// false
endsWith()
;判断结尾有没有包含某个字符串console.log(str.endsWith("abc"));// false console.log(str.endsWith("?"));// true console.log(str.endsWith("要吃什么呢?"));// true
includes
判断字符串是否包含某个字符串(常用)console.log(str.includes("下课"));// true console.log(str.includes("abc"));// true console.log(str.includes("cd"));// false
repeat
重复当前的字符串,可以规定次数let str = "发财"; console.log(str.repeat(2));// 发财发财
补充字符
padStart()
当字符串不够某个长度的时候,在前边补充任意字符let num = 4; console.log(num.toString().padStart());// 4 console.log(num.toString().padStart(2));// 4 console.log(num.toString().padStart(2,0));// 04 console.log(num.toString().padStart(5,0));// 00004 console.log(num.toString().padStart(5,"abc"));// abca4
padEnd()
,//当字符串不够某个长度的时候,在后边补充任意字符let str = "A"; console.log(str.padEnd(2)+"!");// A ! console.log(str.padEnd(2,0)+"!");// A0! console.log(str.padEnd(5,0)+"!");// A0000! console.log(str.padEnd(5,"abc")+"!");// Aabca! ``` * 实现封装一个函数用于获取当前时间:yyyy-mm-dd HH:mm:ss ```js // 默认返回当前时间,可以根据时间戳返回时间 // time时间戳 function getNowTime(time=Date.now()){ const times = new Date(time); return times.getFullYear()+"-"+ (times.getMonth()+1).toString().padStart(2,0)+"-"+ times.getDate().toString().padStart(2,0)+" "+ times.getHours().toString().padStart(2,0)+":"+ times.getMinutes().toString().padStart(2,0)+":"+ times.getSeconds().toString().padStart(2,0); } console.log(getNowTime(99999));// 2023-04-21 16:18:20
五、spread运算符与rest参数
在
ES6
中, 三个点(…) 有2个含义。分别表示扩展运算符(spread运算符) 和 剩余运算符(rest参数)
5.1、spread运算符
- 复制、合并数组
// 深复制 // const arr = [1, 2, 3, 4]; // const arr2 = [...arr]; // console.log(arr === arr2);// false // 浅复制 // const arr = [{a:1}]; // const arr2 = [...arr]; // console.log(arr === arr2);// false // console.log(arr[0] === arr2[0]);// true // arr[0].a = 100; // console.log(arr2[0].a); // 合并 const arr = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = arr.concat(arr2); console.log(arr3); const arr4 = [...arr, ...arr2];// [1,2,3,4,5,6] console.log(arr4); const arr5 = [ 0, ...arr, 100, 200, ...arr2, 99 ]; console.log(arr5);
- 复制、合并对象
// 复制 // const obj = { // a:1, // b:2, // c:3 // } // const obj2 = {...obj}; // console.log(obj2 === obj);// false // 合并 // const obj = { // a:1 // } // const obj2 = { // b:2 // } // const obj3 = Object.assign({},obj,obj2); // console.log(obj3); // // const obj4 = { // ...obj, // ...obj2 // } // console.log(obj4); // // const obj5 = { // userName:"laxi", // ...obj,// a:1 // age:12, // ...obj2// b:2 // } // console.log(obj5);
- 字符串转换为数组
let str = "abcdefg"; console.log(str.split("")); console.log([...str]);
- 伪数组转为真数组
// 1 // function fn(){ // const changeArguments = [...arguments]; // changeArguments.push(100); // console.log(changeArguments) // } // fn(1,2,3,4); // 2 [...document.querySelectorAll("button")].map(function(btn){ console.log(btn); })
5.2、rest参数
解构数组与对象(剩余运算)
rest 参数(形式为
...变量名
),rest运算符用于获取函数调用时传入的参数。// function fn(a,b,c,d,...suibian){ // console.log(a,b,c,d,suibian);// 1 2 3 4 [5,6,7,8] // } // fn(1,2,3,4,5,6,7,8); // function fn(...suibian){ // console.log(suibian);// [1,2,3,4,5,6,7,8] // } // fn(1,2,3,4,5,6,7,8); * 和普通参数混合使用的时候,需要放在参数的最后 ```js // 以下不允许 function fn(a,...my,b){ } fn(1,2,3,4);
- 函数的
length
属性,不包括 rest 参数function fn(a,b,c,d,...my){ } console.log(fn.name);// fn console.log(fn.length);// 4
六、数组的扩展
6.1、Array对象的新方法
- from:把伪数组转换成数组(可以使用数组的方法)
{ // 1 // function fn(){ // // 转为真数组 // const arr = Array.from(arguments); // arr.push(100); // console.log(arr);// [1,2,3,4,100]; // } // fn(1,2,3,4); // 2 const btns = document.querySelectorAll("button"); // let arr2 = []; // btns.forEach(function(btn){ // arr2.push(btn.innerText) // }) // console.log(arr2); let arr2 = Array.from(btns).map(function (suibian) { return suibian.innerText; }) console.log(arr2); } { // 可以实现对数组的复制(针对于简单数组为深复制,复杂数组为浅复制) // const arr = [1, 2, 3, 4]; // const arr2 = Array.from(arr); // console.log(arr2, arr2 === arr);// [1, 2, 3, 4] false const arr = [1,{},2]; const arr2 = Array.from(arr); console.log(arr === arr2,arr[1] === arr2[1]); arr2[1].userName = "zhangsan"; console.log(arr[1].userName);// zhangsan }
- of:将一组数字转换成数组 弥补Array的不足
{ // 不足:传递的参数代表的含义不同。 // console.log(new Array());// [] // console.log(new Array(10));// 定义了一个长度为10的数组,元素为empty // console.log(new Array(1,2,3,4,5,6,7));// [1,2,3,4,5,6,7] console.log(Array.of());// [] console.log(Array.of(1));// [1] console.log(Array.of(1,2,3,4));// [1,2,3,4] console.log(Array.of("one","two","three")); }
6.2、Array原型上的新增方法
copyWithin
:数组实例的copyWithin()
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组// const arr = [1,2,3,4,5]; // arr.copyWithin(); // console.log(arr);// [1,2,3,4,5] // const arr = [1,2,3,4,5]; // // 复制的内容:1,2,3,4,5, 从下标1开始替换[1,1,2,3,4] // // 操作完毕之后会将arr进行返回 // const arr2 = arr.copyWithin(1);// 从下标为1的元素开始进行覆盖。默认从下标0开始到长度-1复制 // console.log(arr,arr2,arr === arr2);// [1, 1, 2, 3, 4] [1, 1, 2, 3, 4] true // const arr = [1,2,3,4,5,6]; // const arr2 = arr.copyWithin(3);// 复制出来的内容1 2 3 4 5 6 // console.log(arr2);// [1,2,3,1,2,3] // const arr = [1,2,3,4,5,6]; // 第一个参数为替换位置下标 // 第二个参数是复制内容开始的下标,默认为0 // console.log(arr.copyWithin(3,2));// 复制的内容 3,4,5,6 结果:[1,2,3,3,4,5] // const arr = [1,2,3,4,5,6]; // 第三个参数是复制结束的位置-1 // console.log(arr.copyWithin(3,2,4));// 复制的内容 3 4 结果[1,2,3,3,4,6] const arr = [1,2,3,4,5,6,7,8,9]; console.log(arr.copyWithin(3,2,5));// 复制的内容 3,4,5 结果:[1,2,3,3,4,5,7,8,9] console.log(arr.copyWithin(2,1,6));// 复制的内容 2 3 3 4 5 结果:[1,2,2,3,3,4,5,8,9]
fill:使用固定值填充数组
fill
方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。const arr = [1,2,3,4,5]; const arr2 = arr.fill(100); console.log(arr2,arr,arr===arr2);// [100, 100, 100, 100, 100] [100, 100, 100, 100, 100] true const arr3 = []; console.log(arr3.fill(1)); console.log(new Array(10).fill(1));// [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
fill
方法还可以接受第二个(默认0)和第三个参数(默认数组的长度),用于指定填充的起始位置和结束位置。const arr = [1,2,3,4,5]; // console.log(arr.fill("one",1));// [1, 'one', 'one', 'one', 'one'] console.log(arr.fill("one",1,3));// [1, 'one', 'one',4, 5]
entries()
,keys()
和values()
ES6
提供三个新的方法——entries()
,keys()
和values()
——用于遍历数组。它们都返回一个遍历器对象,可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。const arr = ["one","two","three"]; // for(let item of arr.values()){ // console.log(item); // } // for(let item of arr){ // console.log(item); // } // for(let index of arr.keys()){ // console.log(index) // } // for(let item of arr.entries()){ // const [index,value] = item; // console.log(item,index,value); // } for(let [index,value] of arr.entries()){ console.log(index,value); }
find
和findIndex
(记住)数组实例的
find
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
{ const arr = [ { id:1, bookName:"水浒传" },{ id:2, bookName: "西游记" },{ id:3, bookName: "三国演义" },{ id:4, bookName: "红楼梦" } ,{ id:5, bookName: "西游记" } ]; // find:接收一个回调函数 // 回调函数一旦返回真,那么后续的遍历不会执行。 // 会将第一个满足条件的元素返回,如果没有满足条件 的元素返回的是undefined // const bookInfo = arr.find(function(item){ // return item.bookName === "西游记"; // }) // if(bookInfo){ // console.log("有") // }else{ // console.log("无") // } // console.log(arr.some(function(item){ // return item.bookName === "西游记1" // })) // findIndex:接收一个回调函数 // 回调函数一旦返回true,那么后续的遍历不会执行 // 会将第一个满足条件元素的下标进行返回,如果找不到满足条件的元素,那么返回-1 console.log(arr.findIndex(function(item){ return item.bookName === "西游记1"; })) }
includes:(记住)
includes
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似const arr = [1,2,3,4]; console.log(arr.includes(4));// true console.log(arr.includes(40));// false
flat
数组的成员有时还是数组,
Array.prototype.flat()
用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
flat()
默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()
方法的参数写成一个整数,表示想要拉平的层数,默认为1// const arr = [1,2,[3,4,5],6,7,8]; // console.log(arr.flat(),arr);// [1,2,3,4,5,6,7,8] // const arr = [1,2,[3,[4,5,6],7],8]; // console.log(arr.flat());// [1,2,3,[4,5,6],7,8]; // console.log(arr.flat().flat());// [1,2,3,4,5,6,7,8]; // console.log(arr.flat(1));// [1,2,3,[4,5,6],7,8]; // console.log(arr.flat(2));// [1, 2, 3, 4, 5, 6, 7, 8];
- 如果不管有多少层嵌套,都要转成一维数组,可以用
Infinity
关键字作为参数const arr = [1,2,[3,[4,5,6],7],8]; console.log(arr.flat(Infinity));// [1, 2, 3, 4, 5, 6, 7, 8]
七、函数的扩展
7.1、 函数参数默认值
7.1.1、ES5
默认参数
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function fn(num){ if(num === undefined) num = 1; console.log(num); } fn();
7.1.2、ES6
默认参数
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
// function fn(num=1){ // console.log(num);// 1 // } // fn();
7.2 rest参数(详见5.2)
7.3 箭头函数
7.3.1、什么是箭头函数
ES6
允许使用“箭头”(=>
)定义函数。// 1 function fn(a, b) { return a + b; } console.log(fn(1, 2));// 3 // 2 const fn2 = function (a, b) { return a + b; } console.log(fn2(10, 20));// 30 // 3 const fn3 = new Function("a", "b", "return a+b"); console.log(fn3(100, 200));// 300 // 4- 箭头函数 const fn4 = (a, b) => { return a + b; } console.log(fn4(9,99));// 108
7.3.2、箭头函数的写法
箭头函数分为以下几种情况
- 只有一个参数,括号可以省略
{ // const fn = (a) => { // return 100 + a; // } // 上方函数的括号可以省略 // const fn = a => { // return 100 + a; // } // console.log(fn(1));// 101 // 如果参数为多个或无参数括号不能省略 // 无参数,不可以省略括号 const fn = ()=>{ } // 多个参数,不可以省略括号 const fn2 = (a,b,c) => { } }
- 函数体是一条语句的时候,可以省略花括号,会将该语句的结果进行返回(return 省略不写)
{ const fn = () => { return 1; } // 简写为: const fn2 = () => 1; console.log(fn());// 1 console.log(fn2());// 1 } { const fn = (a) => { return a; } const fn2 = a => a; console.log(fn(100));// 100 console.log(fn2(100));// 100 }
没有参数或者多个参数的时候,参数的括号不能省略
当函数体不是一句话的时候,花括号 不可以省略
{ const fn = ()=>{ let a = 1; console.log(a); } }
- 如果函数体内只有一行代码,该行代码返回的是对象的话,可以使用括号。
// 1 // const fn = function(){ // return { // a:1, // b:2 // } // } // 2- 箭头函数 const fn = ()=>{ return ({ a:1, b:2 }) } // 3- 箭头函数简写 const fn2 = ()=>({ a:1, b:2 })
7.3.3、箭头函数的注意事项
关于this
箭头函数没有自己的this,箭头函数内部的this并不是调用时候指向的对象,而是定义时指向的对象
// window.userName = "window->userName"; // const obj = { // userName:"obj->userName", // fn:function(){ // // this与调用时查看 // // 1- 是否有显式绑定this. call apply bind // // 2- 是否有隐式绑定 . 左侧 // console.log(this===obj,this.userName) // }, // run:()=>{ // console.log(this.userName); // } // } // obj.fn();// obj // obj.fn.call(window);// window // obj.fn.apply(window);// window // obj.fn.call();// window // obj.fn.apply();// window // obj.fn.bind()();// window // // obj.run();// window // 2-示例 // const btns = document.querySelectorAll("button"); // btns.forEach(function (btn){ // btn.onclick = function(){ // console.log(this);// btn // } // }) // btns.forEach(function (btn){ // btn.onclick = ()=>{ // console.log(this);// {a:1,b:2} // } // }.bind({a:1,b:2})) // btns.forEach((btn)=>{ // btn.onclick = ()=>{ // console.log(this);// window // } // }) // 3- 示例 function Tag(){ this.btns = document.querySelectorAll("button"); this.init(); } window.btns = document.querySelectorAll("button"); Tag.prototype.init = ()=>{ this.btns.forEach(btn=>{ btn.onclick = ()=>{ console.log(this);// btn } }) } const tag = new Tag();
箭头函数不能用于构造函数,也就是不能使用new关键字调用
const Tag = ()=>{ } // new Tag();// ypeError: Tag is not a constructor console.log(Tag.prototype);// undefined // function Box(){ // // } // const obj = new Box(); // console.log(obj.constructor === Box);// true // console.log(obj.__proto__.constructor === Box);// true // console.log(Box.prototype.constructor === Box);// true // console.log(Box.constructor === Function);// true - 箭头函数没有arguments对象 const fn = () => { // ReferenceError: arguments is not defined // console.log(arguments); } fn();
- 箭头函数使用call apply bind,无法提定this
const obj = { a: 1, b: 2 } const fn = () => { console.log(this === obj); } fn.call(obj);// false fn.apply(obj);// false fn.bind(obj)();// false
八、对象的扩展
8.1、对象的简写
ES6
允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。// 未简写: // let a = 100; // const obj = { // a:a, // b:2, // fn:function(){ // console.log(this.a,this.b); // } // } // console.log(obj.a,obj.b);// 100 2 // obj.fn();// 100 2 // 简写: let a = 100; const obj = { a,// 将变量a的名字作为属性名,变量a的值作为属性值。 b:2, // 可以省略:function fn(){ console.log(this.a,this.b); }, run:()=>{ console.log(this.a,this.b);// undefined undefined } } console.log(obj.a,obj.b);// 100 2 obj.fn();// 100 2 obj.run();
8.2 、属性名表达式
JavaScript 定义对象的属性,有两种方法:点运算符和中括号运算符
// const obj = { // a:1 // } // obj.b = 2; // obj["c"]=3; // console.log(obj);
但是,如果使用字面量方式定义对象(使用大括号),在
ES5
中只能使用标识符,而不能使用变量定义属性。也就是说在
ES5
中 key/value key是固定不变的,在ES6
中,支持属性表达式,支持key发生变化let b = 2; const obj = { a:1, [b]:2,// 将变量b的值作为key(属性名), "#$%^&*(":3 } // b = 3; console.log(obj["2"]);// 2 console.log(obj[b]);// 2 console.log(obj["#$%^&*("]);// 3 console.log(obj);// {2:2,a:2}
8.3、 对象的扩展运算符 …(见5.1)
8.4 对象新增的方法
8.4.1、Object.is()
判断对象是否相等 相当于===,修复NaN不等自己的问题
console.log(1 == "1");// true console.log(1 === "1");// false const obj = {}; console.log(obj.a === undefined);// true console.log(null === null);// true console.log(NaN === NaN);// false console.log(Object.is(NaN,NaN));// true console.log(Object.is(1,"1"));// false console.log(Object.is(1,1));// true
8.4.2、合并方法Object.assign()
8.4.3、Object.keys
8.4.4、Object.values
九、Math的扩展
9.1、指数运算符
- 在Math中提供了 pow的方法 用来计算一个值的n次方
ES11
提出了新的方法求一个值的n次方 那就是 ** 操作符2**4 // 16
9.2、进制写法
ES6
提供了二进制和八进制数值的新的写法,分别用前缀0b
和0o
表示。0b11110 // 30 0o12 // 10
9.3、Math的新增方法
Math.trunc()
方法会将数字的小数部分去掉,只保留整数部分Math.trunc(123.456) // 123
Math.sign()
判断一个数字的正数还是负数 还是0 或者是NaN
Math.sign(123.456) //1 Math.sign(-123.456) // -1 Math.sign('qwe1') // NaN Math.sign('') // 0
Math.sqrt()
平方根Math.sqrt(16) // 4
Math.cbrt()
立方根Math.cbrt(27) // 3
Math.hypot()
求所有参数平方和的平方根
十、Number的扩展
Number.isFinite(i)
: 用来检查一个数值是否为有限
Number.isNaN(i)
: 判断是否是NaN
Number.isInteger(i)
: 判断是否是整数
Number.parseInt(str)
: 将字符串转换为对应的数值
十一、新增数据类型
11.1、Symbol
11.1.1、什么是Symbol
ES5
的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6
引入Symbol
的原因。
ES6
引入了一种新的原始数据类型Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型
11.1.2、Symbol的使用
Symbol 值通过
Symbol
函数生成。
- 这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
11.1.3、Symbol表示独一无二的值
11.1.4、Symbol的注意事项
Symbol中传入的字符串没有任何意义,只是用来描述Symbol
Symbol不能使用New调用
类型转换的时候,不能转数字
如果把Symbol当作一个对象的属性和方法的时候,一定要用一个变量来储存,否则定义的属性和方法没有办法使用
for in 不能遍历出来,可以使用
Object.getOwnPropertySymbols
方法来拿;
11.2、BigInt
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回
Infinity
。
引入了一种新的数据类型
BigInt
(大整数),来解决这个问题。BigInt
只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。为了与 Number 类型区别,
BigInt
类型的数据必须添加后缀n
。
BigInt
与普通整数是两种值,它们之间并不全等。
typeof
运算符对于BigIn
t 类型的数据返回bigint
。
十二、新增数据结构
12.1、Set
12.1.1、什么是Set
ES6
提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set 数据结构。
Set
函数可以接受一个数组(或者具有iterable
接口的其他数据结构)作为参数,用来初始化
12.1.2、 Set的属性及方法
- size 返回Set的长度
- add 添加某个值,返回 Set 结构本身。
- delete 删除某个值,返回一个布尔值,表示删除是否成功。
- has 返回一个布尔值,表示该值是否为
Set
的成员- clear 清除所有成员,没有返回值。
keys()
:返回键名的遍历器,由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员
12.2、Map
12.2.1、什么是Map
JavaScript 的对象(Object),本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
- 为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
12.2.2、Map的属性和方法
size
属性返回 Map 结构的成员总数。set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。set
方法返回的是当前的Map
对象,因此可以采用链式写法。get
方法读取key
对应的键值,如果找不到key
,返回undefined
。has
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。delete
方法删除某个键,返回true
。如果删除失败,返回false
。clear
方法清除所有成员,没有返回值。keys()
:返回键名的遍历器。values()
:返回键值的遍历器。entries()
:返回所有成员的遍历器。forEach()
:遍历 Map 的所有成员。
十三、iterator
13.1、什么是iterator
- JavaScript 原有的表示“集合”的数据结构,主要是数组(
Array
)和对象(Object
),ES6 又添加了Map
和Set
。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。- 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
- Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是
ES6
创造了一种新的遍历命令for...of
循环,Iterator 接口主要供for...of
消费。
13.2、iterator
在
ES6
中,只要一种数据结构具有了Symbol.iterator
属性,那么就认为是可以迭代的在ES6中,很多数据结构都部署了iterator接口(Array,set,Map,string)
应用场景:
解构赋值的时候默认调用iterator接口
扩展运算符使用的时候页默认调用iterator接口
for of 使用的是iterator接口
对象是没有部署Iterator接口