ES6
1.环境搭建
nodejs
下载地址nodejs
2.ES6介绍
1)ES5的版本升级
2)提供了简介的语法和新的特性
迭代器,Map、Set,Promise,箭头函数,异步函数…
3)ES6在浏览器上的兼容性比ES5差一点,但是在nodejs环境下完全兼容
有些代码在浏览器上没有效果,比如模态化
在js中做ajax、dom时,必须将代码运行在浏览器上
3.基础语法
参考书籍 阮一峰 《ES6 入门教程》有在线文档,可以不用下载或者购买书籍,网站上即可进行自学。
1.let命令
ES6新增了let命令,用来声明变量,用法类似于var
{
var a = 1;
}
console.log(a); //1
{
let b = 1;
console.log(b); //1
}
console.log(b); //报错
JS中只有函数作用域和全局作用域,没有块级作用域,{}限定不了var声明变量的范围
通过let声明的变量,只有在块级作用域中使用,类似于局部变量
实例(vscode测试)
var date = new Date();
function getDate(){
console.log(date);
if(false){
var date = 2020/05/18; //变量提升了
console.log(date);
}
}
getDate(); //undefined
-->
var date = new Date();
function getDate(){
console.log(date);
if(false){
let date = 2020/05/18;
console.log(date);
}
}
getDate(); //当前时间
特点:
1.存在块级作用域
2.不允许变量的重复声明
var a = 1;
var a = 2;
console.log(a); // 2
let b = 1;
let b = 2;
console.log(b); // 'b' has already been declared
3.不允许变量的提升
console.log(a); // undefined
var a = 1;
console.log(b); // b is not defined
let b = 2;
使用let关键字声明的变量,不可以在声明之前被使用,否则会报错
4.暂时性死区
var temp = '123';
if(true){
temp = 'abc';
let temp;
}
console.log(temp) //报错
在判断体中,使用了let声明了一个temp局部变量,在这个变量声明之前,都不可以操作该变量,这个现象叫“暂时性死区”
2.const命令
声明一个只读的常量。一旦声明,常量的值就不能改变
const a = 1;
console.log(a); //1
const a = 1;
a++;
console.log(a); //报错:Missing initializer in const declaration
在使用第三方库的时候,一般会使用const
特点:
1.存在块级作用域
2.不允许变量的重复声明
3.存在暂时性死区
4.变量的声明和初始化要同时进行
const a = 1;
console.log(a); //1
const b;
b = 2;
console.log(b); //报错
总结:
声明方式 变量提升 暂时性死区 重复声明 块级作用域
var 允许 不存在 允许 不存在
let 不允许 存在 不允许 存在
const 不允许 存在 不允许 存在
3.解构(模式匹配)
ES6中,允许按照一定的模式,从数组或对象中提取值,并且对其进行一些操作(赋值......)
1.数组的解构
ES6之前,赋值
var name = 'xpf';
var age = 25;
var gender = 'male';
console.log(name,age,gender);
ES6后,赋值
let [name,age,gender] = ['xpf',25,'male'];
console.log(name,age,gender); // xpf 25 male
只要等号两边的模式相同,左边的变量就会被赋予等号右边对应位置上的值
let [age,name,gender] = ['xpf',25,'male'];
console.log(name,age,gender); // 25 xpf male
案例
等号的左边模式个数等于于右边的
let [,,gender] = ['xpf',25,'male'];
console.log(gender); // male
let [a,b,c] = [1,2,[3,4]];
console.log(a,b,c); // 1 2 [3,4]
等号的左边模式个数大于右边的
let [name] = [];
console.log(name); // undefined
等号的左边模式个数小于右边的
let [gender] = ['xpf',25,'male'];
console.log(gender); // xpf
等号两边的模式不相同
let [gender] = 'xpf';
console.log(gender); // x
let [gender] = true;
console.log(gender); // 报错,true不可迭代
可遍历
Array、String、Map、Set、函数的arguments对象
不可遍历
true/false、NaN、undefined、null、{}
2.数组解构默认值
结构的时候,允许指定默认值
let [name='xpf'] = [];
console.log(name); // xpf
let [name='xpf'] = ['张三'];
console.log(name); // 张三
1、只有当数组对应位置上的值为undefined时,默认值才会生效
let [name='xpf'] = [null];
console.log(name); // null
let [name='xpf'] = [undefined];
console.log(name); // xpf
2、默认值可以引用解构赋值的其他变量,但是这个变量必须已经声明过,否则会报错
let [name,age,gender=name] = ['xpf',25];
console.log(name,age,gender); // xpf 25 xpf
let [name='xpf',name2=name] = [];
console.log(name,name2); // xpf xpf
let [name=name2,name2='xpf'] = [];
console.log(name,name2); // 报错,name2未定义,无法在初始化之前访问到name2
3.对象的解构
var obj = {
name:'xpf',
age:25,
gender:'male'
}
/*var name = obj.name;
var age = obj.age;
var gender = obj.gender;
*/
let {name,age,gender} = obj;
console.log(name,age,gender); // xpf 25 male
对象的解构与数组的解构不同点:
1、数组的元素是按顺序排列的,变量的取值由它的位置决定(根据索引一一对应取值)
对象的属性没有次序,变量必须与属性名同名,才能取到正确的值,跟变量的位置没有关系
等号左边变量的次序,与等号右边同名属性的次序不一样,对取值没有影响
let {age,name,gender} = obj;
console.log(name,age,gender); // xpf 25 male
变量与属性名不同名(解构失败,变量的值为undefined)
let {name,age,address} = obj;
console.log(name,age,address); // xpf 25 undefined
2、对象解构的内部机制
let {name,age,gender:address} = obj;
console.log(name,age,address); // xpf 25 male
==>等价于
let {name:name,age:age,gender:address} = obj;
console.log(name,age,address); // xpf 25 male
let {name:name,age:age,gender:address} = obj;
console.log(name,age,gender); // 报错,gender is not defined
4.对象解构默认值
var obj = {
name:'xpf',
age:25,
gender:undefined
}
let {name,age,gender='male'} = obj;
console.log(name,age,gender); // xpf 25 male
===> 等价于
var obj = {
name:'xpf',
age:25
}
let {name,age,gender='male'} = obj;
console.log(name,age,gender); // xpf 25 male
只有当对象的属性值为undefined的时候,默认值才生效
var obj = {
name:'xpf',
age:25,
gender:'female'
}
let {name,age,gender='male'} = obj;
console.log(name,age,gender); // xpf 25 female
5.解构的用途
1、交换两个变量的值
let a = 1;
let b = 2;
let temp;
temp = a;
a = b;
b = temp;
console.log(a,b); // 2 1
===> 等价于
let a = 1;
let b = 2;
[a,b] = [b,a];
console.log(a,b); // 2 1
2、让函数返回多个值
function math(a,b){
var sub = a-b;
var add = a+b;
return {sub,add}
}
let res = math(1,2);
console.log(res);
console.log(res.sub); // -1
console.log(res.add); // 3
此时取值不方便
说明:如果希望函数同时返回多个参数,我们可以将这几个参数放在一个数组或者对象中返回
function math(a,b){
var sub = a-b;
var add = a+b;
return [add,sub]
}
let [add,sub] = math(1,2);
console.log(add,sub); // 3 -1
3、在对象中快速取值
var obj = {
name:'xpf',
age:25,
gender:'male'
}
/*var name = obj.name;
var age = obj.age;
var gender = obj.gender;
*/
let {name,age,gender} = obj;
console.log(name,age,gender); // xpf 25 male
~~~~
let obj = {
name:'xpf',
age:25,
gender:'male',
address:{
province:'安徽省',
city:'合肥市'
}
}
//var city = obj.address.city;
let {name,address:{city}} = obj;
console.log(name,city); // xpf 合肥市
4、函数参数的定义
ES6之前,不能直接为函数的参数设定默认值
function ajax({url,method='get',data,success}){
console.log(url);
console.log(method);
console.log(data);
console.log(success);
}
ajax({
url:'www.baidu.com',
success:function(){
},
data:{
name:'xpf'
}
})
使用es6的解构传参,此时method有了一个默认值,为get
1、不传递method的时候,为默认的get,传递了method的时候,为传递的值
2、传递参数的时候,参数的顺序可以随便写
4.对象的扩展
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
1)对象属性的简写
ES6之前写对象
let name = 'jack';
let obj = {
name:name
}
console.log(obj.name); //jack
ES6的简写
let name = 'jack';
let obj = {
name
}
console.log(obj.name); //jack
错误写法:
let gender = 'xpf';
let obj = {
name
}
console.log(obj.name); // name is not defined
对象的解构
var obj = {
name:'jack',
age:20,
gender:'male'
}
let {name.age,gender} = obj;
===>等价于
let {name:name,age:age,gender:gender} = obj;
2)对象方法的简写
ES6之前写对象的方法
let obj = {
id:1,
name:'jack',
address:'湖南省',
sayAddress:function(){
console.log('my address is',this.address);
}
}
let {sayAddress} = obj;
obj.sayAddress();
console.log(sayAddress); //my address is 湖南省
ES6的简写
let obj = {
id:1,
name:'jack',
address:'湖南省',
sayAddress(){
console.log('my address is',this.address);
}
}
let {sayAddress} = obj;
obj.sayAddress();
console.log(sayAddress); //my address is 湖南省
注意:
对象中简写的方法不能够当作构造函数,否则会报错
let obj = {
id:1,
name:'xpf',
address:'安徽省',
sayAddress(){
console.log('my address is',this.address);
}
}
let {sayAddress} = obj;
new sayAddress(); // sayAddress is not a constructor
3)构造函数的扩展 Object.xxx
var obj = new Object();
obj 实例
Object 构造函数
Object.prototype 构造函数的原型
实例能够调用构造函数原型中的方法,但是无法调用构造函数中的方法
1.Object.is(value1,value2)
判断两个值是否相等
var res1 = Object.is(1,1);
var res2 = Object.is(1,'1');
var res3 = Object.is('1','1');
var res4 = Object.is({},{});
var res5 = Object.is(+0,-0);
var res6 = Object.is(NaN,NaN);
console.log(res1); // true
console.log(res2); // false
console.log(res3); // true
console.log(res4); // false
console.log(res5); // false
console.log(res6); // true
与 === 类似,会进行数据类型的比较,但是也有不同
1、NaN使用Object.is判断的时候,等于自身
2、+0和-0使用Object.is判断时,不相等
2.Object.assign(target,source1,source2...)
将所有可枚举属性的值从一个或多个源对象复制到目标对象中,并且返回目标对象
可枚举:一般指用户自己写的属性,不包含继承的属性
源对象:source1,source2...
目标对象:target
1、合并对象
let obj1 = {
id:1
}
let obj2 = {
name:'xpf'
}
let obj3 = {
gender:'male'
}
let obj = Object.assign(obj1,obj2,obj3);
console.log('obj',obj); // { id: 1, name: 'xpf', gender: 'male' }
console.log('obj1',obj1); // { id: 1, name: 'xpf', gender: 'male' }
此时,obj1是目标对象,obj2和obj3是源对象
合并后目标对象自身也会改变
注意:当合并的对象具有相同的属性名时,属性会被源对象当中具有相同属性名的值覆盖掉
let obj1 = {
id:1
}
let obj2 = {
name:'xpf'
}
let obj3 = {
name:'male'
}
let obj = Object.assign(obj1,obj2,obj3);
console.log('obj',obj); // { name: 'male', id: 1 }
2、复制对象(克隆)
let obj1 = {
name:'tom',
age:13,
gender:'male'
}
let obj = Object.assign({},obj1);
console.log(obj); // { name: 'tom', age: 13, gender: 'male' }
console.log(obj1); // { name: 'tom', age: 13, gender: 'male' }
console.log(obj == obj1); // false
案例:
一、当源对象中某一个属性值为对象时(即对象嵌套对象)
let obj1 = {
name:'tom',
address:{
province:'安徽省',
city:'合肥市'
}
}
let obj2 = {
name:'tom',
address:{
province:'江苏省'
}
}
let obj = Object.assign(obj1,obj2);
console.log(obj); // { name: 'tom', address: { province: '江苏省' } }
并不会出现如下所期望的结果
address:{
province:'江苏省',
city:'合肥市'
}
也就是说,obj1中的address是被完全替换掉的,这个现象叫做浅拷贝,
如果需要实现深拷贝,可以使用JSON.stringify或者第三方库(lodash)
let obj1 = {
name:'tom',
address:{
province:'安徽省',
city:'合肥市'
}
}
let obj2 = obj1;
let obj3 = JSON.parse(JSON.stringify(obj1));
let obj4 = Object.assign({},obj1);
obj1.address.city = '苏州市';
console.log('obj1',obj1); // 苏州市
console.log('obj2',obj2); // 苏州市
console.log('obj3',obj3); // 合肥市
console.log('obj4',obj4); // 苏州市
注意:如果对象中包含方法,使用JSON.stringify时,该方法会丢失
let obj1 = {
name:'tom',
address:{
province:'安徽省',
city:'合肥市',
sayAddress(){
console.log(111);
}
}
}
let obj3 = JSON.parse(JSON.stringify(obj1));
console.log(obj3); // { name: 'tom', address: { province: '安徽省', city: '合肥市' } }
3. Object.setPrototypeOf(obj,prototype)
将一个对象的原型(prototype)设置到另一个对象obj中
4. Object.getPrototypeOf(obj)
获取一个对象的原型(prototype)
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('你好');
}
var p1 = new Person();
p1.sayName();
function Dog(){
}
//ES5解决方案:子构造函数的原型指向父构造函数的实例,需要在实例创建之前
//Dog.prototype = new Person();
var dog1 = new Dog();
//ES6解决方案:Object.setPrototypeOf
Object.setPrototypeOf(dog1,Person.prototype);
dog1.sayName();
var res = Object.getPrototypeOf(dog1);
console.log(res); // Person { sayName: [Function] }
5. Object.keys(obj)
键
返回一个对象obj的可枚举属性名组成的数组
6. Object.values(obj)
值
返回一个对象obj的可枚举属性值组成的数组
7. Object.entries(obj)
键值对
返回一个对象obj的可枚举属性的键值对数组
let obj = {
name:'xpf',
age:25,
gender:'male'
}
console.log(Object.keys(obj)); // [ 'name', 'age', 'gender' ]
console.log(Object.values(obj)); // [ 'xpf', 25, 'male' ]
console.log(Object.entries(obj)); // [ [ 'name', 'xpf' ], [ 'age', 25 ], [ 'gender', 'male' ] ]
用途:将一个对象转换为Map集合
let obj = {
name:'xpf',
age:25,
gender:'male'
}
//let map = new Map(obj); // 直接放进去会报错
let map = new Map(Object.entries(obj));
console.log(map);
5、函数的扩展
1) 函数简写
1. 函数声明在对象中
let obj = {
name:'xpf',
sayName:function(){
console.log(1);
}
}
===>等价于
let obj = {
name:'xpf',
sayName(){
console.log(1);
}
}
2. 函数声明在参数中(回调函数/匿名函数)
forEach(function(){})
setInterval(function(){},1000)
ES6中允许使用箭头 => 定义函数
function(){}
===>等价于
()=>{}
let fun1 = function(item){
return item;
}
let fun2 = item => item;
console.log(fun1);
console.log(fun2);
箭头函数可以根据参数个数来省略(),可以根据函数体内部代码的行数来省略{}
一个参数时,可以省略()
item => {}
不要参数或者多个参数时,不可以省略()
() => {}
(item,index) => {}
函数体内部只有一行代码的时候,可以省略{}
多行代码时,不可以省略{}
案例一:
let arr = [{
name:'xpf',
gender:'male'
},{
name:'tom',
gender:'male'
},{
name:'xiaohong',
gender:'female'
}]
let res1 = arr.filter(function(item){
return item.gender == 'female';
})
let res2 = arr.filter( (item)=>{
return item.gender == 'female';
} )
console.log(res1);
console.log(res2);
案例二:
let obj = {
name:'xpf',
age:25,
sayName:()=>{
console.log(this.name);
},
sayAge:function(){
console.log(this.age);
}
}
obj.sayName(); // undefined
obj.sayAge(); // 25
注意:箭头函数中的this指向该箭头函数的外部函数
如果箭头函数外部没有函数,那么这个this就全局指向对象
案例三:
let obj = {
list:[1,2,3,4],
add(){
this.list.forEach(function(){
console.log(this.list); // undefined
})
}
}
obj.add();
解决方案一:手动更改this
let obj = {
list:[1,2,3,4],
add(){
var that = this;
this.list.forEach(function(){
console.log(that.list)
})
}
}
obj.add();
解决方案二:使用箭头函数
let obj = {
list:[1,2,3,4],
add(){
this.list.forEach(()=>{
console.log(this.list)
})
}
}
obj.add();
箭头函数的this先指向了外部的add函数,由于add函数被obj对象调用,所有add方法中的this指向了obj
最终,箭头函数中的this指向了obj
2) 函数参数的默认值
在ES6之前,一般无法为函数的参数提供默认值
function sayName(name,age,gender){
console.log(gender); // undefined
}
sayName(name,age)
如何设置默认值?
function person(name,age){
age = age || 15;
console.log(name,age);
}
person('xpf'); // xpf 15
person('xpf',25); // xpf 25
person('xpf',''); // xpf 15
在ES6时,可以使用解构给函数参数提供默认值
function sayName({name,age,gender='male'}){
console.log(gender); // male
}
sayName({})
应用:
1、ajax参数默认
2、可以规定函数必须要传递某一个参数(即该参数不可省略)
function throwErr(){
throw new Error('该参数不可被省略!')
}
function sayName({name = throwErr,age}){
console.log(name,age);
}
sayName({name:'xpf',age:25}); // xpf 25
sayName({name:'xpf'}); // xpf undefined
sayName({age:25}); // 抛出错误 25
3)参数rest ...变量名
1.函数参数中
在ES6之前,函数的所有参数存放在arguments中
function sayMsg(name,age,gender){
console.log(name,age,gender);
console.log(arguments,'---');
}
sayMsg('xpf',25,'male','安徽合肥');
...变量名,该变量是一个数组,该变量将函数多余的参数都存放在数组中,就不需要arguments对象了
function sayMsg(...params){
console.log(params); // [ 'xpf', 25, 'male', '安徽合肥' ]
}
sayMsg('xpf',25,'male','安徽合肥');
function sayMsg(name,age,...params){
console.log(name,age,params); // xpf 25 ['male','安徽合肥']
}
sayMsg('xpf',25,'male','安徽合肥');
注意:rest参数后面,不可以再有其他参数,否则会报错
function sayMsg(...params,name){} // 这个代码是错误的
2.在对象中 (剥离)
let obj1 = {
name:'xpf'
}
let obj2 = {
age:25,
gender:'male'
}
//如何将obj1与obj2合并?
//let obj = {obj1,obj2};
//console.log(obj);
//obj1.other = obj2;
//console.log(obj1);
//Object.assign(obj1,obj2);
//console.log(obj1);
///var obj = Object.assign({},obj1,obj2);
//console.log(obj);
let obj = {...obj1,...obj2};
console.log(obj); // { name: 'xpf', age: 25, gender: 'male' }
3.在vue中
computed:{
...mapState('home',['article'])
}
6、数组的扩展
1) Array.from(v)
用于将两类对象转换为真正的数组:类数组对象、可遍历对象(Set、Map)
数组的创建方式
1. 数组字面量
var arr = [1,2,3];
2. Array构造函数
var arr = new Array('a'); // ['a']
3. [...str]
let str = 'hello';
let arr = [...str]; // [ 'h', 'e', 'l', 'l', 'o' ]
4. Array.from(v)
v表示类数组对象、可遍历对象
1、类数组对象(DOM操作返回的NodeList集合、函数内部的arguments对象)
ES5的写法:
let arr_like = {
"0":"xpf",
"1":"male",
length:2
}
let arr = Array.prototype.slice.call(arr_like);
console.log(arr); // [ 'xpf', 'male' ]
ES6的写法:
let arr_like = {
"0":"xpf",
"1":"male",
length:2
}
let arr = Array.from(arr_like);
console.log(arr); // [ 'xpf', 'male' ]
2、可遍历对象
String
let str = 'hello';
let arr = Array.from(str); // [ 'h', 'e', 'l', 'l', 'o' ]
Set
let set = new Set([1,2,3,3,2,1]);
let arr = Array.from(set); // [ 1, 2, 3 ]
...
注意:
1、如果参数为一个真正的数组,Array.from会返回一个一模一样的新数组
2、任何包含lenght属性的对象,都可以通过Array.from方法转换为一个数组
2) Array.of()
为了弥补Array构造函数的不足,由于Array构造函数在传递一个数字类型的参数时,会出现与我们预期不一样的结果
Array构造函数
let arr1 = new Array(1,3); // [1,3]
let arr2 = new Array(3); // [ <3 empty items> ]
Array.of()
let arr3 = Array.of(1,3); // [1,3]
let arr4 = Array.of(3); // [3]
3) find()与findIndex()
Array.prototype.find()
找出第一个满足条件的元素并返回,如果没有满足条件的元素时,返回undefined
let arr = [1,2,3,4];
let res = arr.filter((item)=>{
return item < 3;
})
console.log(res); // [1,2]
let arr = [1,2,3,4];
let res = arr.find((item)=>{
return item < 3;
})
console.log(res); // 1
Array.prototype.findIndex()
返回第一个满足条件的元素的索引,如果没有满足条件的元素时,返回-1
let arr = [1,2,3,4];
let res = arr.findIndex((item)=>{
return item < 3;
})
console.log(res); // 0
let arr = [1,2,3,4];
let res = arr.findIndex((item)=>{
return item < 1;
})
console.log(res); // -1
数组有一个 indexOf()方法,能够找到元素首次出现时的索引,可以用来实现数组的去重
findIndex是为了弥补indexOf的不足
let arr = [1,2,3,NaN];
let res = arr.indexOf(NaN); // -1
也就是说,indexOf方法无法找出数组中的NaN元素
findIndex方法也不能直接找到NaN出现的位置,但是可以借助Object.is来实现
let arr = [1,2,3,NaN];
let res = arr.findIndex((item)=>{
return Object.is(item,NaN);
})
console.log(res); // 3
4) fill()
Array.prototype.fill()
使用给定值,填充数组
一个参数
let arr = [1,2,3];
arr.fill(4);
console.log(arr); // [4,4,4]
数组中原本已存在的元素,会被全部替换掉
三个参数
第一个参数 :填充的数
第二个参数 :填充的起始位置
第三个参数 :填充的结束位置(不包含)
let arr = [1,2,3,4,5,6];
arr.fill(7,2,4);
console.log(arr); // [1,2,7,7,5,6]
注意:
1、如果被填充的数组为空,被填充后仍然是空数组
let arr = [];
arr.fill(1);
console.log(arr); // []
2、如果填充的为对象,被赋值的只是同一个内存的指针
let arr = new Array(3);
arr.fill({name:'xpf'});
console.log(arr); // [ { name: 'xpf' }, { name: 'xpf' }, { name: 'xpf' } ]
arr[1].name = 'tom';
console.log(arr); // [ { name: 'tom' }, { name: 'tom' }, { name: 'tom' } ]
5) keys、values、entries
这三个方法都用于数组的遍历,返回的都是一个遍历器对象(迭代器对象),该遍历器对象可用于for-of循环
Array.prototype.keys()
对键名的遍历
Array.prototype.values()
对键值的遍历
Array.prototype.entries()
对键值对的遍历
let arr = ['tom','larry','xpf'];
console.log(arr.keys()); // Object [Array Iterator] {}
console.log(arr.values()); // Object [Array Iterator] {}
console.log(arr.entries()); // Object [Array Iterator] {}
案例一:
let arr = ['xpf','tom','larry'];
/*ES5的写法:
for(let key in arr){
console.log(arr[key]); // xpf tom larry
}
*/
//ES6的写法:
let values = arr.values();
for(let item of values){
console.log(item); // xpf tom larry
}
let entry = arr.entries();
for(let item of entry){
console.log(item,'---');
}
案例二:
let arr = ['xpf','tom','larry'];
let iterator = arr.values();
console.log(iterator.next()); // { value: 'xpf', done: false }
console.log(iterator.next()); // { value: 'tom', done: false }
console.log(iterator.next()); // { value: 'larry', done: false }
console.log(iterator.next()); // { value: undefined, done: true }
在遍历器对象中,提供一个next()方法,该方法会返回一个对象,表示当前数组的元素
value属性返回当前位置的元素
done属性是一个布尔值,表示遍历是否结束(即是否有必要再调用next方法)
案例三:
let arr = ['xpf','tom','larry];
//方法一
let value_iterator = arr.values();
let item;
while(!(item = value_iterator.next()).done){
console.log(item.value); // xpf tom larry
}
//方法二
let values = arr.values();
for(let item of values){
console.log(item); // xpf tom larry
}
//方法三
for(let key in arr){
console.log(arr[key]); // xpf tom larry
}
6) includes()
Array.prototype.includes()
表示数组是否包含给定的值,如果包含,返回true,否则返回false
includes是为了弥补indexOf方法的不足
1、不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观
2、它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判
let arr = [1,2,3,NaN];
console.log(arr.indexOf(NaN)); // -1
console.log(arr.includes(NaN)); // true
console.log(arr.includes(4)); // false
7、Set、Map集合
1) Set集合
类似于数组,但是成员的值都是唯一,不可重复
1. 定义方式:Set构造函数
let set = new Set();
console.log(set); // Set {} 在浏览器中会显示一个[]
2. 初始化:Set构造函数的参数必须为可遍历的数据
let set1 = new Set(1);
console.log(set1); // 1 is not iterable
let set2 = new Set([1,2,3]);
console.log(set2); // Set { 1, 2, 3 }
let set3 = new Set('hello');
console.log(set3); // Set { 'h', 'e', 'l', 'o' }
也可以使用Set集合中的add()方法
let set = new Set();
let arr = [1,2,3,4];
arr.forEach((item)=>{
set.add(item);
})
console.log(set); // Set { 1, 2, 3, 4 }
对比数组的push方法记忆
3. 特点:
1、Set构造函数的参数必须为可遍历的数据
2、Set集合不会含有重复的值
let set = new Set([1,2,3,3,2,1]);
console.log(set); // Set { 1, 2, 3 }
4. Set实例的属性
Set.prototype.size
返回Set集合的所有成员总数
let set = new Set([1,2,3,3,2,1]);
console.log(set); // Set { 1, 2, 3 }
console.log(set.size); // 3
Set.prototype.constructor
构造函数,也就是Set函数
let set = new Set([1,2,3,3,2,1]);
console.log(set.constructor); // [Function: Set]
5. Set实例的方法
Set.prototype.add(value)
向Set集合添加某个值,返回添加完值之后的Set集合
let set = new Set([1,2,3,3,2,1]);
set.add(4);
console.log(set); // Set { 1, 2, 3, 4 }
Set.prototype.delete(value)
删除Set集合中的某个值,删除成功时返回true,否则返回false
let set = new Set([1,2,3,3,2,1]);
let res = set.delete(3);
console.log(res); // true
let res1 = set.delete(3);
console.log(res1); // false
注意:在浏览器下,返回被删除值之后的Set集合
Set.prototype.has(value)
表示某个值是否属于Set集合,如果属于返回true,否则返回false
let set = new Set([1,2,3,3,2,1]);
let res = set.has(3);
console.log(res); // true
let res1 = set.has(4);
console.log(res1); // false
Set.prototype.clear()
清空Set集合中所有的成员
let set = new Set([1,2,3,3,2,1]);
set.clear();
console.log(set); // Set {}
Set.prototype.keys()
键名的遍历器
Set.prototype.values()
键值的遍历器
Set.prototype.entries()
键值对的遍历器
Set集合中没有键名,只有键值(键名和键值是同一个值),所以keys方法和values方法行为完全一致
let set = new Set(['tom','larry','xpf']);
//值的遍历器对象
let value_iterator = set.values();
//键的遍历器对象
let key_iterator = set.keys();
for(let val of value_iterator){
console.log(val); // tom larry xpf
}
for(let key of key_iterator){
console.log(key); // tom larry xpf
}
Set集合的实例默认可遍历,因为它的默认遍历器生成函数就是它values方法
let set = new Set([1,2,3,4]);
console.log(Set.prototype)
console.log(Set.prototype[Symbol.iterator])
console.log(Set.prototype.values)
console.log(Set.prototype[Symbol.iterator] === Set.prototype.values) // true
任何部署了iterator接口的数据,都可以完成遍历操作
let set = new Set(['tom','larry','xpf']);
for(let val of set){
console.log(val);
}
6.应用
1. 将Set集合转换为数组 --> Array.from()
let set = new Set([1,2,3,4]);
console.log(set); // Set { 1, 2, 3, 4 }
let arr = Array.from(set);
console.log(arr); // [1,2,3,4]
2. 数组的去重
方法一:
indexOf
let arr = [1,2,3,3,2,1];
let newArr = [];
for(let i = 0;i<arr.length;i++){
if(newArr.indexOf(arr[i])==-1){
newArr.push(arr[i])
}
}
console.log(newArr);
方法二:
function myFun(arr){
let set = new Set(arr);
let array = Array.from(set);
return array;
}
let arr = [1,2,3,3,2,1];
let newArr = myFun(arr);
console.log(newArr); // [1,2,3]
===>简化
function myFun(arr){
return Array.from(new Set(arr));
}
let arr = [1,2,3,3,2,1];
let newArr = myFun(arr);
console.log(newArr); // [1,2,3]
3.(拓展) 对Set集合中的元素做一些操作后返回(加减乘除) map
let arr = [1,2,3,4];
let newArr = arr.map((item)=>{
return item*2;
});
console.log(newArr); // [2,4,6,8]
在set集合中实现上述效果
方法一:
let set = new Set([1,2,3,4]);
let arr = Array.from(set,(item)=>{
return item*2;
})
var newSet = new Set(arr);
console.log(newSet); // Set { 2, 4, 6, 8 }
方法二:
let set = new Set([1,2,3,4]);
//通过扩展运算符将set集合转换为数组
let arr = [...set];
let res = arr.map((item)=>{
return item*2;
})
let newSet = new Set(res);
console.log(newSet); // Set { 2, 4, 6, 8 }
2)Map集合
JavaScript 的对象,本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制
为了解决这一局限性,ES6引入了Map集合
1) 定义方式:Map构造函数
let map = new Map();
console.log(map); // Map {}
2) 初始化:接受一个数组作为参数。该数组的成员是一个个表示键值对的数组
let map = new Map([
['name','xpf'],
['gender','male']
]);
console.log(map); // Map { 'name' => 'xpf', 'gender' => 'male' }
还可以通过set方法向Map集合中设置键值对
let map = new Map();
map.set('name','xpf');
map.set('gender','male');
console.log(map); // Map { 'name' => 'xpf', 'gender' => 'male' }
3) 特点
1. 类似于对象,也是键值对的集合
2. Object结构提供了'字符串—值'的对应,Map结构提供了'值—值'的对应
即Map集合的键不仅仅局限于字符串了
4) Map集合的属性
Map.prototype.size
返回Map集合的成员总数
let map = new Map();
map.set('name','xpf');
map.set('gender','male');
console.log(map.size); // 2
5) Map集合的方法
Map.prototype.set(key,value)
设置键值对,键名为key,键值为value,返回Map集合
Map.prototype.get(key)
获取key对应的键值,如果找不到,返回undefined
let map = new Map();
map.set('str',1);
map.set(2,1);
map.set(undefined,1);
map.set(function(){},1);
如果键名已存在,则键值会更新,否则就会就会生成新的键
map.set(2,5); // {2 => 5}
console.log(map.get('str')); // 1
console.log(map.get('str123')); // undefined
Map.prototype.has(key)
表示某个键是否属于Map集合,属于时返回true
Map.prototype.delete(key)
删除某个键,删除成功时返回true
Map.prototype.clear()
清空Map集合
Map.prototype.keys()
Map.prototype.values()
Map.prototype.entries()
Map.prototype.forEach()
遍历Map集合中的所有成员
6) 应用
1. Map集合转换为数组
let map = new Map([
[1,2],
[2,3],
[3,4]
]);
console.log(map); // { 1 => 2, 2 => 3, 3 => 4 }
方法一:通过Array.from()
let arr = Array.from(map);
console.log(arr); // [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ] ]
方法二:...
let map = new Map([
[1,2],
[2,3],
[3,4]
]);
console.log(map); // { 1 => 2, 2 => 3, 3 => 4 }
let arr = [...map];
console.log(arr); // [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ] ]
2. 对象转换为Map集合
思路:
Map构造函数的参数需要是二维数组
->
如何将普通对象转换为二维数组的形式?
->
Object.entries()可以拿到对象的键值对组成的数组
->
作为参数传递给new Map();
->
实现对象转Map集合
实现代码:
let obj = {
name:'xpf',
age:25,
gender:'male'
}
let entry = Object.entries(obj);
let map = new Map(entry);
console.log(map);
3. 购物车
// 1. 初始化购物车
let shopCar = new Map();
// 2. 初始化商品
let product_one = {
productId:1,
productName:'雪碧',
productPrice:4.5,
number:1
}
let product_two = {
productId:2,
productName:'可乐',
productPrice:4.0,
number:1
}
// 3. 初始化添加至购物车事件
function addShopCar(item){
console.log(item,'----');
// 判断该商品是否已经存在于购物车
// 如果有该id,说明已经存在该商品,只需要将该商品的数量改变即可
if(shopCar.has(item.productId)){
shopCar.get(item.productId).number += item.number;
} else {
shopCar.set(item.productId,item);
}
}
// 4. 购买雪碧
addShopCar(product_one);
//addShopCar(product_one);
//addShopCar(product_one);
//addShopCar(product_one);
// 再次购买雪碧
addShopCar({productId:1,productName:'雪碧',productPrice:4.5,number:3});
addShopCar({productId:1,productName:'雪碧',productPrice:4.5,number:1});
// 购买可乐
addShopCar(product_two);
console.log(shopCar);
3)(拓展)Iterator(遍历器)
1) 介绍
数组、对象、Map和Set四种数据结构,需要一种统一的访问机制
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
2) 作用
1. 为各种数据结构提供统一的访问接口
2. 使得数据结构的成员能够按某种次序排列
3. Iterator接口可以用于for-of循环
3) next()方法
在遍历器对象Iterator中,有一个next方法,该方法会返回一个对象,对象中有两个属性value、done
let arr = ['xpf','tom','larry'];
let iterator = arr.values();
console.log(iterator.next()); // xpf
console.log(iterator.next()); // tom
console.log(iterator.next()); // larry
console.log(iterator.next()); // undefined
4) 向类数组对象中添加遍历器
获取数组中的values方法
console.log(Array.prototype[Symbol.iterator])
console.log([][Symbol.iterator])
let arr_like = {
"0":"xpf",
"1":"25",
"2":"male",
length:3,
[Symbol.iterator]:[][Symbol.iterator]
}
console.log(arr_like);
for(let val of arr_like){
console.log(val);
}
function sayName(a,b){
console.log(arguments,'--')
for(let arg of arguments){
console.log(arg)
}
}
sayName('xpf','male',25);
注意:
1. 对于一般对象,就算添加了遍历器接口,也没有效果(不会报错,但是使用for-of遍历出来的值为undefined)
let obj = {
name:'xpf',
age:25,
gender:'male',
length:3,
[Symbol.iterator]:[][Symbol.iterator]
}
for(let val of arr_like){
console.log(val); // undefined undefined undefined
}
2. Symbol是一个新的数据类型,表示独一无二的值,是第七种数据类型
undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
let s = Symbol();
console.log(s); // Symbol()
console.log(typeof s); // 'symbol'
注意:
1、Symbol函数不可以使用new关键字,否则会报错
2、通过Symbol创建出来的变量,是独一无二的,这个变量可以用于向对象中添加一个属性,而不影响其他已存在的属性(即不会出现覆盖的情况)
向对象中添加symbol
let obj = {
name:'xpf',
age:25,
gender:'male'
}
let symbol1 = Symbol('province');
let symbol2 = Symbol('city');
let symbol3 = Symbol('city');
obj[symbol1] = '安徽';
obj[symbol2] = '合肥';
obj[symbol3] = '苏州';
console.log(obj);
console.log(obj.city); // undefined
console.log(obj['city']); // undefined
console.log(Object.getOwnPropertySymbols(obj));
Object.getOwnPropertySymbols(obj)用于获取对象中所有通过symbol添加的属性名
3. 获取遍历器
数组的遍历器对象
Array.prototype[Symbol.iterator]
[][Symbol.iterator]
类数组对象的遍历器对象
arguments[Symbol.iterator]
function sayName(a,b,c){
let i = arguments[Symbol.iterator]();
for(let item of i){
console.log(item)
}
}
sayName(1,2,3);
===>等价于
function sayName(a,b){
console.log(arguments,'--')
for(let arg of arguments){
console.log(arg)
}
}
sayName('xpf','male',25);
Set
let set = new Set();
set[Symbol.iterator]
8.Promise对象
9.(拓展)Generator函数
10.****async函数