ES6
是JavaScript语言的下一代标准,是使得js语言可以编写复杂的大型应用程序,成为企业级开发语言
1.用let声明变量
- 拥有块级作用域 (不会污染全局)
也就是说如果你在大括号内用 let 定义一个变量,在外面是访问不到的,而var定义却可以
- 不允许重复声明(不会覆盖,会报错)
- 声明不提升
也就是不可以未定义就调用(因为存在暂存性死区) ,而var可以将声明提前
- 不与顶层对象挂钩
- var会自动跟window关联(跟window挂上),也就是当你用var定义时,打印是可以写成window.变量名,是可以访问到的 —————————
- 但是let定义的话(不跟window挂钩),再用 window.变量名 只会得到undefined
2.const声明常量
- 必须有初始值
- 不能重复定义
- 拥有块级作用域
- 声明不提升
- 不与顶层对象挂钩
问:用const修饰的变量就一定不能被改吗?
const myobj={
name:"kerwin",
age:100
}
//myobj="dwa",这样肯定不行,不能改变
//复杂类型,所以myobj是指向复杂类型的一个地址,那我们通过地址myobj找到对象中的key然后改变它的值
//这样就可以
myobj.name="tiechui"
但是如果我们用的是复杂类型,但又不希望它改变,我们就用fresze来冻结住它(冻结住对象中的每一个属性)
但是这个只能冻住一级属性,如果freeze里再有复杂类型,是冻不住的(除非你再用递归方式写一个小函数)
const myobj=Object.freeze({
name:"kerwin",
age:100
})
myobj.name="tiechui"
//但是你此时再这样改,就没有效果了
3.变量解构赋值
就是快速地从对象或者数组中取出成员的一个语法方式
数组的解构赋值
- 一个最简单的数组解构赋值
let arr=[1,2,3]
let[a,b,c]=arr
- 不借助第三个变量,实现元素交换
let x=1;
let y=2;
[y,x]=[x,y]
console.log(x,y);
比如我现在想取3这个值
let arr=[1,2,3]
let[,,a]=arr
console.log(a) //3
如果数组嵌套的话,我想取2,4
let arr=[1,[2,3,4],5,6]
let [a,[b,,d],c]=arr
console.log(b,d) //2,4
- 有默认值
//后端返回一个空数组
let [x]=[]
console.log(x)//undefined
//x有默认值,且接受一个空数组,则会显示其默认值
let [x=1]=[]
console.log(x) //1
let [x=1]=[100]
console.log(x) //100
对象的解构赋值
let obj={
name:"kerwin",
age:100
}
let {name,age}=obj
//let {age,name}=obj
console.log(name,age)//kerwin 100
- 这里跟数组不一样的是,name和age可以调换位置,因为它是按照key值找的 (跟顺序无关)
- 而数组不可以,数组要对号入座
- 而如何让应对重命名导致的报错呢?
即给它来个新名字(重命名)
:新名字
let code="AAAA"
let res={
code:200,
data:"11111"
}
//这里重命名一下
let {data,code:co}=res
console.log(data,co)
- 如果data的value是个对象呢?如果我们此时想访问这个对象里的值呢?
let code="AAAA"
let res={
code:200,
data:{
list:["aaa","bbb","ccc"]
}
}
let {data:{list},code:co}=res
console.log(list,co)
- 想拿list里面的某个元素同理
let {data:{list:[x,y,z]},code:co}=res
console.log(x,co)
字符串的解构赋值
let myname="kerwin"
let [x,y,z]=myname
console.log(x,y,z) // k e r
//还可以获取字符串长度
let {length}=myname
console.log(length) //6
4.模板字符串
原来:
//这样写不能换行(除非在换行处加一个\反斜杠)
//但是这样添加的话,里面的东西就是写死了的
let oli="<li>kerwin</li>"
//字符串拼接
let name="tiechui"
let lio="<li>\
"+name+"\
</li>"
使用模板字符串
用单引号:
//可以换行,也不用拼接
let name="tiechui"
let oli=`<li>
${name}
</li>`
例子:比如你想在页面的ul中插入li的示例
let arr=["tiechui","kerwin","gangdan"]
let newlist=arr.map(function(item){
return `<li>
<b>${item}</b>
</li>`
})
console.log(newlist)
let oul=document.querySelector("ul")
//oul.innerHTML=newlist
oul.innerHTML=newlist.join("")
//但是数组强行展示到页面中,会强行转化为字符串,并用逗号,分割
比如只想给第一个加样式
let newlist=arr.map(function(item,index){
return `<li class="${index===0?`active`:``}">
<b>${item}</b>
</li>`
})
5.字符串与数值扩展
字符串扩展
- includes函数 :判断字符串中是否存在指定字符
(包含,开头,结尾)
后面也可以带参数,表示从哪个索引开始查找
- repeat(数字/整数/取上)
数值扩展
- 支持二进制八进制十进制写法
- Number . isFinite有限 / isNaN
减少全局性方法,使得语言逐步模块化
3. isInteger()方法 判断一个数值是不是整数
4. 极小常量Number.EPSILON
5. Math.trunc 将小数部分抹掉,返回一个整数
console.log(Math.trunc(1.2)) //1
6. Math.sign 用来判断一个数到底是正数,负数,还是0。对于非数值,会先将其转换为数值
6.数组扩展
- 扩展运算符(展开运算符)(三个点...)
let arr=[1,2,3]
//复制一个新的
//原来let arr2=arr.concat()
let arr2=[...arr]
- Array.from() 可以把类数组对象转化为真数组
- Array.of() 将一组值转化为数组,即新建数组
两个的结果区别:
但是 如果传正常的数组格式,array和array.of 没有区别
- find findIndex() 查找到第一个符合条件的元素
let arr=[11,12,13,14,15]
let res=arr.find(function(item){
return item>13
})
console.log(res) //14
let res=arr.findIndex(function(item){
return item>13
})
console.log(res) //3
- find findLastIndex(),同理
- fill() 填充,初始化数组/替换
- flat() flatMap() 扁平化
flat()破掉数组(如果是几个对象就不太行了)
let arr=[1,2,3,[4,5,6]]
let arr1=arr.flat()
console.log(arr.arr1)
效果:直接二维转一维
flatMap()
7.对象扩展
- 对象简写
就是当key的名字和value相等时,可以进行简写
比如:name,等价于name:name
- 对象属性 表达式
let name="a"
let obj={
//如果想让name变成一个变量,就给它套一个中括号
[name]:"kerwin"
}
console.log(obj)//a:“kerwin”
- 扩展运算符...ES6不支持
- 支持的是Object.assign(),快速合并对象
- Object.is() 判断两个值的相等,相当于三等于号
与===不同的是,这个可以判断NaN===NaN为true,三等于判断出来是false
8.函数的扩展
- 参数默认值
- 剩余参数...三个点
- 箭头函数
- 只有return,可以省略
- 如果返回对象要注意加()
- 如果只有一个参数,可以省略括号()
- 无法访问arguments,无法new
- 箭头函数的this指向父作用域(或者说没有this )
9.Symbol(新数据类型)
两个用途:
- 可以防止重命名覆盖
- 作为常量(统一传代码的一致性)
ES6引入了一种新的原始数据类型symbol,表示独一无二的值(加在对象的属性上),它属于js语言的原生数据类型之一。其它数据类型是:undefined、null、boolean、string、number、object
-
不能进行运算
-
如果非要想运算或者什么,就加上个toString()转一下
-
隐式转换布尔
-
不能for in遍历,如果用的话,只能返回普通属性
用object.getOwnPropertySymbols(),这个可以遍历处特殊的symbol类型数据
如果非想只用一种方法遍历出来全部,可以用Reflect.ownKeys(),把他们变成一个数组返出来
(先通过reflect转换为普通数组,再通过foreach遍历出来)
5. 作为常量
10.lterator迭代器
一个独有的内置接口
lterator的作用
两个功能:
- for in拿到的是索引值,for of拿到的是每个具体内容
- 除了for of,迭代器还有转数组的功能(前提是有迭代器)
console.log([...oli])跟之前Array.from(oli)
遍历
let arr=["kerwin","tiechui","gangdaner"]
for(let i of arr){
console.log(i)
}
Symbol.iterator是js内置的,可以访问直接对象[Symbol.iterator],访问的时不会生成新数组导致报错
遍历流程(本质)
let arr=["aaaa","bbbb","cccc"]
//把返回值赋值给一个变量然后打印出来
let iter=arr[Symbol.iterator]()
console.log(iter)
//返回的就是遍历器对象↑
//迭代器对象中有一个方法叫next
//next方法可以依次得到你数组的元素数据
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
原生默认具备lterator接口的数据结构如下
- Array
- Set
- Map
- String
- arguments对象
- NodeList对象
11.set数据结构(类似于数组)
类似于数组,但成员的值都是唯一的,没有重复的值
所以可以用于数组去重
数组去重
1.直接写好初始数组
let s1=new Set([1,2,3,2,3,4])
console.log(s1)
//转换成数组
console.log([...s1])
console.log(Array.from(s1))
2. 用add一个个添加创建
当然可以像上面那样直接初始写好数组,当然也可以先建立一个空的,然后再一个个添加
let s2=new Set()
s2.add(1)
s2.add(2)
s2.add(3)
s2.add(3)
console.log(s2)
set实例的属性和方法
console,log(s1.size) //长度
s1.add().add() //添加,也可以链式这样写
console.log(s1.has()) //判断有没有某个值,true/false
s1.delete() //删除
s1.clear() //清空
遍历
Set.prototype.keys() //返回键名的遍历器
Set.prototype.values() //返回键值的遍历器
Set.prototype.entries() //返回键值对的遍历器
Set.prototype.forEach() //遍历每个成员
这样用:
for(let i of s2.keys()){
console.log(i)
}
对复杂数据类型进行去重
let list=[1,2,2,"kerwin","kerwin",[1,2],[3,4],[1,2],{name:"kerwin"},
{age:100},{name:"kerwin"}]
function uni(list){
let res=new Set()
return list.filter((item)=>{
//对象怎么判断,用json把它转换成字符串就可以了
//先转字符串
let id=JSON.stringify(item)
//然后再判断
if(res.has(id)){
return false
}else{
//如果没有就让它加上,这样下次判断才有意义
res.add(id)
return true
}
})
}
console.log(uni(list))
12.Map结构(类似于对象)
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
创建
1. 写好初始值
let m1=new Map([
["name","kerwin"],
["age",100],
[{a:1},"大连"]
])
console.log(m1)
2. 用set一个一个设置
let m2=new Map()
m2.set("name","kerwin")
m2.set("age",100)
console.log(m2)
方法
- set设置
- get获取
- has判断有没有
- delete删除
- clear清除
- size获取长度
遍历
跟 for of 或者 foreach 结合使用
- keys()
- values()
- entries()
13.Proxy代理
proxy如其名,它的作用是在对象和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值,以及实例化等等多种操作,都会被拦截住,经过这一层我们可以统一处理,我们可以认为它就是“代理器”。
方法
学proxy之前,用的是Object.defineProperties
-
拦截对象的属性被访问和修改的时候的一个拦截函数
-
这个方法是直接访问原对象(比如下面写的obj)
缺点:
- 只能拦截一个属性,如果有很多个就需要循环遍历对每一个属性进行拦截
- 只能拦截对象,不能拦截数组
<div id="box"></div>
<script>
let obj={}
//Object.defineProperties这个方法的三个参数:
//要被拦截的这个对象/要被修改拦截的属性/get和set的两个回调
Object.defineProperties(obj,"data",{
get(){
console.log("get")
return box.innerHTML
},
set(value){
console.log("set")
//设置dom
box.innerHTML=value
}
})
console.log(obj)
</script>
proxy
- 不是直接访问原对象,而是给原对象加一个代理,将来是改这个代理
- 可以拦截好多个属性
<div id="box"></div>
<script>
let obj={}
let proxy=new Proxy(obj,{
get(target,key){
console.log("get",target[key])
//必须要返回东西,不然是undefined
return target[key]
},
set(target,key,value){
console.log("set",target,key,value)
if(key==="data"){
box.innerHTML=value
}
//这个加上后,才能真正影响原对象
target[key]=value
}
})
</script>
set获取要这样
但是这里有一个易错点:当你访问一个方法的时候会报错,因为你此时的this指向的是proxy而不是你的对象
处理方法:改变this指向
14.Reflect对象(反射)
可以用于获取目标对象的行为,它与object类似,但是更易读,为操作对象提供了一种更优雅的方式,它的方法与proxy是对应的
writeable:false不可写
enumerable:false不可配置(也就是不可修改,比如删除之类的)
有什么用,跟object有什么区别
4.
对象
数组
15.Promise对象(解决回调地狱的)
16.Generator函数(手动)
后面的await和async会更好用
是ES6提供的一种异步编程解决方案(长得像同步的)
是一个状态机,封装了多个内部状态
执行generator函数会返回一个遍历器对象,也就是说,generator函数除了状态机,还是一个遍历器对象生成函数,返回的遍历器对象,可以依次遍历generator函数内部的每一个状态
基本语法(yield暂停标记)
如果想让有产出值
小应用
1.手动版本
这种写法需要手动添加,并且如果特别多的话,next那里也可能形成回调地狱
//你康这一部分,是不是很像同步代码
function *gen(){
let res=yield ajax("1.json")
console.log("第一个请求的结果",res)
let res2=yield ajax("2.json",res)
console.log("第二个请求的结果",res2)
}
let g=gen()
console.log()
g.next().value.then(data=>{
//console.log(data)
g.next(data).value.then(res=>{
g.next(res)
})
})
2.自动版本(运用了递归调用)
(前提:yield后面必须是promise对象)
function *gen(){
let res=yield ajax("1.json")
console.log("第一个请求的结果",res)
let res2=yield ajax("2.json",res)
console.log("第二个请求的结果",res2)
}
function AutoRun(gen){
let g=gen()
function next(data){
let res =g.next(data)
if(res.done)return
res.value.then(function(data){
next(data);
});
}
next();
}
AutoRun(gen)
Class语法(类语法)
原来构造函数的写法
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.say=function(){
console.log(this.name,this.age)
}
let obj=new Person("kerwin",100)
console.log(obj)
class,一种语法糖,更简便一点
class Person {
//原来构造函数那里的
constructor(name, age) {
this.name = name
this.age = age
}
//原来原型上的
say() {
console.log(this.name, this.age)
}
}
let obj = new Person("kerwin", 100)
console.log(obj)
与symbol也可以结合
let s=Symbol('say')
class Person {
//原来构造函数那里的
constructor(name, age) {
this.name = name
this.age = age
}
//原来原型上的
[s]() {
console.log(this.name, this.age)
}
}
let obj = new Person("kerwin", 100)
console.log(obj)
obj[s]()
tip:还有一种静态属性的写法,可以替代下面那个
class继承
class Student extends Person{}
super方法就是表示对一个构造函数的继承,可以直接继承父类的constructor里的,相当于给子类加了父类里的东西
有个渲染页面的实例,在20集class继承那里
Module语法(模块化语法)
1.异步加载
原来 如果在head里写的话,后面加async或者defer让script异步加载(不过更建议defer)
模块化
不过直接这样写的话,你这个1.js里面就全不能访问了
所以建议是在body那个script上写猫叫,然后用导入导出
2.私密不漏
原来
如果不想被访问到(私密),可以在名字前面加个—下划线,不过这只是个君子协定,看到这样写一般就不去访问了,但是其实理论上还是可以访问到的
模块化
- 在body里的script里写一个导入1.js中的方法
import A1 from 单引号./1.js单引号(相对路径) - 再在1.js中写一个导出
export default A1
tip:
导出名字必须写对,导入无所谓
并且default只能用一次
所以可以这样写(把你需要导的框起来)
3.重名不怕
4.依赖不乱
在哪用在哪引
如果想在3.js中调用必须这样写
TIP:第二种导入导出写法
还有一个不写default的导出方法
不过这里的导入名字必须严格写
export{
A1,
A2
}
import{A1,A2} from `./1.js`
不过这种写法导入test相同名字会有重名问题,下面是解决方案
总结就是:
另外导出这里,给每个function单独export和下面大括号一块export效果是一样的
————两种方法可以结合写
NodeJS中的模块化
但是这个一导入就是全部导入,不像es6可以部分导入(导入你想要的)
//导出规范
module.exports=A1
//当然也可以直接给上面的挂exports属性
exports.B1=B1
//导入(这个名字随便起,就是定义了一个变量)
const A1=require("./1.js")