原生AJAX1.ajax技术:可以在网页不跳转的情况下 向服务器发送请求 应用:局部刷新 2.ajax语法:使用内置对象XMLHttpRequst 请求方法get和post区别： 传参方式不同 get请求： 直接在url后面拼接参数 * 参数在url中，安全性不高 post请求： 1.需要设置请求头（固定语法）：xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded') * 注意：这是固定格式，错一个字母都不行，强烈建议复制粘贴 2.使用xhr的send方法发送参数： xhr.send('参数名=参数值'); * 注意：不要加前面的? get请求参数//(1)创建XMLHttpRequest对象(小黄人) const xhr = new XMLHttpRequest() //(2)设置请求方法和地址 xhr.open('get', 'https://autumnfish.cn/api/joke') //(3)发送请求 xhr.send() //(4)注册响应回调事件 /* 这个事件不是立即执行,而是过一会执行. 取决于很多因素:带宽 天气 服务器 网络运营商 */ xhr.onload = function () { console.log(xhr.responseText); }post请求//(1).实例化ajax对象 let xhr = new XMLHttpRequest() //(2).设置请求方法和地址 xhr.open('post', 'https://autumnfish.cn/api/user/register') //(3).设置请求头（post请求才需要设置） xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') //(4).发送请求 ： 参数格式 'key=value' xhr.send('key=value') //(5).注册回调函数 xhr.onload = function () { console.log(xhr.responseText) } ajax工作原理http协议 : 网络传输协议* 协议 : 规定网络数据传输格式 http协议组成 : 请求报文 + 响应报文* 浏览器发请求 必须是 ： 请求报文 * 服务器响应 必须是 ： 响应报文 请求报文 由三部分组成3.1 请求行 : 请求方法 + 请求地址 3.2 请求头 : 浏览器告诉服务器,我发送给你的数据是什么格式 (文本、json、图片、音视频等等) 3.3 请求体 : 请求参数 响应报文 由三部分组成4.1 响应行 : 服务器状态码、 服务器地址等2xx : 请求成功 200:成功 3xx : 重定向 302 服务端重定向(服务器主动修改浏览器网址) 4xx : 前端的问题 404 : url错误 400: 参数错误 403/402 : 没有权限 405：请求方法错误 5xx : 服务器的问题 500 服务器挂了(后台java代码像js一样爆红了)4.2 响应头 : 服务器告诉浏览器，我响应给你的数据是什么格式 (文本、json、图片、音视频等等) 4.3 响应体 : 服务器响应数据 5.ajax原理 ： 发送请求报文过程* 给服务器发请求，其实发的就是请求报文 从网页输入url到呈现过程 1.DNS解析 : 把域名解析成ip地址 2.TCP连接三次握手 : 建立 安全可靠的传输协议 2.1 什么是TCP : 一种 传输控制协议 2.2 TCP作用 : 保证HTTP网络传输是 安全 + 可靠的 （检测客户端 与 服务器的网卡是不是通的） 2.3 TCP三次握手 : 第一次: 浏览器 -> 服务器 (你能听到我说话吗?,检测浏览器:发送) 第二次: 服务器 -> 浏览器 (我听到了,你能听到我说话吗。 检测浏服务器: 接收 + 发送) 第三次: 浏览器 -> 服务器 (嗯,我听到了. 检测浏览器: 接收) 3.HTTP请求 : 网络传输协议 3.1 浏览器发送请求 3.2 服务器处理请求 3.3 服务器响应请求 4.服务器响应数据之后,渲染引擎开始渲染页面 4.1 解析html生成：dom树 4.2 解析css生成：样式树 4.3 dom树 与 样式树 合并得到 渲染树 4.4 呈现页面 AXIOS框架axios语法 : axios .get('url') .then( res=>{ //请求成功 } ) .catch( err=>{ //请求错误} ) .then( ()=>{ //请求完成 } )axios基本使用//基本使用 document.querySelector('.btn1').onclick = function() { axios({ url: "https://autumnfish.cn/api/joke/list?num=10", method: "get" }).then(res => { console.log(res) }) } //get请求 document.querySelector('.btn2').onclick = function() { axios({ url: `https://autumnfish.cn/api/joke/list`, method: "get", params:{ num:10 } }).then(res => { console.log(res) }) } //post请求 document.querySelector('.btn3').onclick = function() { axios({ url: "http://www.liulongbin.top:3009/api/login", method: "post", data: { username: "admin", password: "123456" } }).then(res => { console.log(res) }) }第三钟写法axios({ url:'请求路径', method:'get', data: { 'post请求参数'}, params: { 'get请求参数'}}).then(res=>{ //成功回调 console.log(res)})js链式语法/* 1.链式语法 : 对象可以连续调用方法 axios.get().then().catch() 2.链式语法底层原理 : 在对象的方法中返回自身 */ let obj = { name:'张三', age:20, sex:'男' } //给对象添加方法 obj.setName = function(str){ this.name = str return this } obj.setAge = function(num){ this.age = num return this } obj.setSex = function(str){ this.sex = str return this } //调用方法 obj .setName('李四') .setAge(22) .setSex('女') console.log( obj )查询参数和请求体查询参数 get delete (params)请求体 post put patch (data)FormData 对象/*文件上传 1.前置知识点 1.1 file表单, 默认自带点击事件，作用是选择文件 1.2 上传文件‘必须’要使用原生内置的FormData对象 (1)文件需要设置单独的请求头 : multipart/form-data (2)文件以二进制方式传输（文本是utf8编码，但是文件不是） 1.3 file表单有一个特殊的事件onchange事件 ： 用户选择了文件就会执行 2.文件上传流程 2.1 在file表单的onchange事件中获取用户选择的图片 this.files[0] 2.2 创建FormData对象,将图片添加到fd对象中 let fd = new FormData() fd.append('接口参数',文件数据) 2.3 ajax发送请求 : 参数必须是formdata对象 */ onreadystatechangeXMLHttpRequest对象的两个事件 + xhr对象的请求状态 1. onload事件 : 服务器响应之后执行 ( 一次请求,只会执行一次 ) 2. onreadystatechange事件 : xhr请求状态变化会执行 ( 一次请求，会执行多次 ) 0: 请求未初始化 (open之前) 1: 服务器连接已建立 (open之后) 2: 请求已接收 ( 服务器已经收到你的请求 ) 3: 请求处理中 ( 服务器正在处理你的请求 ) 4: 请求已完成，且响应已就绪 ( 服务器完成响应, onload事件就是在这里执行 )let xhr = new XMLHttpRequest() //请求未初始化 console.log( xhr.readyState )//0 //(2).设置请求方法和地址(请求行) xhr.open('post', 'http://www.liulongbin.top:3009/api/login') //服务器连接已建立 console.log( xhr.readyState )//1 //(3).设置请求头（post请求才需要设置） xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded') //(4).发送请求 ： 参数格式 'key=value' xhr.send('username=admin&password=123456') //(5).注册回调函数 xhr.onreadystatechange = function () { console.log( xhr.readyState )//2 3 4 //当readyState为4,表示响应数据了 if( xhr.readyState == 4 ){ console.log( xhr.response ) } } axios拦截器1.axios拦截器作用 : 拦截 请求 + 响应 2.axios拦截器执行流程 2.1 axios发送请求 2.2 执行请求拦截器 (发送服务器之前执行) 2.3 发送给服务器 2.4 服务器响应请求 2.5 执行响应拦截器 (then方法之前执行) 2.6 执行axios的then方法 // 添加请求拦截器axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么 return config;}, function (error) {// 对请求错误做些什么 return Promise.reject(error);});// 添加响应拦截器axios.interceptors.response.use(function (response) {// 对响应数据做点什么 return response;}, function (error) {// 对响应错误做点什么 return Promise.reject(error);});函数防抖 1.函数防抖 : 单位时间内，频繁触发一个事件， 以'最后一次'触发为准 2.防抖应用场景: 输入框输入事件 3.防抖流程 : 3.1 声明一个全局变量存储定时器ID 3.2 每一次触发交互的时候，先清除上一次定时器。 然后把本次事件处理代码放入定时器中let timeID = null //输入框输入事件 document.querySelector('input').oninput = function(){ //1.先清除之前的定时器 clearTimeout(timeID) //2.开启本次定时器 timeID = setTimeout(()=>{ //function函数： this->window //箭头函数 this->上级this 表单 console.log(`发送ajax,搜索内容是${this.value}`) },500) } 函数节流1.函数节流 : 单位时间内，频繁触发一个事件， 只会触发一次 2.节流场景 : 解决高频事件 （滚动条事件高频触发） 3.节流实现: 3.1 声明一个全局变量存储触发时间 let lastTime = null 3.2 每一次触发事件，获取当前时间 let currentTime = Date.now() 3.3 判断 当前时间 与 上一次触发时间，是否超过间隔 currentTime-lastTime>=500 3.4 如果超过触发间隔，则执行事件处理代码。 然后存储本次触发事件 lastTime = currentTime //声明一个全局变量存储触发时间 let lastTime = null window.onscroll = function(){ //1.每一次触发，先获取本次时间戳 let currentTime = Date.now() //2.判断当前时间 与 上一次触发时间 是否超过间隔 if( currentTime - lastTime >= 500 ){ console.log( document.documentElement.scrollTop) //3.存储本次触发时间 lastTime = currentTime } }防抖---王者荣耀回城 节流---王者荣耀释放技能
reduce方法是JavaScript中一个比较强大的方法，可能在平时开发中，有人根本没用过，通过下面的8个例子，学会reduce的用法以及它的常用场景。reduce方法是一个数组的迭代方法，和map、filter不同，reduce方法可缓存一个变量，迭代时我们可以操作这个变量，然后返回它。这是我大白话的解释，可能还是不容易理解，下面看例子吧1. 数组累加数组累加是项目经常遇到的，比如计算商品总价等，使用reduce就可以一行代码搞定，而且不用定义外部变量，reduce是完全无副作用的函数。// 累加[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a + i);// 输出：36// 累加，默认一个初始值[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a + i, 5);// 输出：41// 累乘[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a * i);// 输出：403202. 找出数组最大值在数组每次的迭代中，我们使用Math.max获取最大值并返回，最后我们将得到数组所有项目的最大值。[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => Math.max(a, i));当然如果数组每项都是数字我们可以使用...展开运算符和Math.max配合。Math.max(...[1, 2, 3, 4, 5, 6, 7, 8]);3. 处理不规则数组通过map和reduce配合使用，返回每个子数组拼接好的结果。let data = [ ["红色","128g", "苹果手机"], ["南北","两室一厅","128㎡","洋房住宅"], ["小米","白色","智能运动手表","心率血压血氧","来电信息提醒"], ["官方发售","2020年秋季","篮球","球鞋","品牌直邮"]]let dataConcat = data.map(item=>item.reduce((a,i)=>${a} ${i}))// 输出结果：["红色 128g 苹果手机""南北 两室一厅 128㎡ 洋房住宅""小米 白色 智能运动手表 心率血压血氧 来电信息提醒""官方发售 2020年秋季 篮球 球鞋 品牌直邮"]4. 删除数据重复项检查当前迭代项是否存在，如果不存在添加到数组中。let array = [1, 2, 3, 'a', 'b', 'c', 1, 2, 3, 'a', 'b', 'c'];array.reduce((noDupes, curVal) => { if (noDupes.indexOf(curVal) === -1) { noDupes.push(curVal) } return noDupes}, [])// 输出：[1, 2, 3, 'a', 'b', 'c']5. 验证括号是否合法这是一个很巧妙的用法，我在dev.to上看到的用法。如果结果等于0说明，括号数量是合法的。[..."(())()(()())"].reduce((a,i)=> i === '(' ? a+1 : a-1 , 0);// 输出：0// 使用循环方式let status=0for (let i of [..."(())()(()())"]) { if(i === "(")status = status + 1else if(i === ")")status = status - 1if (status < 0) {break;}}6. 按属性分组按照指定key将数据进行分组，这里我用国家字段分组，在每次迭代过程中检查当前国家是否存在，如果不存在创建一个数组，将数据插入到数组中。并返回数组。let obj = [ {name: '张三', job: '数据分析师', country: '中国'}, {name: '艾斯', job: '科学家', country: '中国'}, {name: '雷尔', job: '科学家', country: '美国'}, {name: '鲍勃', job: '软件工程师', country: '印度'},]obj.reduce((group, curP) => { let newkey = curP['country'] if(!group[newkey]){group[newkey]=[]} group[newkey].push(curP) return group}, [])// 输出：[ 中国: [{…}, {…}] 印度: [{…}] 美国: [{…}] ]7. 数组扁平化这里展示的数组只有一级深度，如果数组是多级可以使用递归来进行处理[[3, 4, 5], [2, 5, 3], [4, 5, 6]].reduce((singleArr, nextArray) => singleArr.concat(nextArray), [])// 输出：[3, 4, 5, 2, 5, 3, 4, 5, 6]当然也可以使用ES6的.flat方法替代[ [3, 4, 5],[2, 5, 3], [4, 5, 6]].flat();8. 反转字符串这也是一种很奇妙的实现方法[..."hello world"].reduce((a,v) => v+a)或者[..."hello world"].reverse().join('')最后通过上面的了解，可以看到reduce的强大用法。通过使用reduce更好的简化代码，抽象代码。
map是映射,forEach是遍历map有返回值,返回的是一个新数组,forEach没有returnlet和const声明变量关键字 1. ES5声明变量 var 1.1 有变量预解析 1.2 没有块级作用域 2.ES6声明变量 let const 1.1 没有变量预解析 1.2 有块级作用域 3. let与const区别 let : 变量。 可以修改 const : 常量。 不可以修改 * 实际开发中, 只要不修改的变量都用const,需要改的时候用let 1. ES5声明变量 var 1.1 预解析 : 变量的声明会提升到当前作用域最顶端 console.log(num) var num = 10 var num console.log( num ) num = 10 1.2 没有块级作用域 : for (var i = 1; i <= 5; i++) {} console.log(i) 对象解构赋值解构赋值 ： 变量赋值的简写(解构精髓：当变量名 和 对象属性值一致的时候，只需要写一个) 1. 取出对象的属性 赋值 给 变量 let { name,age,sex } = obj 2. 取出变量的属性 赋值 给 对象 let obj = { name,// name : name age, sex } let obj = { name:'张三', age:20, sex:'男' } 1. 取出对象的属性 赋值 给 变量 ES5 let name = obj.name let age = obj.age let sex = obj.sex let score = obj.score ES6 let {name,age,sex,score} = obj console.log(name,age,sex,score)//'张三',20,'男',undefined 2. 取出变量的属性 赋值 给 对象 let username = 'admin' let password = '123456' ES5 let p = { username:username, password:password } ES6 let p = { username,// username:username password, say(){ console.log(111) }//say:function(){} } console.log(p) p.say() 数组解构数组解构 1.取出数组元素 赋值给变量 2.取出变量的值 赋值给数组元素 let arr = [10, 20, 30] //ES5 // let n1 = arr[0] // let n2 = arr[1] // let n3 = arr[2] // let n4 = arr[3] //ES6 let [n1, n2, n3, n4] = arr console.log(n1, n2, n3, n4) //10 20 30 undefined //取出变量的值 赋值给数组 let num1 = 1 let num2 = 2 let arr1 = [num1, num2] let newArr = [1, 2, 3, 4, 5, 6, 7, 8] let [a, b, c, d, e, f, g] = newArr console.log(a, b, c, d, e, f, g);函数参数解构函数参数解构: 当函数参数是对象类型,就可以对形参进行解构 */ //传参本质： 实参给形参赋值 function fn(obj){// let obj = {name:'张三',age:20,sex:'男'} console.log( obj ) //对函数形参进行解构 let {name,sex,age} = obj console.log( name,sex,age ) } function fn1( {name,sex,age} ){// let {name,sex,age} = {name:'张三',age:20,sex:'男'} console.log( name,sex,age ) } fn( {name:'张三',age:20,sex:'男'} ) fn1( {name:'张三',age:20,sex:'男'} ) 箭头函数1.箭头函数 : 相当于 function函数的简写 (1)去掉function,改成 箭头 => (2)形参小括号写到箭头左边 2.箭头函数语法注意点 2.1 如果箭头函数只有一个形参,则可以省略小括号(在原型链和构造函数上不能使用箭头函数) 2.2 如果箭头函数的 函数体 只有一行代码，则可以省略大括号。 （此时必须省略return） 3.箭头函数this : 没有this(也没有arguments) * 箭头函数本质是访问 上级作用域中的this */ let fn = (a,b) => { console.log(a+b) } fn(10,20) //(1)如果箭头函数只有一个形参,则可以省略小括号 let fn1 = a=>{ console.log(a*2) } fn1(66) //(2)如果箭头函数的 函数体 只有一行代码，则可以省略大括号。 （此时必须省略return） let fn2 = a=>a*2 let res = fn2(88) console.log(res)//由于箭头函数没有this,所以箭头函数不能作为构造函数 (new会修改this指向,而箭头没有this) // new fn() //箭头函数也无法修改this (call apply bind)对箭头函数是无效的 fn.call({name:'张三'}) //由于箭头函数没有this,所以箭头函数一般不作为事件处理函数 ## 展开运算符 ## 1.展开运算符: ... 相当于对象遍历的简写 2.应用 2.1 连接两个数组 2.2 求数组最大值 let arr1 = [10,20,30] let arr2 = [40,50,60] //ES5 : concat() // let arr = arr1.concat(arr2) // arr.push(70) //ES6 let arr = [...arr1,...arr2,70] console.log( arr ) //求数组最大值 let max = Math.max(...arr1,...arr2) console.log( max ) reducereduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数，每一次运行 reducer 会将先前元素的计算结果作为参数传入，最后将其结果汇总为单个返回值。第一次执行回调函数时，不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行，则需要传递初始值。否则，数组索引为 0 的元素将被作为初始值 initialValue，迭代器将从第二个元素开始执行（索引为 1 而不是 0）。下面的例子能够帮助你理解 reduce() 的用处——计算数组所有元素的总和//用reduce进行累加 let arr3 = [1, 2, 3, 4, 5] let lo = arr3.reduce((pre, next) => { return pre + next }) console.log(lo); // 二维数组转成一维数组 // let flattened = [ // [0, 1], // [2, 3], // [4, 5] // ].reduce( // (previousValue, currentValue) => { // return previousValue.concat(currentValue) // } // ) // console.log(flattened); reduce第一个参数：回调 (上一次值,当前值,当前下标)=>{} * 默认下标不是从0开始，而是从1开始。 开发中一般需要设置默认值 * return 值 就是下一次 sum的值 第二个参数: 初始值 * 一般需要设置初始值为0, 如果不设置遇到空数组则会报错 reduce方法返回值是 ： 最后一次sum的结果 */ // let res = arr.reduce( ( sum,item,index )=>{ // console.log( sum,item,index) // return sum + item // } , 0 ) 数组findIndex方法 1.数组findIndex方法作用: 查找元素的下标 * 数组的findIndex与indexOf异同点 相同点：功能一致，都是查找元素下标。 有则返回下标，无则返回固定值-1 不同点：应用场景不同 indexOf : 查找数组中的元素都是值类型 findIndex : 查找数组中的元素都是引用类型 2. findIndex方法特点 2.1 回调函数执行次数 != 数组长度 2.2 回调函数内部的return * return true : 循环结束。 找到了,此时返回值就是下标 * return false : 循环继续。 没找到，循环继续。 如果所有元素全部遍历还是没找到，最终结果就是-1 2.3 some方法的返回值 * 元素下标 或者 -1 数组every1.数组every方法作用: 判断数组中 是否所有的 元素都满足条件 * 应用场景: 开关思想 (购物车全选框) * 举例 ： 判断数组中是不是都是正数 2. every方法特点 2.1 回调函数执行次数 != 数组长度 2.2 回调函数内部的return * return true : 循环继续, 满足条件,如果所有的元素全部遍历还是true,则最终的结果就是true * return false : 循环结束。 只要要到不满足条件的元素，循环结束。最终的结果false 2.3 every方法的返回值 * true : 所有的元素 都符合条件 * false : 有元素 不符合条件数组some方法1.数组some方法作用: 判断数组中是否有符合条件的元素 * 应用场景: 非空判断（判断多个表单元素，有没有空文本） * 举例 ： 判断数组中有没有负数 2. some方法特点 2.1 回调函数执行次数 != 数组长度 2.2 回调函数内部的return * return true : 循环结束。 找到了满足条件的元素 * return false : 循环继续。 没找到，循环继续。 如果所有元素全部遍历还是没找到，最终结果就是false 2.3 some方法的返回值 * true : 有符合条件的元素 * false : 没有符合条件的元素数组forEach1.数组forEach方法作用: 遍历数组 * 应用场景: 和 for(let i=0;i<arr.length;i++){} 功能一致 2. forEach方法特点 2.1 回调函数执行次数 == 数组长度 2.2 回调函数内部的return * 没有返回值 2.3 forEach方法的返回值 * 没有返回值数组filter 1.数组filter方法作用: 筛选数组 * 应用场景: 筛选数组,将符合条件的元素放入新数组中 * 举例： 找出数组中所有大于10的元素 2. filter方法特点 2.1 回调函数执行次数 == 数组长度 2.2 回调函数内部的return * return true : 符合筛选条件，放入新数组中 * return false : 不符合筛选条件，不放入新数组中 2.3 filter方法的返回值 * 返回筛选之后的新数组数组 map1.数组map方法作用: 映射数组 说人话：按照某种映射关系, 把数组的每一个元素给修改了 举例：全场8折: 数组的每一个元素 * 0.8 2. map方法特点 2.1 回调函数执行次数 == 数组长度 2.2 回调函数内部的return * return 新数组的元素 * 如果没有return, 则map的返回值都是undefined 2.3 map方法的返回值 * 返回映射之后的新数组 Array.from 伪数组转真数组for-in与for-of区别 es6的模块化前端模块化的发展es6之前：amd规范cmd规范es6之后:● es6模块化 ○ 每个 js 文件都是一个独立的模块 ○ 导入其它模块使用 import 关键字 ○ 向外共享模块使用 export 关键字● es6模块化 ○ 每个 js 文件都是一个独立的模块 ○ 导入其它模块使用 import 关键字 ○ 向外共享模块使用 export 关键字让nodejs支持ES6模块化node.js 中默认支持 CommonJS 模块化规范,让node.js支持ES6的模块化语法的话要确保node.js更新到最新稳定版.在 package.json 的根节点中添加 "type": "module"节点(不添加默认就是CommonJs语法)注意 完成配置后,就只能使用ES6的模块语法了,不能再使用CommonJS语法.ES6模板化的内容默认导入导出默认导出的语法： export default 默认导出的成员 默认导入的语法： import 接收名称 from '模块路径注意每个模块中，只允许使用唯一的一次 export default默认导入时的接收名称可以任意名称，只要是合法的成员名称即可按需导入导出按需导出的语法： export const s1 = 10 按需导入的语法： import { 按需导入的名称 } from '模块路径'注意每个模块中可以有多次按需导出按需导入的成员名称必须和按需导出的名称保持一致按需导入时，可以使用 as 关键字进行重命名默认导出和整体导出一起使用es6导入导出总结让chrome浏览器支持es6模块化文件目录- index.html # 引入 index.js - index.js # 引入 tool.js - tool.jsindex.html 注意这里的module index.jsimport {a,b} from './tool.js' console.log(a,b)
1、Map是什么Map类型是键值对的有序列表，而键和值都可以是任意类型Map与Set的区别Set是一种叫做集合的数据结构，Map是一种叫做字典的数据结构集合-----是由一堆无序的、相关联的，且不重复的内存结构【数学中称为元素】组成的组合字典-----是一些元素的集合。每个元素有一个称作key 的域，不同元素的key 各不相同Set集合是以[值，值]的形式存储元素，且值不能重复Map字典是以[键，值]的形式存储2、增删改查2.1sizesize属性返回 Map 结构的成员总数。const map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 22.2 set()设置键名key对应的键值为value，然后返回整个 Map 结构如果key已经有值，则键值会被更新，否则就新生成该键同时返回的是当前Map对象，可采用链式写法const m = new Map(); let fn = function(){} m.set('edition', 6) // 键是字符串 m.set(fn, 'standard') // 键是函数 m.set(undefined, 'nah') // 键是 undefined m.set(1, 'a').set(2, 'b').set(3, 'c') // 链式操作 2.3 get()get方法读取key对应的键值，如果找不到key，返回undefinedconst m = new Map(); const hello = function() {console.log('hello');}; m.set(hello, 'Hello ES6!') // 键是函数 m.get(hello) // Hello ES6!2.4 has()has方法返回一个布尔值，表示某个键是否在当前 Map 对象之中const m = new Map(); m.set('edition', 6); m.set(262, 'standard'); m.set(undefined, 'nah'); m.has('edition') // true m.has('years') // false m.has(undefined) // true2.4 delete()delete方法删除某个键，返回true。如果删除失败，返回falseconst m = new Map(); m.set(undefined, 'nah'); m.has(undefined) // true m.delete(undefined) m.has(undefined) // false2.5 clear()clear方法清除所有成员，没有返回值let map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2 map.clear() map.size // 03、遍历keys()：返回键名的遍历器values()：返回键值的遍历器entries()：返回所有成员的遍历器forEach()：遍历 Map 的所有成员const map = new Map([ ['F', 'no'], ['T', 'yes'], ]); //keys() for (let key of map.keys()) { console.log(key); // "F" "T" } //values() for (let value of map.values()) { console.log(value); // "no" "yes" } // entries() for (let item of map.entries()) { console.log(item[0], item[1]); } // "F" "no" 、"T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" 、"T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" 、 "T" "yes" map.forEach(function(value, key, map) { console.log(key, value); // "F" "no" 、 "T" "yes" });