ES6
自动严格模式
//使用严格模式 必须写在最上面
"use strict"
1. 使用严格模式后,变量必须使用var 或 let定义,不能直接写变量
2. this在函数中严格模式时(最顶层不是在window),不再指向window,而指向undefined
//严格模式下
function fn(){
console.log(this)
}
fn()//undefined
var arr=[1,2,3,4]
arr.forEach(function(item){
console.log(this)//全是undefined
})
//去掉严格模式时
var a=1
function fn(){
var a=2
console.log(this.a+a)//3
}
//此时this指向a=1 所以得 1+2=3
fn()
//严格模式时,如果call或apply传入的第一个参数是什么就指向什么
function fn(){
console.log(this)
}
fn.call(null)//null
fn.call(undefined)//undefined
fn.call(1)//1
//非严格模式下null、undefined的this指向window,this指向引用型数值对象1
function fn(){
console.log(this)
}
fn.call(null)//window
fn.call(undefined)//window
fn.call(1)//Number{1}
3.禁止使用argument.callee,fn.caller
//严格模式下
function fn(){
console.log(arguments.callee)//报错
console.log(fn.caller)//报错
}
fn()
4.禁止使用with
var obj={
o:{
a:1,
b:2
}
}
console.log(obj.o.a)//1
//with非严格模式下只能修改该对象下已有的属性,如果不是已有默认创建一个全局变量
//严格模式下会报错
with(obj.o){
a=3;
b=4;
c=5;
}
console.log(c)//5
5.禁止使用o作为八进制写法
var a=o5;//早期使用,现在被禁止
var a=0o5;//现在可以使用的
6.禁止使用eval(官方文档是禁止重新定义eval和将eval的内容指向顶层)
var o={a:1}
var a=1;
var b=2;
//var eval=3;//报错
//将字符串转换为js语句
//效率不高
console.log(eval("o"))//{a:1}
console.log(eval("a+b"))//3
//eval不能转换函数 反射
var fn = eval("function fn({console.log('aaa')}")
console.log(fn)//undefined
//反射
function getValue(a, b, type) {
return (eval(a + type + b))
}
var sum = getValue(3, 5, "-")
console.log(sum)//-2
字符串模板
var str="a"
var str1='a'
var str2=`a`//字符串模板
//双引号和单引号 无法换行
var str="<div>
</div>" //报错
//使用字符串模板可以换行
var str=`<div>
<span></span>
</div>`
//双引号和单引号如果和变量组合使用,就必须使用字符串拼接才可以连接
//字符串模板使用${ }
var age = 30;
var str = "我今年" + age + "岁了"
var str1 = `我今年${age}岁了`
console.log(str, str1)
案例1
var arr = [
{ href: "http://www.baidu.com", site: "百度" },
{ href: "http://www.tencent.com", site: "腾讯" },
{ href: "http://www.taobao.com", site: "淘宝" },
{ href: "http://www.jd.com", site: "京东" },
{ href: "http://www.163.com", site: "网易" }
]
var div1 = document.getElementById("div1")
// reduce方式
/div1.innerHTML = `<ul>
${arr.reduce(function (value, item) {
return value + `<li><a href='${item.href} '>${item.site}</a></li>`
}, "")}
</ul>`
// map方式
div1.innerHTML = `<ul>
${arr.map(function (item) {
return `<li><a href='${item.href}'>${item.site}</a></li>`
}).join("")}
</ul>`
案例2
var arr = [
{ content: "商品1", show: true },
{ content: "商品2", show: false },
{ content: "商品3", show: true },
]
div1.innerHTML = `<ul>
${(function () {
var str = ``;
for (var i = 0; i < arr.length; i++) {
str += `<li style='display:${arr[i].show ? "block" : "none"}'>${arr[i].content
}</li>`
}
return str;
})()}
</ul>`
//方式2 把函数写在外面调用
div1.innerHTML = `<ul>
${fn()}
</ul>`
function fn() {
var str = ``;
for (var i = 0; i < arr.length; i++) {
str += `<li style='display:${arr[i].show ? "block" : "none"}'>${arr[i].content}</li>`
}
return str;
}
解构赋值
//这就是解构赋值
//原来是一个数组 通过解构之后 把里面的对象给一个个拿出来了
var arr = [
{ x: 10, y: 20 },
{ x: 30, y: 40 }
]
var [point1, point2] = arr
console.log(point1)//{x: 10, y: 20}
console.log(point2)//{x: 30, y: 40}
数组解构
var arr = [1, 2]
//将数组中的元素按位赋值给[]里面的变量
var [a, b] = arr
console.log(a, b)//1 2
//定义的 变量名要保持一致 即 上面是arr 下面也得是arr
var arr = ["张三", "李四"]
var [name1, name2] = arr
console.log(name1, name2)//张三 李四
交换变量
//交换变量
var x = 1;
var y = 2;
var arr = [x,y];//此处一定要加分号
[y,x] = arr;
// [x,y]=[y,x] 这个式子等同于上面两行
console.log(x, y)// 2 1
默认值
var [x,y]=[1,2,3]//1 2
var [x,y,z]=[1,2]//1 2 undefined
var [x,y,z=0]=[1,2]//1 2 0 虽然z没有给值 但是前面赋给了默认值所以是0
var [x,y,z=0]=[1,2,3]//1 2 3 z给值了 0会被覆盖
console.log(x,y,z)
//函数传参和上面一样
function fn(a,b,c=0){
console.log(a,b,c)//1 2 0
}
fn(1,2)
多维数组
//会把数组中的值一个一个拿出来
var [a, b, [c, d, [e, f]]] = [1, 2, [4, 5, [6, 7]]]
console.log(a, c, b, d, e, f)//1 4 2 5 6 7
var [a,b,[c,d,arr]]=[1,2,[4,5,[6,7]]];
console.log(arr)//(2) [6, 7]
对象解构
var point1={x:10,y:20}
var {x,y}=point1
console.log(x,y)//10 20
//对象按属性名解构,不分前后顺序
//所以不会交换
var {y,x}=point1
console.log(x,y)//10 20
默认值
var o = { name: "张三", age: 20, sex: "男" }
var { name, age, sex } = o
console.log(name, age, sex)//张三 20 男
//没有age属性
var {name,sex}={name:"张三",age:20,sex:"男"}
console.log(name,sex)//张三 男
//给age属性赋默认值0
var {name,age=0,sex}={name:"张三",age:20,sex:"男"}
console.log(name,age,sex)//张三 20 男
function fn({ name, sex, age }) {
console.log(name, sex, age)
}
fn({ name: "张三", sex: "男", age: 30 })//张三 男 30
function fn({ name, sex, age = 0 }) {
console.log(name, sex, age)
}
fn({ name: "张三", age: 40 })//张三 undefined 0
函数中对象解构做为参数
//在函数中如果参数是对象解构,使用函数传参时,可以不按照顺序传入内容,对象中的属性名对应就可以
//更灵活了 不用满足实参 形参一一对应
function fn({name,sex="男",age}){
console.log(name,sex,age)
}
fn({name:"张三",age:40,sex:"女"})//张三 女 40
多复杂型
var obj = {
a: 1,
b: 2,
c: {
d: 3,
e: 4
}
}
var {a,b,c:{d,e}}=obj
//c被解构为后面的{d,e} c是不存在的
console.log(a,b,d,e)//1 2 3 4
//重新将c从对象中单独解构出来
var {a,b,c:{d,e},c}=obj;
console.log(a,b,c,d,e)//1 2 {d: 3, e: 4}3 4
var obj = {
a: 1,
b: 2,
c: {
d: [3, 4],
e: [5, 6]
}
}
var { a, b, c: { d: [x, y], e: [m, n] } } = obj
console.log(a, b, x, y, m, n)//1 2 3 4 5 6
var { a, b, c: { d: [x, y], e: [m, n] ,d,e},c } = obj
console.log(a, b, x, y, m, n,c,d,e)//1 2 3 4 5 6 {d: Array(2), e: Array(2)} (2) [3, 4] (2) [5, 6]
有重复属性名
//因为对象是按照属性名解构,要求变量名必须和属性名一致,多个对象中如果有同名的属性,这时候需要给变量 起别名
var arr = [
{ x: 10, y: 20 },
{ x: 30, y: 40 }
]
var [{ x, y }, { x: x1, y: y1 }] = arr
console.log(x, y, x1, y1)//10 20 30 40
var obj = {
a: 1,
b: 2,
c: {
a: 3,
b: 4
}
}
var { a, b, c: { a: a1, b: b1 } } = obj
console.log(a, b, a1, b1)//1 2 3 4
起别名遇到默认值
//b没赋值 但是有默认值0 就使用默认值
var { a, b, c: { a: a1, b: b1 = 0 } } = { a: 1, b: 2, c: { a: 3 } }
console.log(a, b, a1, b1)//1 2 3 0
//b被赋值了 默认值0被覆盖
var { a, b, c: { a: a1, b: b1 = 0 } } = { a: 1, b: 2, c: { a: 3, b: 4 } }
console.log(a, b, a1, b1)//1 2 3 4
//传参问题
//如果函数的参数使用解构对象,但是函数没有传参,这时候无法解构对象,就会报错
//因此我们需要给解构对象一个默认值
function fn({ a, b }) {
console.log(a, b)
}
fn()//报错
//给默认值 {}
function fn({ a, b }={}) {
console.log(a, b)//undefined undefined
}
fn()
//有默认值 但是没赋值 就使用默认值
function fn({ a, b } = { a: 0, b: 0 }) {
console.log(a, b)//0 0
}
fn()
//因为没有传参 所以还是使用默认值
function fn({ a, b = 10 } = { a: 0, b: 0 }) {
console.log(a, b)//0 0
}
fn()
//当传参时默认值对象{a:0,b:0}就无效了,将{a:5}给参数{a,b}做解构,但是只有a属性,没有b属性,b属性使用自身的默认值
function fn({ a = 5, b = 10 } = { a: 0, b: 0 }) {
console.log(a, b)
}
fn()//0 0
fn({})//5 10 传参了但是 是空使用其初始值
fn({ a: 1 })//1 10
fn({ b: 100 })//5 100
function fn({ a = 5, b: { c = 1, d = 2 } = { c: 5, d: 6 } } = { a: 0, b: {} }) {
console.log(a, c, d)
}
fn()//0 1 2 此时没传参 使用默认值对象 a=0 b={} -> 相对于给传参了所以使用自身的默认值
fn({ a: 10 })//10 5 6 此时传参了不使用默认值对象 给a赋值 但没有给b 所以b使用其默认对象
fn({})// 5 5 6 传参了不使用默认值对象 但是是空a无默认值对象所以等于其默认值 b使用其默认值对象
fn({ b: { d: 10 } })//5 1 10 传参了不使用默认值对象 a同上 b给值了所以使用自身对象
箭头函数
箭头函数不会预解析,所以不许先定义后使用
写法
var fn = function () {
}
//箭头函数
var fn = () => {
}
var fn = function (a, b) {
console.log(a, b)
}
var fn = (a, b) => {
console.log(a + b)
}
//如果函数中的参数仅有一个,可以省略小括号
//但是如果函数中没有参数,或者函数参数超过一个,就必须写小括号
var fn=function(a){
console.log(a)
}
var fn=a=>{
close.length(a)
}
//如果函数仅有一句话,就可以省略{}包括这句话的return
//省略{}相当于加了一个return
var fn=function(a,b){
return a+b
}
var fn=(a,b)=>a+b
var fn=function(a,b){
console.log(a+b);
}
var fn=(a,b)=>console.log(a+b);
var fn = function (name, age, sex) {
// 在写对象时,如果键名等于变量名
// return {
// name:name,
// age:age,
// sex:sex
// }
//可以省略键名 下面等同于上面
return {
name,
age,
sex
}
}
//错误写法 这里把{}看做函数的{}
// var fn=(name,age,sex)=>{name,age,sex};
//错误写法 不能有return
// var fn=(name,age,sex)=>return {name,age,sex};
//正确写法 需要加一个()
var fn = (name, age, sex) => ({ name, age, sex });
var o = fn("张三", 30, "男");
console.log(o)//{name: '张三', age: 30, sex: '男'}
使用方式
var fn=function(a,b){
a++;
b++;
return a+b
}
var fn=(a,b)=>(a++,b++,a+b)
console.log(fn(3,4))//9
var arr = [2, 5, 2, 1, 4, 6, 8, 9];
arr.sort((a, b) => a - b)
var arr1 = arr.map(item => item + 10)
console.log(arr1)//(8) [11, 12, 12, 14, 15, 16, 18, 19]
if (arr.some(item => item > 5)) {
console.log("Aa")//Aa
}
箭头函数this的指向
//箭头函数使用的特征,改变箭头函数中的this指向
/*
在箭头函数中,this指向,父级程序的this指向
如果父级程序有this指向,那么箭头函数指向的就是父级程序的this
如果父级程序没有this指向,那么指向的就是window
*/
//严格模式下 普通函数this指向undefined 箭头函数还是指向window
var fn = function () {
console.log(this);
}
var fn = () => {
console.log(this);//window
}
fn()
var obj = {
a: 1,
b: function () {
// console.log(this);
var fn = () => {
console.log(this);//obj
}
fn();
}
}
obj.b();
var a = 10;
var obj = {
a: 1,
name: this.a,//this 对象外的this的指向
b: function () {
console.log(this)
},
c() {
console.log(this)
// 在对象中直接写一个 函数名(){} 和写入 方法名:function(){} 完全相同
},
d: () => {
console.log(this);//对象外的this指向 window
}
}
obj.b()//obj
obj.c()//obj
obj.d()//window
展开语法
作用
var arr=[1,2,3,4]
console.log(arr)//(4) [1, 2, 3, 4]
console.log(...arr)//1 2 3 4
var arr=[0]
var arr1=[1,2,3]
//push向数组中添加新元素,不会改变原数组
//把arr1 展开后 把值添加到arr
arr.push(...arr1)
//此时只是改变了其值 并没有改变引用地址
console.log(arr)//(4) [0, 1, 2, 3]
//使用concat 返回一个新数组,重新赋值给arr,这就把原来的arr数组给覆盖
//引用地址改变了
arr=arr.concat(arr1)
console.log(arr)//(4) [0, 1, 2, 3]
//实现让arr1的值覆盖arr的值
var arr = [1, 2, 3, 4]
var arr1 = [4, 5, 6, 7]
arr.length = 0;
arr.push(...arr1)
console.log(arr)//(4) [4, 5, 6, 7]
复制数组
//复制数组
var arr=[1,2,3,4]
//concat,map两个方法都会返回新数组
var arr1=arr.concat()
var arr2=arr.concat()
console.log(arr1)//(4) [1, 2, 3, 4]
console.log(arr2)//(4) [1, 2, 3, 4]
var arr3=arr.map(item=>item)
console.log(arr3)//(4) [1, 2, 3, 4]
//使用展开语法复制数组
var arr=[1,2,3,4]
var arr1=[...arr]
console.log(arr1)//(4) [1, 2, 3, 4]
var arr=[1,2,3];
var arr1=[4,5,6];
var arr2=[...arr,...arr1];
console.log(arr2)//(6) [1, 2, 3, 4, 5, 6]
求最大值
var arr=[1,5,2,7,3,8]
var max=Math.max.apply(null,arr)//8
var max=Math.max(...arr)//8
console.log(max)
在函数中的使用
//在实参中使用...
function fn(a, b, c) {
console.log(a, b, c)//1 2 3
}
var arr = [1, 2, 3]
fn(...arr)
//在形参中使用...
// 在形参中使用...语法,就是把实参聚合成一个数组
function fn(...arg) {
console.log(arg)//(3) [1, 2, 3]
}
fn(1, 2, 3)
案例 判断参数类型
/*
1、必填参数 参数必须写入实参,如果不写会出现错误
2、默认值参数 参数如果没有实参,会自动赋值默认值
3、选填参数 参数为选填,如果没有实参,不会引起函数错误
4、剩余参数 如果除了前面参数以外,还想多填充其他参数,将会归入剩余参数 ...arg
*/
function fn(a, b = 1, c, ...arg) {
// a 必填参数 b 默认值参数 c 选填参数 arg 剩余参数
if (typeof a !== "number" || typeof b !== "number") throw new TypeError("a 或者 b 不是数值!")
var s = a + b;
if (c !== undefined) {
if (typeof c !== "number") throw new TypeError("c 不是数值!")
s += c;
}
if (arg.length > 0) {
if (arg.every(item => typeof item !== "number")) throw new TypeError("c 不是数值!")
s += arg.reduce((v, t) => v + t)
}
return s
}
console.log(fn(1))
console.log(fn(1, 2))
console.log(fn(1, 2, 3, 4, 5, 6, 7))
对象展开语法
浅复制
var obj = {
a: 1,
b: 2,
c: 3,
d: {
e: 10
}
}
//浅复制 对象的第一层是没有引用关系的,第二层及以后都是还有引用关系,
var o = { ...obj }
obj.a = 10;
obj.d.e = 100
console.log(o)//{a: 1, b: 2, c: 3, d: {e:100}}
连接对象
var o1 = { a: 1, b: 2 }
var o2 = { c: 3, d: 4 }
var o3 = { ...o1, ...o2, e: 10 }
console.log(o3)//{a: 1, b: 2, c: 3, d: 4, e: 10}
解构与展开语法的区别
//解构:解构赋值允许你使用数组或对象字面量的语法,将数组和对象的属性付给各种变量。
//展开:允许你讲一个数组展开为另一个数组,或一个对象展开为另一个对象。
Set
知识点补充:
数据结构 数据在存储过程中的存储方式
Array Object
Array 存储按位存储,紧密结构,在数组中查找元素,就必须要遍历数组,优点可以根据当前元素 找到前一个或者后一个元素,可以排序
Object 按键存储,松散结构(删除其中一个属性,不会影响其他属性的位置),添加、删除、查找速度极快
缺点:无法找到相邻关系数据,无法排序
set的方法
//Set 类似于数组 Set是集合,松散型结构,不能排序。集合存储仅有元素,而且仅能存储不重复的元素
var s=new Set([1,2,3,4,5])
// 把数组做为Set参数,会自动把数组元素放在set对象中
//相当于数组的length
console.log(s.size)//5
var s = new Set();
//s.add向集合中添加元素 返回集合对象自身
//每次只能添加一个,如果添加的元素在数组中已经存在,就不在添加
s.add(1)
s.add(2)
s.add(3)
s.add(4)
s.add(2)
console.log(s)//Set(4) {1, 2, 3, 4}
console.log(s.has(3))//true 判断集合中是否有该元素
s.delete(3)// 删除集合中的元素
console.log(s)//Set(3) {1, 2, 4}
s.clear()// 清空集合中的元素
console.log(s)//Set(0) {size: 0}
//遍历集合
s.forEach(item => {
console.log(item)
})
for of
//for of 类似for in,但是这里for(var value of s) value就是每个元素 可以用来遍历所有的迭代器
var s = new Set();
s.add(1)
s.add(2)
s.add(3)
s.add(4)
for (var value of s) {
console.log(value)
}
console.log(s)
var arr = [1, 2, 3, 4, 5];
for (var value of arr) {
console.log(value)
}
set的用法
1.数组去重
var arr = [1, 2, 5, 2, 3, 5, 1, 3, 5, 7, 9];
arr = Array.from(new Set(arr))
console.log(arr)//(6) [1, 2, 5, 3, 7, 9]
var arr = [
{ id: 1, name: "商品1", price: 3000 },
{ id: 2, name: "商品2", price: 4000 },
{ id: 1, name: "商品1", price: 3000 },
{ id: 3, name: "商品3", price: 5000 },
{ id: 4, name: "商品4", price: 6000 },
]
var s = new Set(arr)
//每个对象的引用地址都是不同的
console.log(s)
2.不会因为塌陷造成处理问题
var arr = [
{ id: 1, name: "商品1", price: 3000 },
{ id: 2, name: "商品2", price: 4000 },
{ id: 3, name: "商品3", price: 5000 },
{ id: 1, name: "商品1", price: 3000 },
{ id: 4, name: "商品4", price: 6000 },
{ id: 2, name: "商品2", price: 4000 }
]
var s = new Set(arr)
for (var i = 0; i < arr.length; i++) {
var index = arr.slice(i + 1).findIndex(item => item.id == arr[i].id)
if (index > -1) {
index += i + 1;
arr.splice(index, 1)
}
}
console.log(arr)
3.增改查
//不使用set
var arr=[];
function add(f){
if(arr.includes(f)) return;
arr.push(f);
}
function remove(f){
var index=arr.indexOf(f);
if(index<0) return;
arr.splice(index,1);
}
function callAllFn(){
arr.forEach(fn=>{
fn();
})
}
function fn1(){
console.log("fn1");
}
function fn2(){
console.log("fn2");
remove(fn2);
}
function fn3(){
console.log("fn3");
}
add(fn1);
add(fn2);
add(fn3);
add(fn2);
console.log(arr)
callAllFn();
//使用set
var s=new Set();
function add(f){
s.add(f)
}
function remove(f){
if(s.has(f)) s.delete(f);
}
function callAllFn(){
s.forEach(fn=>
fn()
)
}
function fn1(){
console.log("fn1");
}
function fn2(){
console.log("fn2");
remove(fn2);
}
function fn3(){
console.log("fn3");
}
add(fn1);
add(fn2);
add(fn3);
add(fn2);
// console.log(arr)
callAllFn();
callAllFn();
WeakSet 弱引用集合
var s=new WeakSet();
/* 只有添加、删除、清空
必须只能添加引用类型
不能被遍历 forEach、for of不能遍历 */
s.add({a:1,b:2});
// s.add()
//s.delete();
// s.clear();
console.log(s)
var o={a:1,b:2};
var o1=o;
// 强引用关系
o=null;
console.log(o1);//o被清除 o1不变
Map
理念
map 映射对象
键值对 松散结构 对象的key只能是字符串或者symbol,但是map的key可以是任何类型,增删改查速度快
可以做任何关联关系,map可以单独遍历key,或者单独遍历value,map也有size,对象是没有length
var m=new Map()
m.set("a",1)//给m设置了一个字符串a属性,并且值设置为1
m.set(false,2)//给m设置了一个字符串false属性,并且值设置为2
var o={a:1}
var o1={b:2}
m.set(o,o1)//将对象o做为key,将对象o1做为值
console.log(m)
//对比对象
var obj={}
obj["a"]=1
obj["fasle"]=2
obj[o]=o1
console.log(obj)
// 任何对象转换为字符串都会变为 [object Object]
console.log(String(o))//[object Object]
方法
var m = new Map()
m.set("a", 1)
m.set(false, 2)
var o = { a: 1 }
var o1 = { b: 2 }
m.set(o, o1)
//删除
// m.delete("a")
// m.clear()//清空m
console.log(m.get(false))//2 根据键获取值
console.log(m.get(o))//{b: 2}
console.log(m.has("a"))//true 判断m中有没有键a
//map中存储键值对的个数
console.log(m.size)//3
console.log(m)
遍历m 中的键和值
m.forEach(function(value,key){
console.log(value,key)
})
遍历m中的值 返回为数组
for(var item of m){
console.log(item)
}
//使用解构 遍历出一个一个值
for(var [key,value] of m){
console.log(key,value)
}
使用keys()、values()、entries()
//m.keys()获取到当前map中所有键的迭代器
for(var key of m.keys()){
console.log(key)
}
// //m.values()获取到当前map中所有值的迭代器
for(var value of m.values()){
console.log(value)
}
//m.entries() 将当前的map转换为迭代器
for(var item of m.entries()){
console.log(item)
}
数组有同样的keys()、values()、entries()方法
var arr=["a","b","c","d"];
for(var item of arr){
console.log(item)//a b c d
}
for(var index of arr.keys()){
console.log(index)//0 1 2 3
}
for(var value of arr.values()){
console.log(value)//a b c d
}
for(var value of arr.entries()){
console.log(value)//[0,'a']
// [1,'b']
// [2,'c']
// [3,'d']
}
对象转换为Map
//对象转换为map时 需要先转换为一个二维数组
//将对象转换为一个迭代器,这个迭代器是一个二维数组,一个元素包括一个key和一个value
//使用Object.entries()转换为二维数组
var obj = { a: 1, b: 2, c: 3 }
var arr = Object.entries(obj)
var m = new Map(arr)
console.log(m)
//Map(3) {'a' => 1, 'b' => 2, 'c' => 3}
Map转换为对象
//使用Object.fromEntries()方法
var m = new Map()
m.set("a", 1)
m.set("b", 2)
m.set("c", 3)
m.set(true, 3)
m.set({ a: 1 }, { b: 2 })
console.log(m)
var o=Object.fromEntries(m)
console.log(o)
案例 button点击
// 根据标签名获取到所有元素列表
var bns = document.getElementsByTagName("button");
var m = new Map()
//迭代器转换为数组
bns = Array.from(bns)
for (var i = 0; i < bns.length; i++) {
//第一行按钮添加了点击事件
if (i == 0) bns[i].onclick = clickHandler;
else {
//其他的按钮变为不可用状态
bns[i].disabled = true
m.set(bns[i - 1], bns[i])
}
}
function clickHandler() {
//在点击事件中,this指向侦听当前点击事件的按钮
//将当前点击的按钮不可用,然后取消当前点击的事件
this.onclick = null
this.disabled = true
//如果在map中没有找到当前按钮对应的key,就跳出
if (!m.has(this)) return
//根据当前按钮的key找到下一个要点击的按钮
//并且设置这个需要被点击的按钮的按钮事件和可用状态
m.get(this).onclick = clickHandler;
m.get(this).disabled = false
}
WeakMap 弱引用类型
//WeakMap 的key只能是引用类型,并且不可遍历
var m=new WeakMap()
var o={a:1}
var o1={a:2}
m.set(o,o1)
o=null;
console.log(m)
symbol类型
// js的数据类型由简单类型和复杂类型组成 简单类型就是string、number、boolean 、undefined、null,复杂类型是object
// js的数据类型bigInt、number、string、boolean、function、undefined、object、symbol
// symbol不做任何元素,相当于一个标识,不管怎么创建Symbol,都不会产生重复的值
var a=Symbol();
var b=Symbol();
console.log(a==b);//false
去除魔术字符串
const LEFT = Symbol(),
RIGHT = Symbol(),
TOP = Symbol(),
BOTTOM = Symbol();
const LEFT="left",RIGHT="right",TOP="top",BOTTOM="bottom"
var state;
state=LEFT
// state ="left"
switch (state) {
case LEFT:
console.log("left");
break;
case RIGHT:
console.log("right");
break;
case TOP:
console.log("top");
break;
case BOTTOM:
console.log("bottom");
break;
}
注意点
将对象的key 设置为symbol可以防止被另外的属性覆盖
var s = Symbol("s")
var abc = Symbol("abc")
var obj = {
a: 1,
[abc]: 2,
[s]: 3,
s: 4,
abc: 5
}
console.log(obj)//{a: 1, s: 4, abc: 5, Symbol(abc): 2, Symbol(s): 3}
for in 不会遍历Symbol属性
for (var key in obj) {
console.log(key)//只遍历出一个a
}