添加小程序,兑换各种视频教程/数据资源。
1. 常量:不能被重新赋值。
//ES5中声明一个常量,并挂载到window下:
Object.defineProperty(window,'PI',{
value:3.1415926,
writable:false, //只读不能修改
})
console.log(window.PI)
//ES6中声明一个常量: const声明一个常量,初始时必须赋值,不能const a; 会报错。
const PI=3.1415926;
console.log(PI)
2. 作用域:块作用域。
//ES5的作用域:var声明变量。
const callbacks=[]
for(var i=0;i<=2;i++){
callbacks[i]=function(){
return i*2
}
}
console.table([ //以表格的方式输入打印
callbacks[0](); //值为6
callbacks[1](); //值为6
callbacks[2](); //值为6
])
//解说:因为for循环中用的是var声明的变量,用到的是变量提升,即在函数体function(){return i*2}中,用到的是i这个变量,而不是i这个变量的值的引用,即仍然会保留i*2这个表达式,而不是0*2或1*2或2*2或3*2,保留的i*2这个表达式会有一个闭包。当代码执行到console.table()之前时,callbacks[0]=function(){return i*2},callbacks[1]=function(){return i*2},callbacks[2]=function(){return i*2},当执行 callbacks[0/1/2]()时,i已经变成3了;
//ES6的作用域:let声明变量。
const callbacks1=[]
for(let j=0;j<=2;j++){
callbacks1[j]=function(){
return j*2
}
}
console.table([ //以表格的方式输入打印
callbacks1[0](); //值为0
callbacks1[1](); //值为2
callbacks1[2](); //值为4
])
//解说:for循环中let声明的变量,会有一个以{}为基准的块作用域的,即每次for循环,以for循环后的{}为一个作用域,并将let声明的j变量的值,保存下来,供给其里面的闭包使用,当代码执行到console.table()之前时,callbacks1[0]=function(){return j*2 //其中j=0被保存下来,在调用callback1[0]()时供给其里面作用域使用},同理callbacks1[1],callbacks1[2]。
3. 箭头函数:()=>{},简化了函数,并使得{}内外的this指向一致。
//ES3/ES5中this指向:
{
var func=function(){
this.a="a";
this.b="b";
this.c={
a:"a+",
b:function(){
return this.a
}
}
}
console.log(new func().c.b()) //输出为"a+"
//解说:因为在没有箭头函数下,this的指向是该函数被调用的对象,new func().c.b()中b()函数是被new func().c调用的,即this指向c对象中的a,即a="a+"
};
//ES6中this指向:
{
var func=function(){
this.a="a";
this.b="b";
this.c={
a:"a+",
b:()=>{
return this.a
}
}
}
console.log(new func().c.b()) //输出为"a"
//解说:因为在有箭头函数下,this的指向是定义时this的指向,即在定义b()这个函数时(定义之前),this指向的是实例对象new func(),new func().c.b()中return this.a即return new func().a,即a="a"。
};
4. 默认参数:
{
//ES3/ES5中默认参数:
function(x,y,z){
if(x===undefined){ //或写成 x=x || 7;
x=7;
}
return x+y+z
}
}
{
//ES6中默认参数:
function(y,x=7,z=10){ //注意默认参数要写在后面。
return x+y+z
}
}
5. 可变参数:即参数个数不确定。
{
//ES3/ES5中可变参数:
function f(){
var arr=Array.prototype.slice.call(arguments);
var sum=0;
arr.forEach(function(item)=>{
sum+=item
})
return sum
}
console.log(f(1,2,3,4))
//解说:在ES5中不确定的可变参数,通过Array.prototype.slice.call(arguments),将实例化函数f(1,2,3,4)的参数存入数组中,然后从数组中获取相应的参数,并操作。
}
{
//ES6中可变参数:
function f(...arr){
var sum=0;
arr.forEach(function(item)=>{
sum+=item
})
return sum
}
console.log(f(1,2,3,4))
//解说:在ES6中不确定的可变参数,通过...arr解构参数,将实例化函数f(1,2,3,4)的参数存入数组中,然后从数组中获取相应的参数,并操作。
}
{
//ES5中合并数组:arr1.concat(arr2)
var arr1=[1,2]
var arr2=[3,4]
var arr=arr1.concat(arr2) //arr=[1,2,3,4]
}
{
//ES6中合并数组:...
var arr1=[1,2]
var arr2=[3,4]
var arr=[1,2,...arr2] //arr=[1,2,3,4]
}
6. 对象代理:即私有变量/方法(保护数据不被修改),即当前对象内部可用,外部无法调用。
{
//ES3中保护数据不被修改:模拟数据保护
var Person=function(){
var data={
name:"Ace",
sex:"男",
}
this.get=function(key){
return data[key]
}
this.set=function(key,value){
if(key !=='sex'){
data[key]=value
}
}
}
var p=new Person(); //声明一个实例
p.get('name'); //获取数据
p.set('name','阿哲'); //修改数据,如果是不能修改的变量sex则不会报错,也不会修改其值。
}
{
//ES5中保护数据不被修改:通过声明常量的方式
var Person={
name:"Ace",
}
Object.defineProperty(Person,'sex',{
value:"男",
writable:false
})
Person.name; //获取数据
Person.name="阿哲"; //修改数据,如果是不能修改的变量sex则会报错
}
{
//ES6中保护数据不被修改:代理对象
let Person={
name:"Ace",
sex:"男"
}
let person=new Proxy(Person,{ //用代理对象,使得外部操作person,而不是操作Person.
get(target,key){
return target[key]
},
set(target,key,value){
if(key !=='sex'){
target[key]=value
}
}
})
person.name; //获取数据
person.name='Ace'; //修改数据,如果是不能修改的变量sex则会报错。
}
7. 变量声明:var 和let。
{
//ES5中:
console.log(a); //不会报错,因为声明提前(即变量预解析),但是输入为undefind
var a=1; //var声明变量,会有声明提前(即变量预解析),可以重复声明
var a=5;
b=2; //这种方式声明变量,是全局变量。
{
var c=1
}
console.log(c) //可以输入c=1,因为var 不支持块级作用域
}
{
//ES6中:
console.log(a; //会报错,因为不存在声明提前(即变量预解析),即未声明变量a
let a=1; //let声明变量,没有声明提前(即变量预解析),且同一作用域下不允许重复声明,且支持块级作用域。
let a=2; //会报错,因为不允许重复声明
{
let c=1
}
console.log(c) //会报错,因为let 支持块级作用域,其let声明的变量,只能在当前{}块级里面使用。
}
8. 解构赋值:
{
//ES5中:直接声明变量赋值
var obj={
name:"Ace",
age:27
}
var name=obj.name;
var age=obj.age;
var arr=[1,2,3,4]
var f=arr[0]
var s=arr[1]
}
{
//ES6中:解构赋值
var obj={
name:"Ace",
age:27
}
var arr=[1,2]
var {name,age}=obj; //声明的变量名name、age与对象obj的属性名name、age一致。
var [f,s]=arr; //依次对应数组arr从头开始的下标
console.log(name,age); //输出'Ace'和27
console.log(f,s); //输出1和2
var {job}=obj;
console.log(job); //因为obj中没有job这个属性名,所有该操作是声明变量job,并为obj声明属性名job=underfind,即最后输出为underfind。arr同理,若arr中不存在改下标对应的值,则为underfind。
var {name:n='阿哲'}=obj; //若obj中没有name属性名,则定义一个name属性名='阿哲',声明一个n变量,其值等于obj.name;
console.log(n)
var [f,s,t=3]=arr; //arr同理
var [f,,t]=arr; //只去数组的下标为0和2的值赋值给变量f,t。
var {name,...o}=obj; // ...o指的是obj去掉obj.name属性后剩余的对象,即o={age:27}
var [f,...a]=arr; //同理a=[2]
}
9. Set:es6中新的数据结构,如{'a','b','c',1,2,NaN,false},类似数组和对象的结合体,没有重复值,NaN和NaN也不重复。key=value值;
{
//ES6中set新的数据结构类型:
var s=new Set([1,1,'a',false,NaN,NaN])
console.log(s) //输出是set类型的{1,'a',false,NaN},去掉重复项
s.add('c') //修改并在s尾部添加'c'值{1,'a',false,NaN,'c'},如果该值存在,就不会重复添加。
var len=s.size //返回s中元素的个数
var isDel= s.delete('c') //删除指定的值,并修改s,如果存在就删除成功true,否则不存在就返回false
var isHas=s.has("c") //判断s中是否存在某个值,并返回true、false
s.clear() //清空s
s.forEach((item,index,set)=>{
console.log(item,index,set) //item和index一样都是当前项的值,而set=s。
})
var keys=s.keys(); //和s.values()一致,类似python的生成器,依次执行,执行完整个s的长度后{value:underfind,done:true} s.values().next()同理,因为key=value; s.entries().next()类似。返回{value:[1,1],done:false}即每个value是item和index组成的数组,而set数据类型item=index
console.log(keys.next()); //返回{value:1,done:false} done:false表示未执行完,执行为true
console.log(keys.next()); //返回{value:'a',done:false}
}
10. Map:es6中新的数据结构,{key=>val}如{'a'=>'Ace'},,支持任意类型(数组,对象,数字,字符串,布尔)的key,但没有重复的key;
{
//ES6中Map新的数据结构类型:
var m=new Map(['name','Ace'],['age',27])
console.log(m) //输出是Map类型的{'name'=>'Ace','age'=>27},去掉重复项
m.get('name') //获取m中键名为name的值
var len=m.size //返回m中元素的个数
m.set({},2) //设置/添加m中键值对{}=>2,注意m.set({},2)与m.set({},3)中{}不重复,因为{}是新声明的内存地址指向不一样,如果var obj={}m.set(obj,2)与m.set(obj,3),后者键值会覆盖前者值。
var isDel= m.delete('name') //删除指定的值,并修改m,如果存在就删除成功true,否则不存在就返回false
var isHas=m.has("age") //判断m中是否存在某个值,并返回true、false
m.clear() //清空m
m.forEach((item,index,map)=>{
console.log(item,key,map) //item/key分别是当前项的键/值,而map=m。
})
var keys=m.keys(); // 与m.entries()和m.values()类似,类似python的生成器,依次执行,执行完整个m的长度后{value:underfind,done:true} m.values().next()同理; m.entries().next()类似。返回{value:['name','Ace'],done:false}即每个value是item和key组成的数组
console.log(keys.next()); //返回{value:'name',done:false} done:false表示未执行完,执行为true
console.log(keys.next()); //返回{value:'age',done:false}
}
11. 遍历接口:a.keys() ,a.entries()和a.values()。
{
var arr=[1,2,3]
console.log(arr[Symbol.iterator]) //查看该数据类型arr是否有部署遍历接口
var k=arr.keys() //keys(),values(),entries()同样用法
console.log(k.next()) //{value:1,done:false}
console.log(k.next()) //{value:2,done:false}
console.log(k.next()) //{value:3,done:false}
console.log(k.next()) //{value:undefined,done:true}
//自己封装函数,模拟遍历接口keys(),values(),entries()。
var func=(arr)=>{
var i=-1
return ()=>{
i++
var val=i<arr.length?i:undefined;
var done=i<arr.length?false:true;
next:()=>{
value:val,
done:done
}
}
}
var k=func(arr);
console.log(k.next()) //{value:1,done:false}
console.log(k.next()) //{value:2,done:false}
console.log(k.next()) //{value:3,done:false}
console.log(k.next()) //{value:undefined,done:true}
}
12. WeaMap:弱应用数据结构{key=>val},与Map数据结构类似,不同点:WeakMap的key不支持非对象类型(如数字),Map的key支持任意类型,且WeakMap不支持for...of...循环,Map支持for...of...循环,如果Map的key=null,m仍是{key=>val},而WeakMap的key=null,m则为是{}。相同点:都有m.get(),m.set(),m.has()。
{
//ES6弱应用:WeakMap
var m=new Map()
var wm=new WeakMap()
var p1={
name:"Ace",
age:27
}
m.set(p1,"人物p1") //Map类型中key可以为任意类型数据
wm.set(p1,"人物p1") //WeakMap类型中key不能为非对象类型数据(如数字)
p1=null
console.log(m) //输出Map类型的{{name:"Ace",age:27}=>"人物p1"}
console.log(wm) //输出WeakMap类型的{}
}
13. class基本使用:ES6的简化构造函数和原型对象:参考https://blog.csdn.net/qq_42231156/article/details/83014082。
{
//ES5中模拟定义类:通过构造函数定义类
function Person(name,age){
this.name=name; //类属性
this.age=age;
}
Person.prototype.say=function(){ //在原型链上定义一个公共方法。
console.log("say..............")
}
var p1=new Person("Ace",27) //实例化一个类型
p1.say() //调用类方法
}
{
//ES6中class定义类:class
class Person{
construcor(name,age){ //类属性,
this.name=name;
this.age=age;
}
staic isHuman(obj){ //静态方法,不能通过实例对象p1.isHuman()调用,需要通过Person.isHuman()调用
return obj instanceof Person //instanceof 判断某个属性是否在某个方法中
}
say(){ //类方法,实例化对象调用,如p1.say()
console.log("I am "+this.name)
}
eat(){
console.log("eat..............")
}
}
class Person (name,age){
this.name=name;
this.age=age;
staic isHuman(obj){
return obj instanceof Person
}
say(){
console.log("I am "+this.name)
}
eat(){
console.log("eat..............")
}
}
Person.money=3000 //静态属性
var p1=new Person("Ace",27) //实例化一个类型
p1.say() //调用类方法
}
14. Symbol:ES6新增数据类型,代表独一无二的值。 Symbol可作为对象,Symbol也可作为数据类型(不能算术运算(+,-,*,/),不能强制转换为字符串,整数,但是可以转换为布尔类型Boolean(a)或 !!a ,注意!!a表示转换为布尔类型)。
{
//ES6中Symbol:代表独一无二的值。
var a=Symbol("a"); //注意Symbol("a") !=Symbol("a")或Symbol() !=Symbol(),因为Symbol()是独一无二的值,而"a"参数只是该值得一个识别标记
console.log(a) //输入Symbol(a)
//常应用于:如向不确定键个数和键名的对象中,添加一个键值对,且不修改原来已有的键值对。即避免添加键值对的时候,覆盖了原有键值对。
//(一)Symbol作为对象:
var obj={
a:1
}
obj["a"]=3 //修改了原有键值对a=1修改为a=3
var a=Symbol("b")
obj[a]=3 //通过Symbol代表独一无二的值的特征,添加一个键值对,注意这a是变量不是'a'
console.log(obj) //输出{a:1,Symbol(b):3},但是该obj的for..in.. 循环,不能循环键为,Symbol的项,可以通过Object.getOwnPropertySymbols(obj)返回obj所有Symbol键组成的数组[Symbol(b)]
//(二)Symbol作为数据类型:不能算术运算(+,-,*,/),不能强制转换为字符串,整数,但是可以转换为布尔类型Boolean(a)或 !!a ,注意!!a表示转换为布尔类型
console.log(Boolean(a)) //输出true
console.log(!!a) //输出true
}
15. ES6新增字符串API操作:includes,startWith,endWith,repeat。
{
//1.判断字符串是否包含某个字符
var str="helloword"
str.indexOf("lo") !==-1 ; //ES5中通过indexOf判断是否为-1
str.includes('lo'); //ES6中通过includes判断true/false
//2. 判断某个字符串是否以某个字符开头/结尾
str.startWith('hel') //ES6中通过startWith/endWith判断true/false
str.endWith('hel')
//3. 将某个字符串重复n次形成新的字符串
var newStr=str+str+str; //ES5通过字符串拼接
var newStr=str.repeat(3) //ES6通过repeat()函数。
//4. 对于长字符串换行的处理
var html="<ul>" //ES5通过字符串拼接
+"<li></li>"
+"</ul>"
var html=` //ES6中通过模板字符串,如果存在变量,就用${},中括号里面支持函数${fn()},表达式,算术运算,三目运算,但是不支持条件和循环语句
<ul>
<li class='${a}'></li>
</ul>`
}
15. ES6新增数组API操作:of,isArray,from,find,findIndex。
{
//1. 声明一个数组:
var arr=new Array(10) //长度为10,且每个元素为空的数组
var arr=new Array(10,20,30) //数组[10,20,30]
var arr=Array.of(10) //数组[10],与new Array(10)不同结果。
//2. 类数组
var els=document.all //是由所有页面标签组件的类数组,但是仍不是数组
var els=document.getElementsByName("*") //是由所有页面标签组件的类数组,但是仍不是数组
var arrEls=Array.from(els) //将类数组转换为数组
//3. 判断是否是数组
Array.isArray(els) //判断是否是数组,类数组不是数组
//4. 从数组中查找到第一次满足某个条件的值,如查找到第一个大于30的值
var arr=[10,20,50,40]
var val=arr.find((item)=>{ //val最后执行的值是50
return item>30
})
//5. 从数组中查找到第一次满足某个条件的值的下标,如查找到第一个大于30的值的下标
var arr=[10,20,50,40]
var valIndex=arr.findIndex((item)=>{ //valIndex最后执行的值是2
return item>30
})
//6. 数组解构的引用,求数组中最大值和数组去重
var arr=[10,25,11,1,50]
console.log(Math.max(...arr)) //不能Math.max([10,25,11,1,50])
arr=[...new Set(arr)] //解构的去重应用
}
16. ES6新增对象API操作:assin,is,keys(),values(),entries()。
{
//1. ES6中对象的属性方法简写
var a=1
var obj={
a, //属性名和变量名一致时,简写
b:1,
fn(){
console.log("将ES5中fn:function(){}简写")
}
}
//2. ES6中对象的键名是个变量
var attrname="width"
var objname={}
var attrfn="fn"
var obj={
[attrname]:"10px", //obj.width获取属性值
[objname]:"hello", //注意,对象不支持以对象作为键名。所有返回[object object]:"hello"
[attrfn](){ //obj.fn()调用函数
console.log("fn是函数名")
}
}
//3. ES6判断数据相等
console.log(1=='1') //隐示转换,只判断值是否相等,不判断类型是否相等
console.log(1 === '1') //同时判断值和类型是否都相等
console.log(NaN === NaN) //返回false
console.log(Object.is(NaN,NaN)) //类似===,但是在处理NaN时,返回的是true,与===不同。
console.log(Object.is({},{})) //返回false,因为对象的指向地址不一致。
//4. ES6合并对象
function move(obj){
var defaultobj={
a:1
}
var para={ //ES5的合并对象的方法
a:obj.a || defaultobj.a
}
var para={}
Object.assign(para,defaultobj,obj)//ES6的合并对象的方法,后者覆盖前者
console.log(para)
}
move({a:2})
//5. ES6的对象的keys(),values(),entries()
var obj={
a:11,
b:22
}
obj.keys() //返回obj中所有key组成的数组,['a','b']
obj.values() //返回obj中所有value组成的数组,[1,2]
obj.entries() //返回obj中所有key和value组成的二维数组,[['a',1],['b',1]]
//6. ES6的解构
var obj1={
a:1,
b:2
}
var obj2={
...obj,
c:3
}
}
17. ES7的async/ await:解决异步编程,ES6用 promise。
async getInfoAxios(){ //将该函数封装为async异步函数,
var _this=this;
const url= 'https://z1.m1907.cn/api/v/';
const data= {};
const res= await get(10); //通过await等待并接收其执行结果,必须async声明异步。await 后面一般接的是promise封装的异步函数
console.log(res)
}
function get(val){
return new Promise((res,req)=>{
setTimeout(()=>{
if(var<100){
resolve("成功")
}else{
reject("失败")
}
})
})
}
18. ES6的promise:解决异步编程,回调函数也能解决异步编程,但是可能会出现 “回调地狱” 。
{
//1.ES6的基本用法:
var p=new Promise((resolve,reject)=>{
console.log("测试")
return resolve("成功")
})
p.then((msg)=>{ //then(参数1,参数2),参数1是接收成功resolve()之后的执行函数,参数2是接收成功reject()之后的执行函数,参数2可以不写,也可单独用p.catch(()=>{ console.log("失败")})去处理reject()之后的执行函数。
console.log("msg:",msg)
},()=>{
console.log("失败")
})
}
{
//2.ES6的宏任务与微任务:先执行宏任务再执行微任务。
console.log(1)
setTimeOut(()=>{
console.log(2)
},1000)
var p=new Promise((resolve,reject)=>{
console.log("3")
return resolve()
})
p.then((msg)=>{
console.log(4)
})
console.log(5)
//解说:以上的执行顺序结果为:13542。因为 console.log(1), setTimeOut(()=>{}), p.then,console.log(5)是宏任务,(msg)=>{console.log(4)}是微任务。
//3.ES6的promise的链式结构:
var p=new Promise((resolve,reject)=>{
console.log("3")
return resolve()
})
var res=p.then(()=>{}).then(()=>{}).then(()=>{}).catch((err)=>{}).finally(()=>{}) //也可写成res.catch((err)=>{})
//4.ES6的promise的静态方法:Promise.all(),Promise.race(),Promise.resolve(),Promise.reject()
var p1=new Promise((res,rej)=>{
setTimeout(()=>{res("a")},1000)
})
var p2=new Promise((res,rej)=>{
setTimeout(()=>{res("a")},1500)
})
var p3=new Promise((res,rej)=>{
setTimeout(()=>{res("a")},500)
})
var p=Promise.all([p1,p2,p3])
console.log(p.then(data=>console.log(data))) //返回['a','b','c'], 即所有Promise都执行完的值组成的数组。
var p=Promise.race([p1,p2,p3])
console.log(p.then(data=>console.log(data))) //返回c, 即最先执行完的Promise的值。
Promise.resolve().then(()=>{}) //直接处理异步成功之后的操作
Promise.reject().then(()=>{}) //直接处理异步失败之后的操作
//5.ES6通过promise封装动画移动函数。
function movePromise(obj,attr,target,time){ //obj表示通过id或class获取到的标签,attr是标签的属性,如width,height,top,left....,target是最后要移动到的位置,time是动画执行的时间。
return new Promise((res,rej)=>{
var b=parseInt(getComputedStyle(obj)[attr]);
var c=target-b;
var temp=new Date().getTime();
var timer=setInterval(()=>{
var t=new Date().getTime()-temp;
if(t>=time){
clearInterval(timer)
t=d
}
var v=c/d*t+b;
obj.style[attr]=v+"px";
if(t===d){res()}
})
})
}
var box=document.getElementById("box")
movePromise(box,'width',500,100).then(()=>{ movePromise(box,'height',500,100)})
}
19. ES6的模块化:export(导入模块),import(导入模块) 。<script src="a.js" type="module " defer></script>,是export/import模块化,其中defer是type="module "时默认存在。import
语句只能在声明了type="module"
的script
的标签中使用,此外还有一个类似函数的动态import()
,它不需要依赖type="module"的script标签
。参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import。
//b.js文件中:export导出内容
{
var m=1;
var n=2;
var add=()=>{return m+n}
console.log("import './b.js'只导入这块")
export {m,n,add} //指定导出需要{}
export default 100 //默认导出,不需要{}
}
//a.js文件中:import导入内容
{
import {m,n,add} from './b.js' //导入./b.js文件中指定导出的值,需要{m,n,add}
import d from "./b.js" //导入./b.js文件中最后一个export default导出的数据赋值给d,不需要{b}
import d,{m,n,add} from './b.js'
import './b.js' //只导入b.js中 console.log("import './b.js'只导入这块")这个部分。
import * as all from "./b.js" //导入./b.js文件中所有exort的数据,命名为all,all.m,all.n,all.add,all.default。
}
20. ES6的异步编程:js是单线程执行,但是在执行过程中会遇到一些比较耗时的代码块任务(如定时器,ajax),就需要通过新开一个线程来执行这个耗时任务,这个耗时任务又叫异步任务,而之前的单线程继续向下执行。