目录
一、简介
ECMAScript(简称ES6),是JavaScrit语言的下一代标准,它的目标是使JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
二、配置babel环境
因为浏览器不识别ES6,所以要转换为ES5。
①安装node 下载并安装node.js(npm淘宝镜像、nodemon)
②在vscode中选择文件夹,打开终端输入
npm init -y
③ 输入
npm install --save-dev babel-cli babel-preset-es2015
④ 创建文件 .babelrc (注意一定要有点!)并输入
/ 配置用于转化当前代码的规则集 { "presets": [ "es2015" ]
⑤ 创建项目录,src用于存放编写的es6代码,dist目录用于存放由es6转化后的代码
⑥ 将es6 代码转化为es5
⇨单独转一个文件 node_modules/.bin/babel src/test.js --out-file dist/test.js
⇨转整个文件夹 node_modules/.bin/babel src --out-dir dist
借助 node_modules/.bin/babel 将src下的src/test.js 转换到dist/test.js(dist下的js不是自己创建 的,是转化过来的)
⑦配置package.json文件 (为了自动转换)操作如下方截图
//配置监听脚本 一旦src下的js发生变化 自动转化到dist中 "dev":"babel src -w -d dist"
⑧ 用法 在终端输入 注意:dev是一个名字,根据脚本写的
npm run dev
⑨创建index.html文件,输入
<script src="./dist/test.js"></script>
三、变量声明关键字let、const
var的缺点
存在变量提升;同一作用域下可以重复声明;不存在块级作用域,只存在全局作用域和局部函数作用域;不可以限制修改,只能声明变量不能声明固定值的常量
(1)存在变量提升
console.log(a); //输出undefined var a = 1 // 实际上 变量提升 var a; console.log(a); a=1;
(2)同一作用域下可以重复声明
var a = 1; var a = 2; console.log(a) //输出2,可以重复声明,但是在java中会报错
(3)不存在块级作用域,只存在全局作用域和局部函数作用域
(4)不可以限制修改,只能声明变量不能声明固定值的常量
let&const
- let
特点: (1)只在声明的代码块内有效(块级作用域)
(2)在同一作用域内不允许重复声明
(3)没有变量提升
(4)暂时性死区(必须先声明在使用,在变量声明之前,无法读取或者操作这个)
- const
const 用于声明常量 ,只是保证里面的内存地址不变(简单数据类型 如字符串,数字,布尔类型的值,值就保存在变量指向的内容地址,而复杂数据类型 的数据 如对象,数组和函数的时候,变量指向的内存地址,实际上是保存了指向实际数据的指针,所以const只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了)
特点:跟 let 一样
四、Symbol数据类型
symbol是一种基本数据类型(primitivedatatype)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"newSymbol(()"
symbol()、symbol.for
每个从symbol()返回的symbol值都是唯一的,即使名字相同
1.Symbol是新增的一种数据类型,表示一种独一无二的(任意两个Symbol类型都是不相等的)
Symbol相等与否与Symbol()内描述信息没有关系!!!
参数a1相当于对当前Symbol进行备注,用来描述当前这个Symbol let a1=Symbol("a1"); let a2=Symbol("a1"); console.log(a1); console.log(typeof a1); console.log(a1 === a2);
2.Symbol函数可以接受一个字符串作为参数,表示对该Symbol实例的描述,比较容易区分
const sym1 = Symbol("name") const sym2 = Symbol("address") console.log(sym1) console.log(sym2) console.log(sym1 === sym1)
const sym1 = Symbol("name") const sym2 = Symbol("name") console.log(sym1) console.log(sym2) console.log(sym1 === sym2)
3.应用场景: Symbol作为对象的key
let obj={ [a1]:123, aa:123, a1:457 } console.log(obj); let obj2={} obj2[a1]=3;
4.如果描述信息一样,想得到两个一样的Symbol()值,可以使用Symbol.for()
Symbol.for 这个API代表会在全局寻找叫做aa的这个变量,如果有就会把当前这个symbol赋值到a1变量中,如果没有就会创建symbol再赋值
let a1=Symbol.for("aa"); let a2=Symbol.for("aa"); console.log(a1===a2);
6.为对象赋值的另一种方式:参数1是对象的名称 参数2是key的名称 参数3是当前这个key的值
结合上方代码看 Object.defineProperty(obj,a1,{value:890})
7. Symbol的另外一个作用:声明字符串常量,下面代码中的hahaha这个字符串就为常量
const user=Symbol("hahaha"); console.log(user);
8.Object.getOwnPropertySymbol()是用来专门获取指定对象的所有Symbol属性名的
变量作为对象的属性的时候,通过方括号将变量转换为属性
const key1 = Symbol("hi") const key2 = Symbol("flag") const o = { a:1, b:2, [key1]: 2022, [key2]:"暴富" } console.log(o[key1],o[key2])
五、解构赋值
什么是解构赋值?
- 解构赋值可以理解为赋值操作的语法糖,它是针对数组或者对象进行模式匹配,然后对其中的变量进行赋值,代码书写上言简意赅,语义明确,也方便了对象数据的读取操作
使用解构赋值的前提
- ES6中在某种数据上只要有一个遍历接口(数组、字符串、对象),也就说这个数据能够循环遍历的话,就可以进行解构赋值
- 解构赋值必须:赋值符号左右两边的结构必须一致(要么都是数组,要么都是对象);变量的声明和赋值必须在一句话内完成
使用
① 如果赋值时变量的个数和值的数对应不上,多出来的变量中的值就是undefined
{ // 如果赋值时变量的个数和值的个数对应不上,多出来的变量中的值就是undefined let a, b, c [a, b, c, d] = [1, 2, 3]; console.log(a, b, c, d); }
② 指定默认值
{ // 指定默认值 let a, b, c [a, b, c = 9] = [1, 2] console.log(a, b, c); }
③ ...为ES6中的扩展运算符,这样会将a赋值给1,而other会作为一个数据将剩余的值作为整个数组中的元素进行存储
{ // ...为ES6中的扩展运算符 这样会将a赋值为1,而other会作为一个数组将剩余的值作为整个数组中的元素进行存储 let a ,other [a,...other]=[1,2,3] console.log(a,other); }
④ 只读取第一个和第三个值,逗号只作为占位符
{ // 只取第一个和第三个值,逗号只作为占位符 let a,b [a,,b]=[1,2,3]; console.log(a,b); }
⑤ 对象解构赋值,如果当前行中的第一个字符为大括号的会报错,所以需要加上小括号
{ // 对象解构赋值 如果当前行的中的第一个字符为大括号的话会报错,所以需要加上小括号 let a, b; ({a,b}={a:2,b:3}) console.log(a,b); }
⑥ 只使用属性值不使用属性名,相当于给原有对象中的属性名重新命名
{ // 只使用属性值不使用属性名,相当于给原有对象中的属性名重新命名 let num,total; ({a:num,b:total}={a:2,b:3}) console.log(num,total); }
六、字符串的扩展方法及模板字符串
① 在 es5 中显示乱码 es6 中显示汉字 ⇨ \uxxxx 限制范围:\u0000-\uFFFF
es6的方案:将超过两个字节的组成字符的码点放在一对大括号里,就可以正确的识别了
{ // es5 \uxxxx 限制范围:\u0000-\uFFFF const str="\u20bb7"; console.log(str); }
{ // es6的方案:将超过两个字节的组成字符的码点放在一对大括号里,就可以正确的识别了 const str="\u{20bb7}" console.log("es6",str); }
② for of:传统for循环无法处理超出范围的字符的,而for of能够正确的处理,处理字符串循环时推荐使用for of进行处理
{ // for...of: 传统for循环无法处理超出范围的字符的,而 for..of 能够正确的处理,处理字符串循环时推荐使用for of进行处理 const str="\u{20bb7}" for( let i=0;i<str.length;i++){ console.log(str[i]); } for(let word of str){ console.log("for-of",word); } }
③ str.includes() 判断字符串中是否包含指定字符串,返回值是布尔值
{ // str.includes() 判断字符串中是否包含指定字符串,返回值是布尔值 let str="123abc321"; console.log("includes",str.includes("abc")); }
④ str.startsWith() 判断以什么开头的,参数2是从指定的位置数起
{ // str.startsWith() 判断以什么开头的,参数2是从指定的位置数起 let str="123abc321"; console.log("startsWith",str.startsWith("abc",3)); console.log("endsWith",str.endsWith("abc",6)); }
⑤repeat() 方法 返回一个新字符串,表示将原字符串重复n次
{ // repeat() 方法返回一个新字符串,表示将原字符串重复n次 let str="1abc23"; let str2=str.repeat(3); console.log(str); console.log(str2); }
⑥ 头部补全 参数1:补全之后的字符串长度 。参数2: 作为补位的字符串,如果超出补全长度自动截取
{ // 头部补全 参数1:补全之后的字符串长度 。参数2: 作为补位的字符串,如果超出补全长度自动截取 let str="Apple" str=str.padStart(7,"abc"); console.log(str); // 尾部补全 }
⑦ 模板字符串(反引号-tab按键上面那个``,可以换行,可以加入${变量}解析变量里面的值)
{ // 模板字符串(反引号-tab按键上面那个`,可以换行,可以加入${变量}解析变量里面的值) const name="神经质土豆"; const age=20; const str="我叫:"+name+",今年"+age+"岁了" console.log(str); const str2=` 我叫 ${name} 我今年${age}岁了 ` console.log(str2); }
七、ES6和ES7之数组的扩展方法及扩展运算符的使用
①扩展运算符 ...
如果使用arr1=arr2这种方式的话,只是将arr1中存放的地址值赋值给了arr2,这样如果arr1中的数组改变的话,arr2也会跟着改变
arr1 和 arr2 相互独立,互不影响
{ // 扩展运算符 ... 复制 // 如果使用arr1=arr2这种方式的话,只是将arr1中存放的地址值赋值给了arr2,这样如果arr1中的数组改变的话,arr2也会跟着改变 const arr1=[1,2,3,4,5]; // let arr2=arr1; let arr2={...arr1}; arr1.push(6); console.log(arr2); //arr1 和 arr2 相互独立,互不影响 // 分割数组 用逗号 占位 const totalArr=[1,"a","b","c"]; let[,...strArr]=totalArr; console.log(strArr); // 给函数传参 function add(x,y){ return x+y; } let arr=[4,5]; console.log(add(...arr)); }
② fill替换数组中的元素 ⇨ 参数1:要替换后的值 参数2:开始下标 参数3:结束下标
{ const list = [1,2,3,4,5]; //将数组中的元素全部替换为3 原来的数组也发生变化了 // let list2 = list.fill(3); let list2 = [...list].fill(3); console.log(list,list2); //将数组中的下标为 1 的元素到下标为 4 之前的元素中的值替换成3 //参数1:要替换后的值 参数2:开始下标 参数3:结束下标 let list3 = [...list].fill(3,1,4); console.log(list3); }
③find & findindex
{ // find findindex const list=[{title:'es6'},{title:'webpack',id:2},{title:'vue'},{title:'webpack',id:3}] // find :循环遍历数组,根据条件返回对应的这条数据 // 回调函数中的item代表数组中的每个元素(arr[i]) let result=list.find(function(item){ return item.title==="webpack" }) console.log(result); let resultIndex=list.findIndex(function(item){ return item.title==="webpack" }) console.log(resultIndex);//返回对应的索引 }
④ Includes & indexOf
Includes 判断当前元素是否存在,返回值为布尔类型 ; indexOf返回的是索引或者-1
{ // Includes 和 indexOf // Includes 判断当前元素是否存在,返回值为布尔类型 indexOf返回的是索引或者-1 const arr=[1,2,3,4,5,6]; let result=arr.includes(2); let result2=arr.indexOf(2); console.log(result); console.log(result2); }
⑤ flat展开数组
flat (数字可以表示多展开几层),contact只能到第二层
{ // flat 展开数组 const list=[1,2,3,['2nd',4,5,6,['3rd',7,8]]]; let flatList=list.flat(2);//2 的意思是展开2次 console.log("flat", flatList); let flatList2=[].concat(...list) //concat 拼接数组,向空数组中拼接复制过来的list数组 console.log( flatList2); }
⑥Array.from 类数组对象 有length,可以遍历
{ // Array.from 类数组对象 有length,可以遍历 const str="hello"; const arr=Array.from(str); console.log(arr); }
八、map & reduce
① map 数据映射
把数组中的 0 ,1 映射成文字
{ // map 数据映射 const json=[{name:'张三',gender:0},{name:'李四',gender:1},{name:'小花',gender:1},{name:'小明',gender:0}] let arr=json.map(function(item){ return { name:item.name, genderTxt:item.gender ? "女":"男" //判断item.gender是0还是1,0 代表false 选择后面 ;1代表true 选择前面, } item.gender=item.gender? "女":"男" return item let obj={}; // 复制对象 Object.assign(obj,item); obj.gender=obj.gender? "女":"男" return obj }) console.log(arr,json); }
② reduce 对数组中的每个元素进行一次回调
reduce(callback,initalValue)
- initalValue:是一个返回值 它是一个可选参数,如果传入值的话,callback里面的参数acc的初始值就是这个传入的 值,如果不传入的话acc初始值就是数组中的第一个元素
- callback:回调函数 callback里面的参数
acc:返回值(每次回调之后的返回值)
currentValue:当前进行回掉的值
currentIndex:当前回调的值对应的索引
{ /* reduce 对数组中的每个元素进行一次回调 reduce(callback,initalValue) initalValue:是一个返回值 它是一个可选参数,如果传入值的话,callback里面的参数acc的初始值就是这个传入的 值,如果不传入的话acc初始值就是数组中的第一个元素 callback:回调函数 callback里面的参数 acc:返回值(每次回调之后的返回值) currentValue:当前进行回掉的值 currentIndex:当前回调的值对应的索引 */ const letterList="abcadefrdc"; const arr=letterList.split("");//将字符串转换成数组 const result=arr.reduce(function(acc,cur){ acc[cur]?acc[cur]++:acc[cur]=1;//第一次:相当于acc.a 没有值,走后 ;等到再次遇到a,acc.a=1 ,有值,走前 return acc },{}) console.log(result); } //此代码目的:字符串中每个元素重复出现的个数
简单解析版
// 解析上方代码 const letterList="abcadefrdc"; const arr=letterList.split(""); const result=arr.reduce(function(acc,cur){ if(acc[cur] !==undefined){ acc[cur]++ }else{ acc[cur]=1 } return acc },{}) console.log(result);
再来解析 / acc 相当于下面代码中的 a / acc[cur]?acc[cur]++:acc[cur]=1
let obj={};//一开始为空 // obj["a"] ? obj["a"]:obj["a"]=1;//相当于判断obj.a里有值么,如果有,就自增,如果没有,就obj["a"]=1 // obj["a"] ? obj["a"]++:obj["a"]=1; //判断obj.a里有值么,有为true,选择自增,因为上面代码obj["a"]=1再自增就为2
③ reduce 展开多层数组
{ // 展开多层数组 const list=[1,['2nd',2,3,['3rd',4,5]],['2nd',6,7]] const deepFlat=function(arr){ reduce(function(acc,cur){ return acc.concat(Array.isArray(cur)? deepFlat(cur):cur) },[]) } let result=deepFlat(list); console.log(result); }
九、对象中扩展运算符的使用
安装es8 插件
babel 不支持 es8的语法,所以要安装插件
①在终端输入
npm i babel-plugin-transform-object-rest-spread
②"plugins": ["transform-object-rest-spread"] 添加到 .babelrc 里面
{ "presets": ["es2015"], "plugins": ["transform-object-rest-spread"] }
③运行 在终端输入
npm run dev
使用
① 复制对象 ...
注意:简单数据类型时使用扩展运算符是没问题的,但是如果扩展运算符展开对象以后还是一个对象,复制的就是一个指针了
{ // 对象中扩展运算符的使用 // 复制对象 const obj={name:'tom',age:19} // 在对象中使用扩展运算符测试之前需要安装npm i babel-plugin-transform-object-rest-spread 插件,然后在.babelrc文件中加入配置 "plugins": ["transform-object-rest-spread"] ,然后重新npm run dev let obj2={...obj} console.log(obj2); // 设置对象默认值 let obj3={...obj,name:'jerry'}//相当于复制obj 但是把name改为jerry console.log(obj3); // 合并对象 let initObj={color:'red',a:{aaa:'aaa'}} let obj4={...obj,...initObj}//注意这里有逗号 console.log(obj4); initObj.a.aaa='bbb' console.log(obj4); // 坑: // 简单数据类型时使用扩展运算符是没问题的,但是如果扩展运算符展开对象以后还是一个对象,复制的就是一个指针了 }
{ let name='神经质土豆' let age=19; let es5Obj={ name:name, age:age, say:function(){ console.log("es5"); } } //es6 : key value是一样的,可以只写一个,还可以省略function let es6Obj={ name, age, say(){ console.log("es6"); } } }
② Object.is() 和 ===
- NaN跟任何数包括自己 === 都是 false
- Object.is 就是true
{ // Object.is() 和 === let result=Object.is(NaN,NaN) console.log(result,NaN===NaN); }
③Object.assign() 复制对象
- Object.assign() 复制对象 一层对象很好用,如果两层对象的话,复制的就是指针了。修改的话,原对象也会被修改。
{ // Object.assign() 复制对象 const person={name:'xm',age:19,cars:{carname:"法拉利"}}; let person2={}; // 参数1: 目标对象 参数2:来源对象 Object.assign(person2,person) console.log(person2); person.cars.carname='兰博基尼' console.log(person2); }
⑤ Object.keys() ,Object.values() , Object.entries()
- Object.keys() 遍历所有属性的名字
- Object.values() 遍历所有属性的值
- Object.entries() 遍历所有键值对
{ // Object.keys() 遍历所有属性的名字 // Object.values() 遍历所有属性的值 // Object.entries() 遍历所有键值对 const json={name:'tom',age:19,gender:'男'}; console.log(Object.keys(json)); console.log(Object.values(json)); console.log(Object.entries(json)); // 将json中的数据复制到obj中 let obj={}; for(const k of Object.keys(json)){ obj[key]=json[key];//相当于 obj.name=json.name } console.log(obj); }
十、Map与WeakMap结构的特点
map 数据类型
①创建和添加 let map=new Map() 、set() 参数1:key 参数2:value
let map=new Map(); map.set([1,2,3],"aaa"); console.log(map);
- 每一个数组都是map中的一个值,数组中的第一个值为k,第二个值为value
- let map=new Map([["第一个数组的k","第一个数组的value"],["第二个数组的k","第二个数组的value"]])
- 获取长度 size
let map2=new Map([["name","tom"],["gender","male"]]) console.log(map2); console.log(map2.size);//获取长度 size
赋值的时候如果对同key名进行赋值,下面的会覆盖上面的,而且赋值操作也可以链式编程
let map2=new Map([["name","tom"],["gender","male"]]) map2.set("name","jerry").set("hobbies",["reading","singing"]) console.log("map",map2);
②获取值 get()
如果获取对应的key不存在,会返回undefined
console.log("get",map2.get("hobbies"));
③ 判断 has()
判断当前map中是否包含某个k
console.log("has",map2.has("age"));//没有age这个属性,故返回false
④删除操作 delete()
map2.delete("hobbies"); console.log("delete",map2);
⑤清空操作 clear()
map2.clear(); console.log("clear",map2);
⑥ map 中也有 keys() values() entries()
const map =new Map([ ['name','小明'], ['age','20'] ]); for(let key of map.entries()){ console.log(key); }
WeakMap
元素只能是对象 ,无法遍历,没有size 和clear 属性
let weakmap=new WeakMap([ // ["name","jack"] //不对 [{aaa:"aaa"},"jack"] ]) console.log(weakmap);
遍历方法 for each
for each方法主要是针对数组而言的,对数组中的每个元素可以执行一次方法
var array = ['a', 'b', 'c', 'd']; array.forEach((a)=> { console.log(a); });
for each方法主要有三个参数,分别是数组内容、数组索引、整个数组
十一、Set与WeakSet结构的特点
Set
set是es6给开发者提供的一种类似数组的数据结构,可以理解为值的集合,他和数组最大的区别在于:它的值不会有重复项
内部使用的是Object.is() ,判断值是否相同,不判断数据类型
①创建
let set=new Set([1,2,3,4,5,1]) console.log(set);
②添加元素 add()
let set=new Set([1,2,3,4,5,1]) console.log(set); // 添加元素 set.add(1); console.log(set);
获取长度 size
console.log("size",set.size);//获取长度
③判断属性存不存在has() 注意下方代码!这里有坑!!
let set=new Set(); set.add({fruit:"apple"}) // console.log("has",set.has({fruit:"apple"}));//返回false 因为这两个对象是不同的 const item={fruit:"apple"}; set.add(item); console.log(set.has(item));//这里返回的是true
④删除属性 delete()
⑤清空属性 clear()
⑥keys() values() entries() 在set中,key和value是相同的
const set=new Set([1,2,3,4,5]) for(let k of set.keys()){ console.log(k); } for(let value of set.values()){ console.log(value); }
最大的使用场景:数组去重
注意:利用Array.from 将数据类型转换为数组
const arr=[1,1,1,2,3,4,2,4,4,5,7,8,9] let set=new Set(arr); let arr2=Array.from(set);//这步 是将set里的数据类型转化为数组 利用Array.from console.log(arr2);
WeakSet
元素只能是对象 无法遍历,没有size 和clear
十二、数组、对象、map、set 增删改查
{ // 数组、对象、map、set 增删改查 let arr=[]; let obj={}; let map =new Map(); let set=new Set(); const item={fruit:"apple"} // 添加 arr.push(item); obj['fruit']='apple'; map.set('fruit','apple') set.add(item); console.log("add",arr,obj,map,set); // 查询 const resultArr=arr.includes(item); const resultObj='fruit'in obj; const resultMap=map.has('fruit'); const resultSet=set.has(item); console.log("has",resultArr,resultObj, resultMap,resultSet); // 修改 arr.forEach(function(item){ item.fruit=item.fruit? "orange":"" //后面的item.fruit 是判断这个属性存不存在,若存在则赋值为orange,否则为空 }) obj['fruit']='orange'; map.set("fruit","orange"); set.forEach(function(item){ item.fruit=item.fruit? "orange":"" }) console.log("undata",arr,obj,map,set); // 删除 const index=arr.findIndex(function(item){ return item.fruit //如果这个属性存在 ,则返回对应的索引 }) console.log(index); arr.splice(index,1); //参数1:从哪个元素开始删 参数2:删几个 delete obj.fruit; map.delete("fruit"); set.delete(item); console.log("delete",arr,obj,map,set); }
十三、类型转换
{ // 类型转换 // 一、map和对象转换 let obj={ name:'tom', age:19 } // (1)对象转map // 由于map中要接受的是键值对数组,所以想要转换成map之前,需要将对象转换为键值对数组,通过Object.entries()这个API进行转换 console.log(Object.entries(obj)); let map=new Map(Object.entries(obj)); console.log(map); // (2)map转对象 // 通过Object.fromEntries这个API进行逆转换 let obj2=Object.fromEntries(map); console.log(obj2); // 二、数组和set转换 // (1)数组转set let arr=[1,2,3,4]; let set =new Set(arr); console.log(set); // (2)set转数组 let arr2=Array.from(set); console.log(arr2); }
十四、ES6中的代理Proxy和反射Reflect
Proxy
概叙:proxy是es6为了操作对象引入的API,它不直接作用在对象上,而是作为一种媒介,如果需要操作对象的话,需要经过这个媒介的同意
{ let obj = { id: 1234, name: 'admin', phone: "123456789", create_time: "2022", _private: 'test' } let objProxy = new Proxy(obj, { // 拦截、读取和设置操作 // 需求:对手机号码中间四位换成****,创建时间换成2023 get: function (target, key) { //target代表当前的对象 switch (key) { case 'phone': return target[key].substring(0, 3) + "****" + target[key].substring(7) break; case 'create_time': return target[key].replace("2022", "2023") break; default: return target[key] break; } }, // 设置: 需求:如果要设置的是id就不允许,如果是id就赋值为之前的那个默认值,其他属性可以正常赋值 set: function (target, key, value) { if (key === "id") { return target[key] } else { return target[key] = value } }, // 拦截 key in obj 就是判读某个key是否在对象中存在的操作 has: function (target, key) { if (key in target) { console.log(`${key}:`, target[key]); return true } else { console.log("查无此属性"); return false; } }, // 拦截 delete 需求:假如要删除的属性以下划线开头(私有属性),不允许删除,其他属性可以进行删除 deleteProperty: function (target, key) { if (key.indexOf("_") === 0) { //indexof() 判断字符串中是否有这个字符并返回对应的索引 console.warn("私有属性不能被删除") return false; } else { delete target[key]; return true; } }, // 拦截 Object.keys() 遍历所有的属性名。 需求:如果是id或者私有属性就过滤掉 //filter() 方法用于把Array中的某些元素过滤掉,然后返回剩下的未被过滤掉的元素 // 1、filter() 不会对空数组进行检测; 2、filter() 不会改变原始数组。 ownKeys(target) { return Object.keys(target).filter(function (item) { return item !=='id' && item.indexOf("_") !== 0 }) } }) console.log("拦截读取", objProxy.phone, objProxy.create_time, objProxy.name); objProxy.id = 4567; objProxy.name = 'tom'; console.log("拦截设置", objProxy.id, objProxy.name); console.log("拦截 in", "sex" in objProxy);//判断这个属性有没有 console.log("拦截 in", "name" in objProxy); console.log('拦截删除',delete objProxy["_private"]); // console.log('拦截删除', delete objProxy["name"]); console.log("拦截 Object.keys()", Object.keys(objProxy)); }
Reflect
就是操作对象的属性和方法的另外一种方式,推荐使用Reflect,这样操作更加直观
let obj = { name: 'jerry', age: 20, sex: 'male', hobbies: 'running' } console.log(Reflect.get(obj, 'name'));//获取 Reflect.set(obj, "name", "tom");//设置 console.log(obj.name); 'name' in obj; console.log(Reflect.has(obj,'name') );//看属性存不存在
一个小案例
现在html布局 再引入js
<h1>您输入的是:<span id="txt"></span></h1> <input type="text">
{ // 获取dom元素 const inp=document.getElementsByTagName("input")[0]; const txt=document.getElementById("txt"); // 初始化代理对象 const obj={} // 代理选项 const handler={ get:function(target,key){ return Reflect.get(target,key) }, set:function(target,key,value){ if(key==='text'){ inp.value=inp.value === value? inp.value:value; txt.innerHTML=value; } return Reflect.set(target,key,value) } } let objProxy=new Proxy(obj,handler); // 给input绑定键盘弹起事件 inp.addEventListener("keyup",function(event){ objProxy.text=event.target.value }) } // 1.获取dom对象 // 2.设置代理对象 // 3.配置代理选项 // 4.添加事件 // 5.实现双向数据绑定
十五、rest
rest 函数中形参以外剩余的参数,如果函数中没有其他的形参,rest代表所有传过来的参数(以数组形式存储)
{ // 默认参数 function es5Fn(x,y){ y= y || 'word'; console.log('es5',x+y); } es5Fn("hello",'') function es6Fn(x,y="world"){ //es6才有 console.log("es6",x+y); } // es6Fn('hello') es6Fn('hello','haha') }
{ // rest 函数中形参以外剩余的参数,如果函数中没有其他的形参,rest代表所有传过来的参数(以数组形式存储) function add(x,...rest){ console.log(rest);//接受传过来剩余的参数 数组形式 let sum=0; for(let value of rest){ sum+=value; } } add(1,2,3,4,5) }
{ // 数组展开 这样会将后面的数组展开 一个一个输出 console.log(...[1,2,3,4,5]); }
在函数调用后返回另一个函数的调用,这种称之为尾调用,如果后面有任何其他任何运算,都不是尾调用
{ // 尾调用 function fn2(){ console.log('尾调用'); } function fn1(){ // 在函数调用后返回另一个函数的调用,这种称之为尾调用,如果后面有任何其他任何运算,都不是尾调用 return fn2() // return fn2()+1 //这就不是尾调用了 因为有运算 } }
十六、箭头函数
简写 :如果参数列表只有一个,可以省略小括号;如果函数体中只有一句代码,可以省略大括号;如果这句代码,作为函数的返回值,可以省略retnrn关键字
特点:更短的函数、不绑定this、箭头函数没有argument
{ // 声明函数 const arrow = (x) => { // console.log("箭头函数",x); return x * 2 } arrow(123); }
{ // 简写 :如果参数列表只有一个,可以省略小括号;如果函数体中只有一句代码,可以省略大括号;如果这句代码,作为函数的返回值,可以省略retnrn关键字 const arrow = x => x * 2 arrow(4) console.log(arrow(3)); }
{ const obj = { name: 'apple', age: 8, sum() { window.setTimeout(()=> { console.log(this); console.log(this.age); }, 1000) } } obj.sum() }
js中什么时候使用箭头函数?
Object.method() (也就是想要执行这个函数的话,需要某个对象点方法名的方式的话)调用的话就用普通函数,其他情况下都用箭头函数
十七、类的概念&类的封装及继承
- es5的时候是通过构造函数来实现类的功能的
{ // es5的时候是通过构造函数来实现类的功能的 function Person(name,age){ this.name=name this.age=age } Person.prototype.say=function(){ console.log(`大家好,我叫${this.name},我今年${this.ageage}岁了`); } const p=new Person("小明",18); console.log(p); p.say(); }
- es6 改造es5实现类的方式
{ // es6 改造es5实现类的方式 class Person{ constructor(name,age){ this.name=name this.age=age } say(){ console.log(`大家好,我叫${this.name},我今年${this.ageage}岁了`); } } const p=new Person("小明",18); console.log('class',p); console.log(typeof Person); p.say(); }
- 类的继承
{ // 类的继承 class Person{ constructor(name='p'){ this.name=name; } } class Child extends Person{ constructor(name='c',age=10){ //super关键字 要放在构造函数的最前面 作用:调用父类的构造方法 super(name) this.age=age } } console.log("继承",new Child()); }
{ class Person{ constructor(name='东方'){ this.name=name; } //类 中的get和set看上去是一个函数,但是其实它不是函数而是一个属性 get fullName(){ // "\xa0" 空格的转义符 return this.name+"\xa0"+"青苍" } set fullName(value){ this.name=value } } const p=new Person(); console.log("get",p.fullName); p.fullName="小兰花" console.log("get",p.fullName); }
十八、静态方法
只在当前类中可用,其他子类中不能用,不能用于实例对象
通过static关键字修饰的方法就是静态方法
{ // 静态方法 //只在当前类中可用,其他子类中不能用,不能用于实例对象 class Person{ // 静态属性,这种写法在java中是可以的,但是在js中会报错 // static age=10 会报错 constructor(name='p'){ this.name=name; } // 通过static关键字修饰的方法就是静态方法 static sayHello(){ console.log("static方法"); } } const p=new Person(); // p.sayHello() 无法调用 Person.sayHello() //可调用 // 静态属性 Person.age=10; console.log(Person.age); }
十九、import和export
// 按需导入 需要:html文件中需要更改为<script src="./src/demo5-4-1.js" type="module"></script> // 并且运行html是需要以服务的方式运行,不要以文件方式运行 // import {a,say} from "./demo5-4-2.js" //以服务方式可以运行 // console.log(a); // say() // 全部导入 import * as aaa from "./demo5-4-2.js" //将获取来的都存到aaa这个对象里 console.log(aaa); // import bbb from "./demo5-4-2.js" console.log(bbb);
// 下面这种方式相当于在默认的export对象中加入某个属性 // export let a=3; // export function say(){ // console.log('hello'); // } // export class test{ // sayHello(){ // console.log('sayhello'); // } // } export default{ b:5, say(){ console.log("default say"); } }
二十、什么是异步编程及js的异步实现
{ // 异步 console.log(1); setTimeout(()=>{ console.log(2); },0) console.log(3); } //输出 1 3 2
二十一、 Promise
回调地狱
// 回调地狱 // 一层回调 function ajax(cb){ setTimeout(()=>{ cb && cb();//判断往没往里传参数,传了为true,回调,不传为false,cb()会报错 },1000); } ajax(()=>{ console.log('任务1'); }) // 两层回调 // 需求:在执行完任务1之后执行任务2,定义一个回调函数的时候要定义一个参数来接收第二个回调函数 function ajax(cb){ setTimeout(()=>{ cb&&cb(()=>{ console.log('任务2'); }) },1000) } ajax((cb2)=>{ console.log('任务1'); setTimeout(()=>{ cb2&&cb2(); }) })
Promise改造回调函数
function ajax(){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve() },1000) }); } console.log(ajax()); ajax().then(()=>{ console.log('任务1'); })
// 针对两次回调为例,用promise方法 function ajax() { return new Promise((resolve, reject) => {//resolve 代表正确 reject代表错误 setTimeout(() => { resolve() }, 1000) }); } ajax().then(()=>{ console.log('任务1'); return new Promise((resolve, reject) => { setTimeout(()=>{ resolve() },1000) }).then(()=>{ console.log("任务2"); }) })
使用catch方法捕获错误
function fn(num){ return new Promise(()=>{ if(typeof num==="number"){ resolve(num) }else{ const err=new Error("请输入数字") reject(err) } }) } fn(3).then(n=>console.log(n)) .catch(e=>console.log(e)
Promise.all 方法
// Promise.all 方法 const imgUrl1='https://img2.baidu.com/it/u=1641927010,1941805886&fm=253&fmt=auto&app=138&f=JPEG?w=890&h=500' const imgUrl2='https://img2.baidu.com/it/u=763721447,473124631&fm=253&fmt=auto&app=120&f=JPEG?w=690&h=388' const imgUrl3='https://img2.baidu.com/it/u=3450065074,1356681687&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=750' function getImage(url){ return new Promise((resolve,reject)=>{ const img=document.createElement("img") img.src=url; img.onload=()=>resolve(img) img.onerror=()=>reject(err) }) } // 渲染所有图片的方法 function showImage(imgs){ imgs.forEach(item => { document.body.appendChild(item); }); } function showFirstImage(ima){ document.body.appendChild(img) } // 用来判断参数(数组)中所有的promise是否全部执行完毕,当全部执行完毕之后才会执行.then Promise.all([getImage(imgUrl1),getImage(imgUrl2),getImage(imgUrl3)]).then(showImage) // 用于希望多个promise函数当中只要有一个执行完毕就会触发.then Promise.race([getImage(imgUrl1),getImage(imgUrl2),getImage(imgUrl3)]).then(showFirstImage)
二十二、interator 方法
{ // interator 方法 const arr=[1,2,3] const fn=arr[Symbol.interator]() console.log(fn); console.log(fn.next()); console.log(fn.next());//next 他自己就往下输出了,直至完毕,done变为true console.log(fn.next()); console.log(fn.next()); }
const obj={ name:'东方青仓', age:20, gender:'male', [Symbol.interator](){ let index=0; // 通过values获取对象中所有的属性值 const values=Object.values(this) return { next (){ if(index<values.length){ return{ value:values[index++], done:false } }else{ return{ done:true } } } } } } for(const value of obj){ console.log(value); }
二十三、Generator
在Generator使用之前需要安装一个依赖 npm i babel-polyfill -s这个依赖可以是babel转义es6里一些新的API
然后将node_modules里面的polyfill.js复制到外面,再到index.html中引入这个js文件
const say=function*(){ //加上*号,就是generator函数 yield 'a' //相当于return 但是return只能return一次,而yield可以多次 yield 'b' yield 'c' yield 'd' } const fn=say(); console.log(fn.next()); console.log(fn.next()); console.log(fn.next()); console.log(fn.next()); console.log(fn.next());
{ let obj={ a:1, b:2, c:3, } obj[Symbol.iterator]=function*(){ for(const k of Object.keys(obj)){ yield obj[key] } } for(let value of obj){ console.log(value); } }
应用场景
// 应用场景 // 状态机:任何时候都只有一定数量的状态 const state=function*(){ while (true) { //死循环 yield 'success' yield 'fall' yield 'padding' } } const stateDate=state(); console.log(stateDate.next()); console.log(stateDate.next()); console.log(stateDate.next()); console.log(stateDate.next()); console.log(stateDate.next()); console.log(stateDate.next()); console.log(stateDate.next()); }
二十四、async(必须搭配await用)
{ // async async function fn(){ await console.log(1); await console.log(2); await console.log(3); } fn(); }
举个小例子:(以下代码为异步实现,log内容会一起输出显示)
{ function fn1(){ setTimeout(()=>{ console.log('任务1'); },1000) } function fn2(){ setTimeout(()=>{ console.log('任务2'); },1000) } function fn3(){ setTimeout(()=>{ console.log('任务3'); },1000) } function init(){ fn1(); fn2(); fn3() } init();
让函数一个一个蹦出来 变成了同步的一种形式
{ // 让函数一个一个蹦出来 变成了同步的一种形式 function fn1(){ return new Promise(resolve=>{ setTimeout(()=>{ console.log('任务1'); resolve(); },1000) }) } function fn2(){ return new Promise(resolve=>{ setTimeout(()=>{ console.log('任务2'); resolve(); },1000) }) } function fn3(){ return new Promise(resolve=>{ setTimeout(()=>{ console.log('任务3'); resolve(); },1000) }) } async function init(){ await fn1(); await fn2(); await fn3(); }
{ function f(){ return new Promise(resolve=>{ resolve('东方大强三界最强') }) } // function f2(){ // fn().then(response=>(){ // console.log(response); // }) // } async function f2(){ const x= await f() console.log(x); } f2(); }