ES6 语法
文章目录
ES6,全称ECMAScript6.0,是JavaScript的下一个版本标准,2015.06发版。
JavaScript其实是Orcale公司注册的商标。因此JavaScript正式名称是ECMAScript
1. let 与 const
-
let
-
声明的变量只在其命令所在的代码块内有效。
-
不能重复声明同一个变量
-
不存在变量提升
{ let a=0; console.log("a=%d",a); //a=0 } console.log("a=%d",a); //错误,变量a只在{}代码块内有效
-
-
const
- 声明只读变量,声明之后必须初始化且不允许改变,全局有效。
2. 解构赋值
是一种针对数组
或者对象
进行模式匹配
,然后对其中的变量进行赋值
。是对赋值运算符的扩展。
-
数组模型解构(Array)
//基本数组解构 let numbers = [1,2,3]; //将数组numbers解构对其中的变量赋值到a,b,c上 let [a,b,c] = numbers; console.log('a=%d;b=%d;c=%d',a,b,c); //a=1;b=2;c=3
//可忽略解构元素 let numbers = [1,2,3]; let [a,,c] = numbers; console.log('a=%d;c=%d',a,b,c); //a=1;c=3
//嵌套数组解构 let numbers = [1,[2,3],4]; let[a,[b],c]; console.log('a=%d;b=%d;c=%d,',a,b,c); //a=1;b=2;c=4
//不完全解构 let numbers = [1]; let[a,b] = numbers; console.log('a=%d;b=%d',a,b); //a=1;b=undefined //解构默认值(当解构赋值有匹配结果,且解构为[undefined]时,会触发默认值作为返回结果) let numbers = []; //或 let numbers = [undefined]; let[a=5] = numbers; console.log('a=%d',a); //a=5
//剩余运算符 let numbers = [1,2,3,4,5]; let[a,...b] = numbers; console.log('a=%d;b=%o',a,b); //a=1;b=[2,3,4,5]
//字符串等(在数组解构中,解构目标若为可遍历对象,皆可进行解构赋值。即可遍历的对象要实现Iterator接口) let [a,b,c,d,e,f] = 'hello'; consolo.log('a=%s,b=%s,c=%s,d=%s,e=%s',a,b,c,d,e);
-
对象模型解构(Object)
//基本对象解构 let person = {name:'zcmain',age:20}; let{name,age} = person; console.log('name=%s,age=%d',name,age); //name=zcmain,age=20
//嵌套对象解构 let School = {schoolName:'上海第一中学',Class:{className:'高三(1)班',students:35}}; let {schoolName,Class:{className,students}} = School; console.log('schoolName=%s,classname=%s,students=%d',schoolName,className,students); //schoolName=上海第一中学,className=高三(1)班,studnts=35
//忽略解构对象 let p = {p1:['hello',{p2:'world'},'gooBey']}; //忽略对象内部数组中的对象 let {p1:[x,,y]} = p; //或者let {p1:[x,{},y]} = p; console.log('x=%s,y=%s',x,y);
//不完全解构对象 let School={class:['高三(1)班','高三(2)班']}; let {class:[class1,class2,class3]} =School; console.log('class1=%s,class2=%s,class3=%s',class1,class2,class3); //class1=高三(1)班,class2=高三(2)班,class3=undefined
//剩余运算符 let {a,b,...rest} = {a:10,b:20,c:30,d:40} console.log('a=%s,b=%s,c=%o',a,b,c); //a=10,b=20,rest={c:30,d:40}
//解构默认值 let {a=10,b=5} = {a:3}; console.log('a=%d,b=%d',a,b); //a= 3,b=5
3. ES6字符串
-
字符串识别
之前判读是否包含子串,ES6之前用
indexOf
,ES6新增如下三个方法- includes():返回布尔类型,判断是否找到参数字符串。
- startsWith():返回布尔类型,判断参数字符串是否在原字符串头部。
- endsWith():返回布尔类型,判断参数字符串是否在原字符串尾部。
以上三个方法都可以接受两个参数,即后一个参数为要搜索的字符串和可选搜索起始位置索引。
注意:如果要知道字串位置,还得需要使用
indexOf
和lastIndexOf
-
字符串重复
repeat(number)
返回新的字符串,number表示字符串重复次数- number是小数则向下取整
- number是负数则报错
- number是0~-1之间小数,会取整为-0,等同于repeat零次
- number是NaN等同于repeat零次
- number是数字字符串,先将其转换为数字,否则等同于repeat零次
let hello = "Hello"; hello.repeat(3.2); //hello,hello,hello hello.repeat(-0.5); //"" hello.repeat(NaN); //"" hello.repeat("sdf"); //"" hello.repeat("2"); //hello,hello
-
字符串补全
- padStart(int,string):返回新字符串,表示用指定字符串从原字符串头部(左侧)补齐生成指定长度的新字符串
- padEnd(int,string):返回新字符串,表示用指定字符串从原字符串尾部(右侧),补齐生成指定长度的新字符串。
注意
- 第一个参数:指定生成字符串的最小长度。
- 第二个参数:用来补全的字符串,如果没有指定,默认用空格填充。
- 如果指定长度小于或者等于原字符串长度,则返回原字符串。
- 如果原字符串加上补全的字符串大于指定长度,则截去超出位数的补全字符串。
let h = "h"; h.padStart(3,'0'); //00h h.padEnd(5,'e'); //heeee
-
字符串模版
-
字符串中插入变量表达式
let name = 'zcmain'; let age = 20; let content = "Hello,My name is ${name},I am ${age+1} years old next year.";
-
字符串中调用函数
add=(a,b)=>{ return a+b; } let content = "get sum ${add(2,3)}";
-
4. 数值
-
将给定的字符串转换为指定的进制整数
//不指定进制,默认转换为十进制 Number.parseInt('12.24'); //12 Number.paraseInt("0010",2); //2
-
判断给定的参数是否为整数
Number.isInteger(1); //true Number.isInteger(1.1); //false Number.isInteger("20") //false
-
判断给定的数值是否在安全范围内
Number.isSafeInteger(Number.MAX_SAFE_INTEGER-1); //true
-
Math对象扩展
普通计算
-
Math.cbrt:计算一个数的立方根
Math.cbrt(8); //2 Math.cbrt("8"); //2,会对非数值进行转换 Math.cbrt("hhh"); //NaN
-
Math.imul:乘法(32位有符号整数)
//1与2相乘结果;等同于 a*b Math.imul(1,2); //2
-
Math.hypot:计算所有参数的平方和的平方根
Math.hypot(3,4); //5=√25 (3*3 + 4*4) Math.hypot(2,4,5,'6'); //9=√81 (2*2 + 4*4 + 5*5 + 6*6)
数字处理
-
Math.trunc:返回数字的整数部分
console.log(Math.trunc(12.34)); //12 console.log(Math.trunc("-3.1415926")); //-3 console.log(Math.trunc("3.1415926")); //3
-
Math.sign:判断数值的符号(正、负、0)
Math.sign(5); //1 Math.sign(-5); //-1 Math.sign(0); //0 Math.sign(-0); //-0 Math.sign('5'); //1 Math.sign('-5'); //-1
-
5. 对象
ES6允许对象的属性直接写变量,此时属性名默认为变量名,属性值为变量值
const name='zcmain';
const age = 20;
const person={name,age};
//等同于
const person={name:name,age:age}
-
对象的扩拓展运算符
拓展运算符
...
用于取出参数对象所有可能遍历属性然后拷贝到当前对象基本用法
let person={name:'zcmain',age:20}; //使用拓展运算符,将perosn对象中所有属性拷贝到someone对象中 let someone = {...person}; //someone = {name:'zcmain',age:20}
可用于合并两个对象
let name = {name:'zcmain'}; let age = {age:20}; let person = {...name,...age}; //person = {name:'zcmain',age:20}
注意
-
自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉
let userName={name:'zcmain'}; let userAge={age:20}; //自定义的属性name覆盖了前面拓展运算符userName对象中的name属性 let person = {...userName,...userAge,name:'Tom'}; //person={name:'Tom',age:20}
-
-
对象新方法
Object.assign:用于将源对象中所有属性复制到目标对象中
第一个参数是目标对象,后面参数为源对象
注意:
- 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的会覆盖前面的属性。
- 如果函数只有一个参数,当参数不为对象时,先将参数转换为对象然后返回。
assign的属性是浅拷贝(地址引用),改变目标对象中属性,源对象也会同步被更改
。
let target = {a:0}; let sour1 = {b:1}; let sour2 = {c:2}; Object.assgin(target,sour1,sour2); //target={a:0,b:1,c:2}
Object.is:用来比较两个值是否
严格相等
,与===
类似与===区别
Object.is(+0,-0); //false +0 === -0 //true Object.is(NaN,NaN); //true NaN === NaN //false
6. 数组与集合
数组(Array)
-
数组新创建
Array.of()
//类型可为不同类型 Array.of(1,2,3,'4',true,); //[1,2,3,4] //参数为空,返回空数组 Array.of(); //[]
-
从一个可迭代对象创建数组(返回转换后的数组)
Array.from(arrayLike[,mapFn[,thisArg]])
参数详解:
- arrayLike:想要转换的类数组对象或可迭代对象。
- mapFn(可选):map 函数,用于对每个元素进行处理,放入数组的是处理后的元素。
- thisArg(可选):用于调用指定 map 函数执行时的 this 对象
举个🌰
声明可遍历arrayLike
//声明arrayLike数组 let arrayLike = [1,2,3,4]; //参数为数组,返回与原数组一样的数组 let array = Array.from(arrayLike); //[1,2,3,4]
指定mapFn函数
注意:
如果thisArg不为空,则不可以用箭头函数,否则内部的this会指向window
let array = Array.from(arrayLike,(value)=>{ //指定mapFn函数,对原始数组每个元素进行乘2处理 return Math.imul(value,2); }); //[2,4,6,8]
指定thisArg参数,用于指定 map 对象函数执行时的 this 对象
注意:
mapFn不可以使用箭头函数,否则内部的this会指向window
//定义一个map对象 let map={ //声明fun方法 fun:(value)=>{ return Math.imul(value,2); } } //这里mapFn不可以使用箭头函数 let array = Array.from(arrayLike,function(value){ //通过this调用map对象内的fun函数 return this.fun(value); },map); //指定对象
-
转换可迭代对象
map转换成为Array
let map = new map(); map.set('key0','value0'); map.set('key1','value1'); map.set('key2','value2'); let array = Array.from(map); //[['key0','value0'],['key1''value1],['key2','value2']]
Set转Array
let set = new Set(); set.add(1); set.add(2); set.add(3); let array = Array.from(set); //[1,2,3]
字符串转换成Array
let st = 'hello'; let array = Array.from(str); //['h','e','l','l','o']
-
扩展的方法
-
Find() :查找数组中符合条件的元素,若多个符合,返回第一个元素
let array = Array.of(1,2,3,4); let result = array.find((item)=>{ return item>2; //如符合条件元素有: 3、4 ;这里返回第一个 }); //3
-
findIndex():数组中符合元素的索引,若多个符合,则返回第一个元素索引
let array = Array.of('a','b','c','d','e','c'); let result = array.findIndex((item)=>{ return item === 'c'; //符合元素的下标有:2、5;这里返回第一个符合元素的下标 }) //2
-
fill():填充。将一定范围内的数组元素内容修改为指定的值
第一个参数:用于填充的值
第二个参数:被填充的起始索引
第三个参数(可选):被填充的结束所以,默认为数组末尾
let array Array.of('a','b','c','d','e'); //把字符'hello',填充到数组的第二个元素和第三个元素之间[1,5) let result = array.fill('hello',1,5); //['a','hello','hello','hello','e']
-
copyWithin():将一定范围索引的数组元素修改为该数组另一指定范围索引的元素
第一个参数:被修改的数组起始索引
第二个参数:被用来覆盖的数组的起始索引
第三个参数(可选):被用来覆盖数组的结束索引,默认为数组末尾
let array = Array.of('a','b','c','d','e','f','g','h'); //将当前数组下标为[0,2)范围内的元素,替换当前数组下标为3的元素后面对应长度的元素上 let result = array.copyWithin(3,0,2); //['a','b','c','a','b','f','g','h']
-
遍历
键值对遍历(key(下标)-value)
let array = Array.of('a','b',1,false,{},()=>{},'c',5); for(let [key,value] of array.entries()){ console.log(key,value); } //0 a //1 b //2 1 //3 false //4 {} //5 [Function] //6 c //7 5
遍历key(下标)
let array = Array.of('a','b',1,5); for(let key of array.keys()){ console.log(key); } //0 //1 //2 //3
遍历值
let array = Array.of('a','b',1,5); for(let value of array.values()){ console.log(value); } //a //b //1 //5
-
嵌套数组转换一维数组
flat():数组铺平(只会铺平一层)
let array = Array.of(1,2,3,[4,5],6); let result = array.flat(); //[1,2,3,4,5,6]
flat(number):铺平多层
let array = Array.of(1,2,3,[4,5,[6,7]],8); //flat()默认铺平一层 let result = array.flat(); //[1,2,3,4,5,[6,7],8] //指定flat(2)铺平两层 let result = array.flat(); //[1,2,3,4,5,6,7,8]
flat(Infinity)
:全部铺平//嵌套了五层的数组,使用flat(Infinity)对其进行铺平转换为一维数组 let array = Array.of(1,['a',[false,[{},[()=>{},['A']]]]]); let result = array.flat(Infinity); //[1,'a',false,{},[Function],'A']
flatMap
:先对数组中每个元素进行处理,然后再对返回值组成的数组进行铺平注意:
flatMap()只能展开一层数组。多层则出现NaN
let array = Array.of(1,2,3,[4]); //flatmap相当于对源数组进行乘2处理后,返回值组成的新数组[2,4,6,[8]]进行一次平铺 let result = array.flatMap((value)=>{ return Math.imul(value,2); }); //[2,4,6,8] //flatMap降维多层嵌套数组失败 let array2 = Array.of(1,2,3,[4,[5]]); let result2 = array.flatMap((value)=>{ return Math.imul(value,2); }); //[ 2, 4, 6, NaN ]
-
-
数组复制与合并
-
数组复制
let array1 = Array.of(1,2,3,4); let array2 = [...array1]; //[1,2,3,4]
-
数组合并
let array1 = Array.of(1,2,3); let array2 = Array.of(4,5); let array = [...array1,...array2]; //[1,2,3,4,5]
-
-
数组缓冲区
数组缓冲区市内存中的一段地址,实际字节数在创建时确定,之后可修改内容,不可修改数组大小
-
创建数组缓冲区
//创建字节长度为10的数组缓冲区 let arrayBuffer = new ArrayBuffer(10); console.log(arrayBuffer.byte.Length); //10
-
集合(Map & Set)
Map
-
Map的键可以是任意值:
字符串
、对象
、函数
、NaN
//key是字符串 let map = new Map(); let strKey = 'Key'; map.set(strKey,"字符串key对应的value"); let value = map.get(strKey); //字符串key对应的value
//key是对象 let map = new Map(); let objKey = {}; map.set(objKey,"对象类型key对应的value"); let value = map.get(objKey); //对象类型key对应的value let value2 = map.get({}); //undefined,因为objKey !== {}
//key是函数 let map = new Map(); let funKey = function () {}; map.set(funKey,"函数类型的key对应的value"); let value = map.get(funKey); //函数类型的key对象的value let value2 = map.get(function(){}) //undefined,因为funKey !== function(){}
//key是NaN let map = new Map(); let nanKey = NaN; map.set(nanKey,"NaN类型的key对应的value"); let value = map.get(nanKey); //NaN类型的key对应的value let value2 = map.get(NaN); //NaN类型的key对应的value //虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),NaN作为Map的键来说是没有区别的。
-
Map迭代遍历
for...of
forEach()
-
for...of
//迭代遍历输出map中的key-value let map = new Map(); map.set("key1","a") map.set("key2","b"); for(let [key,value] of map){ // for(let [key,value] of map.entries) console.log('key->%s;value->%s',key,value); } //key->key1;value->a //key->key2;value->b
//迭代输出key let map = new Map(); map.set("a",0); map.set("b",1); for(let key of map.keys){ console.log('key->%s',key); } //key->a //key->b
//迭代输出value let map = new Map(); map.set("a",0); map.set("b",1); for(let value of map.values()){ console.log('value->%s',value); } //value->0 //value->1
-
forEach()
let map = new Map(); map.set("a",0); map.set("b",1); //使用forEach遍历map中key-value map.forEach((key,value)=>{ console.log('key->%s;value->%s',key,value); }) //key->a;value->0 //key->b;value->1
-
-
Map对象操作:
转换
、克隆
、合并
-
Map 与 Array转换
map构造函数可以将一个
二维键值对数组
转换成一个Map对象//二维键值对数组————转换为Map let array = [["key1","value1"],["key2","value2"],["key3","value3"]]; let map = new Map(array); //使用Array.from函数可以将一个Map转换为一个二维键值对数组 let map = new Map(); map.set("a",0); map.set("b",1); map.set("c",2); map.set("d",3); let array = Array.from(map); //array1=[["a",0],["b",1],["c",2],["d",3]]
-
Map合并
合并两个Map对象时,如果有重复的键值,后面会覆盖前面的
//合并数组 let map1 = new Map([["a",0],["b",1]]); let map2 = new Map([["a",5],["b",0],["c",8]]); //合并两个map let mergeMap = new Map([...map1,...map2]); mergeMap.forEach((value,key)=>{ console.log('key->%s,value->%s',key,value); }) //{a:5,b:0,c:8}
-
Set
Set集合对象允许存储任何类型的唯一值
,无论是原始值还是对象引用
-
存储
let set = new Set(); set.add(1); set.add(2); set.add(2); //相同的值只能存储一个,唯一性! set.add("a"); set.add(false); set.add(()=>{}); set.add({}); for(let value of set){ console.log(value); } //1 //2 //a //false //[Function] //{}
-
Set迭代遍历
let set = new Set(); set.add(1); set.add(2); set.add(2); //相同的值只能存储一个,唯一性! set.add("a"); set.add(false); set.add(()=>{}); set.add({}); set.add(['a','b','c']); set.forEach((value)=>{ console.log("value=%s",value); }); //value=1 //value=2 //value=a //value=false //value=()=>{} //value={} //value=[ 'a', 'b', 'c' ]
-
类型转换
Array 转 Set (使用Set构造函数)
//使用Set构造函数将Array转为Set let array = [1,2,3,4,5,6,7,8]; let set = new Set(array); //{1,2,3,4,5,6,7,8}
Set 转 Array(使用拓展运算符…操作符号)
//使用...操作符将Set转换为Array let array = [...set]; //[1,2,3,4,5,6,7,8] //或者使用Array.from(set);进行转换 let array = Array.from(set);
String 转 Set(使用Set构造函数)
//使用Set构造函数将String转换为Set,注意Set.toString方法不能将Set转为String let str = 'hello'; let set = new Set(str); //{h,e,l,l,o}
-
Set对象作用
-
数组去重
let array = [1,2,3,3,4,5,5,5,6,7]; let set = new Set(array); //{1,2,3,4,5,6,7}
-
合并Set
let set1 = new Set([1,2,3]); let set2 = new Set([4,3,2]); let mergeSet = new Set([...set1,...set2]) //{1,2,3,4}
-
获取交集
let set1 = new Set([1,2,3]); let set2 = new Set([4,3,2]); let set = new Set([...set1].filter((x)=>{ //返回set2里面包含set1的元素 return set2.has(x); })); //{2,3}
-
获取差集
let set1 = new Set([1,2,3]); let set2 = new Set([4,3,2]); let set = new Set([...set1].filter((x)=>{ //返回set2里面不包含set1的元素 return !set2.has(x); })); //{1}
-
7. 函数(箭头函数)
-
基本用法
-
默认参数
//声明函数 function fn(name,age=20){ console.log(name,age); } //调用函数 fn('zcmain',30); //zcmain,30 fn('zcmain',''); //zcmain fn('zcmain'); //zcmain,20 ;使用默认参数(只有在未传递参数,或者参数为undefined才会使用默)
注意:
使用函数默认参数时,不允许有同名参数
//不报错 function fn(name,name){ //TODO... } //错误!!!使用函数默认参数,出现了同名参数name function fn(name,name,age=20){ //TODO... }
-
不定参数
不定参数用来表示不确定参数个数,形如:
...变量名
,由...
加上一个具体名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。基本用法
//定义不定参数的函数 function fn(...params){ console.log(params.length); } //调用不定参数的函数 fn(1,2) //2 fn(1,2,3,4); //4
-
-
箭头函数
箭头函数,提供了一种更加简洁的函数书写方式,基本语法
参数=>函数体
-
基本用法
let fun = v=>v*2; //等价于 let fun = function(v){ return v*2; } fun(1); //2
-
无参或多参用()括起来参数
无参箭头函数
//无参箭头函数,用()括起 let fun = ()=4; //等价于 let fun1 = function(){ return 4; } fun1(); //4
多参箭头函数
//多个参数箭头函数用()括起参数 let fun = (a,b,c)=> a+b+c; //等价于 let fun = function(a,b,c){ return a+b+c; } fun(1,2,3); //6
-
函数体有多行语句用{}括起来
箭头函数函数体有多行语句,用
{}
包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略{}
,结果会自动返回。//函数体有多行,用{}括起函数体 let fun = (a,b)=>{ let result = a+b; return result; } fun(2,5); //10
-
返回对象也要用
{}
将对象包裹起来,为了区分于代码块//报错 let fun = ()=>{id:1,name:'zcmain'}; //不报错 let fun = ()=>{{id:1,name:'zcmain'}};
-
没有
this
、super
、argument
、new.target
绑定let fun = ()=>{ //箭头函数内没有this对象,此时的this是外层的this对象,即window console.log(this); }
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
每个函数都包含两根非继承而来的方法:
function fn(){ setTimeout(()=>{ // 定义时,this 绑定的是 fn 中的 this 对象 console.log(this.a); },0) } var a = 20; /** *apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的 *值。 *fn 的 this 对象为 {a: 19} */ fn.call({a: 18}); // 18
不可以作为构造函数,也就是不能使用 new 命令,否则会报错
-
8. 迭代器(Iterator)
Iterator迭代器是ES6引入的一种新的遍历机制
可迭代的数据结构
- Array
- String
- Map
- Set
- Dom元素(正在进行中…)
1. 数组迭代
数组(Array)和类型数组(TypeArray)他们是可迭代的
let array = Array.of(1,2,3,4,5);
//迭代数组元素
for(let value of array){
console.log(value);
}
//1
//2
//3
//4
//5
//迭代数组下标和元素index-value
for(let [index,value] of array.entries()){
console.log(index,value);
}
//0,1
//1,2
//2,3
//3,4
//4,5
2. 字符串迭代
字符串可迭代,他们遍历的是Unicode码,每个码可能包含一到两个JavaScript字符
let str = 'hello';
for(let value of str){
console.log(value);
}
//h
//e
//l
//l
//o
3. 迭代Map
Map主要迭代他们的entries
,每个entry都会被编码为[key,value]的项,entries是以确定形式进行迭代,顺序与添加顺序相同
注意:WeakMaps不可迭代
let map = new Map();
map.set('a',1);
map.set('b',2);
map.set('c',3);
map.set('d',4);
//遍历key-value
for(let [ky,value] of map){
console.log(key,value);
}
//a,1
//b,2
//c.3
//d,4
//只遍历key
for(let key of map.keys()){
console.lo(key);
}
//a
//b
//c
//d
//只遍历value
for(let value of map.values()){
console.log(value);
}
//1
//2
//3
//4
4. 迭代Set
Set是对其元素进行迭代,迭代顺序与其添加顺序相同
注意:WeakSet不可迭代
let set = new Set();
set.add('Zcmain');
set.add('Tom');
set.add('Judy');
//遍历set 元素
for(let value of set){
console.log(value);
}
//Zcmain
//Tom
//Judy
说明
-
使用for…of进行遍历
-
如果使用
let
和const
,每次迭代将会创建一个新的存储空间,这可以保证作用域在迭代内部let array = Array.of(1,2,3,4,5); for(let value of array){ console.log(value); } console.log(value); //错误,value为let修饰变量,只在for...of循环作用域内有效
-
如果使用
var
会作为全局,迭代每次不会创建一个新的存储空间let array = Array.of(1,2,3,4,5); for(var value of array){ console.log(value); } console.log(value); //正确访问value,value是通过var修饰为全局变量
-
9. ES6 Class类
在ES6中,class类作为对象的模版被引入,可以通过class关键字定义类。class的本质是function
。
注意要点
- 类不可重复声明
- 类必须被调用前进行定义,否则会报错
- 类中方法不需要
function
关键字 - 方法之间不能加分号
1. 类声明
//声明Person类
class Person{
constructor(name){
this.name = name;
}
}
//声明User类
class User{
constructor(age){
this.age= age;
}
}
2. 类的定义
类表达式可以为匿名或命名
//类表达式——>命名类(声明时指定类的名称)
let person = class Person{
//构造函数
constructor(age){
this.age = age
}
}
//类表达式——>匿名类(声明时不指定类名称)
let user = class{
//构造函数
costructor(name){
this.name = name;
}
}
不可重声明/定义类
class Person{}
class Person{} //错误,Person类已存在不可重复声明
let User = class{}
class User{} //错误,Person类已存在不可重复声明
3. 类的主体
-
属性
-
静态属性
静态属性:class类本身的属性,static修饰,不需要实例化。
ES6规定class内部只有静态方法,没有静态属性
class Person{ //构造函数 constructor(age){ this.age = age; } //静态属性(Person类直接调用) static name = 'zcmain'; //静态方法(Person类直接调用) static getName(){ return this.name; } //实例方法(Person实例化后才可调用) getAge(){ return this.age; } } //静态属性 & 静态方法 通过类名直接调用 Person.name; //zcmain Person.getName(); //zcmain //非静态方法,需要实例化对象后才可调用 let person = new Person(20); person.getAge(); //20
name
类名属性(属于静态属性):返回跟在class 后的类名称(如果是非匿名类)class Person{ constructor(){ } } console.log(Person.name); // 通过类名.name返回class的名称(非匿名类) //Person
-
实例属性
实例属性:定义在实例对象(this)上的属性
class Person{ //实例属性 age = 20; //实例方法 getAge(){ return this.age; } } let person = new Person(); person.getAge(); //20
-
-
方法
-
构造方法
constructor
方法是类默认的方法,创建类的实例化对象时被调用//构造函数默认返回当前实例对象 class User{ constructor(){ //默认返回this(User实例对象) } } console.log(new User() instanceof Test); //true //指定构造函数返回实例对象 class Person{ constructor(){ //指定返回User实例对象 return new User(); } } console.log(new Person() instanceof Person ); //false;
-
静态方法
静态方法:class类本身的方法,static 修饰,不依赖于类的实例对象。
class Person{ static getName(){ return 'zcmain'; } } console.log(Person.getName()); //静态方法通过类直接调用,无需实例化类对象
-
实例方法
实例方法:定义在实例对象上的方法,依赖于类的实例化对象
class Person{ getName(){ return 'zcmain'; } } let person = new Person(); console.log(person.getName()); //实例方法依赖于类的实例化对象
-
-
类的实例化
class的实例化必须通过**
new
**关键字class Person{} //通过new关键字实例化Person对象 let person = new Person();
-
封装于继承
-
getter/setter
class Person{ constructor(name,age){ this.name = name; //实例化时调用 set方法 this.age = age; } set name(name){ console.log('setter'); this.name = name; //自身递归调用(this.name = name赋值会触发name的set方法) } get name(){ console.log('getter'); return this.name; } } //不断输出setter,最终导致RangeError,如何优化?见下面解决方案 let person = new Person('zcmain',20);
解决setter方法递归调用
class Person{ constructor(name,age){ this.name = name; //触发set name方法 将 name 赋值给this._name //或者 //this._name = name; //不会触发set name方法,直接将name赋值给this._name this.age = age; } set name(name){ console.log('setter'); this._name = name; //将 name 重复赋值给变量 this._name } get name(){ console.log('getter'); return this._name; } } let person = new Person('zcmain',20); console.log(person.name); //内部调用getName()方法 //或者 console.log(person._name); //直接访问_name变量,不调用getName方法 //zcmain
setter方法必须放在父类中或者同级出现(不可出现在子类中)
- getter放在父类中,setter放在子类中
//将getter放在父类中 class Person{ constructor(name){ this._name = name; } get name(){ return this._name; } } //setter放在子类中 class Man extends Person{ constructor(name){ super(name); } set name(value){ this._name = name; } } let man = new Man('zcmain'); console.log(man.name); //undefined,不可正常访问
- setter放在父类中,getter放在子类中
//setter放在父类中 class Person{ constructor(name){ this._name = name; } set name(value){ this._name = value; } } //getter放在子类中 class Man extends Person{ constructor(name){ super(name); } get name(){ return this._name; } } let man = new Man('zcmain'); console.log(man.name); //zcmain,可正常访问
ps:
setter/getter同级出现(同时放在父类或者自类中都可正常访问)
-
extends
//类继承通过extends实现 class Man extends Person{}
-
super
//子类构造方法constructor中必须有super,且必须出现在this之前 class Man extends Person{ constructor(name){ this._name = name; super(); //错误,构造函数中 super必须放在this之前 } }
调用父类方法,super作为对象:在实例方法中指向父类实例对象;在静态方法中,指向父类
//父类 class Person{ //静态方法 static getAge(){ return 20; } //实例方法 getName(){ return 'zcmain'; } } //子类 class Man extends Person{ constructor(){ super(); console.log(super.getName()); //在实例方法中,super指向父类Person实例 } static printName(){ console.log(super.getAge); //静态方法中,super指向父类本身 } }
-
10. ES6 模块
ES6引入了模块化。设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量
ES6模块化分为导出(export)和导入(import)
特点
- ES6模块自动启动严格模式,不管在模块头使用加
use strict
; - 导入和导出支持:
变量
、对象
、字符串
、数字
、布尔
、类
等; - 每个模块都有自己的上下文,每个模块内声明的变量都是局部变量,不会污染作用域;
- 每个模块只加载一次(单例),若再去加载同目录下文件,直接从缓存中读取;
注意
- Node.js暂不支持
export和import
可用es-checker
命令查看node支持ES6情况; - 目前浏览器支持ES6情况有限,在支持的浏览器中需要指定type=“module”,import的时候必须带上
.js
的后缀名。
基本用法
-
说明
- 导出(export)的函数声明、类声明必须要有名称(export default命令另外考虑)
- 不仅能导出声明,还能导出引用(例如函数)
- export命令可以出现在模块的任何位置,但必须出现在模块顶层(即导出模块必须在{}内)
- import命令会提升到整个模块的头部,首先执行.
-
导出(export)
Test1.js
/** *批量导出 */ let name = 'zcmain'; //字符串变量 let age = 20; //数值变量 let isLogin = false; //布尔变量 export let Obj = {name:'zcmain'}; //对象 let userClass = class User{static name = 'zcmain';}; //类 let fun = function(){console.log('this is fuction')} //函数 export {name,age,isLogin,Obj,userClass,fun} /** *逐个导出 */ export let name = 'zcmain'; //字符串变量 export let age = 20; //数值变量 export let isLogin = false; //布尔变量 export let Obj = {name:'zcmain'}; //对象 export let userClass = class User{static name = 'zcmain';}; //类 export let fun = function(){console.log('this is fuction')} //函数
-
导入(import)
-
使用
import {name,age,isLogin,Obj,userClass,fun} from './test.js' console.log(name); //zcmain console.log(age); //20 console.log(isLogin); //false console.log(Obj.name); //zcmain console.log(userClass.name); //zcmian console.log(fun()); //this is fuction
-
特点
-
只读属性(不能改写加载导入模块脚本内
引用指向
):不可改:
导入接口的引用指向
、变量类型为基本类型值
;可改:
变量类型为对象类型属性值
//导出基本类型age export age = 20; //导出对象类型Obj export let Obj = { age:20, } //导入对象类型Obj模块,和基本类型name import {Obj,name} from 'xxx'; Obj = {}; //error,不可改写导出接口的引用指向 name = 26; //error ,不可改写导出类型为基本类型的值 Obj.age = 26; //Obj = {age:26}
-
多次重复执行同一句import语句,只会执行一次;import同一个模块不同接口变量,但只执行一次
//import重复执行同一句 import { a } "./xxx.js"; import { a } "./xxx.js"; // 相当于 import { a } "./xxx.js"; //import同一个模块不同接口变量 import { a } from "./xxx.js"; import { b } from "./xxx.js"; // 相当于 import { a, b } from "./xxx.js";
-
-
-
使用as重新定义导入/导出接口名,隐藏模块内部变量
- 重命名导出接口
let name = 'zcmain'; //将变量name重新命名为Name导出 export{Name as name} //导入 import {Name} from 'xxx';
- 重命名导入接口
let name = 'zcmain'; export {name} //导将入的变量name重命名为Name import {Name as name} from 'xxx';
-
export default命令
-
同一个文件模块中,只能出现一次export default; 但是export可以有多个;
-
通过export导出,在导入时需要加**{}; 而export default**则不需要;
-
export default向外导出的成员,可以使用任意变量来接收
let a = "My name is Tom!"; export default a; // 仅有一个export default export default var c = "error"; // error,default 已经是对应的导出变量,不能跟着变量声明语句 //导入 import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收
-
-
组合使用 export & export default
//将 person 转换为 default export {person as default} from 'xxx'; //将default转成 person export {default as person} from 'xxx'; //导入Test.js接口中多有导出的模块 export * from 'Test.js';
11. ES6 Promise对象
Promise
是一个对象,从它可以获取异步操作的消息。
1. Promise状态
-
特点
-
Promise异步操作只有三个状态:pending(进行中)、fulfilled(已完成)、rejected(已失败);
-
Promise状改变只能是:padding—>fulfilled和pending—>rejected的状态改变,只要处于fulfilled和rejected,状态就不会再变了,已定型。
const p1 = new Promise(function(resolve,reject){ resolve('success1'); //状态已完成回调,后续resolve状态不会再变更 resolve('success2'); }); p1.then(function(value){ console.log(value); //success1 }); const p2 = new Promise(function(resolve,reject){ resolve('success3'); //状态已完成回调,后续reject状态不再改变 reject('reject'); }); p2.then(function(value){ console.log(value); //success3 });
-
-
缺点
- Promise无法取消,一旦新建立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出错误,不会反应到外部。
- 当处于pending状态时候,无法得知目前进展到哪个阶段(刚刚开始,还是即将完成)
2. then方法
-
参数
为两个函数,
两个函数只有一个会被调用
- 第一个参数为:Promise执行成功fulfilled时回调
- 第二个参数为:Promise执行失败rejected时回调,
rejected必须被处理,否则整个会catch
Promise执行成功,then只会回调fulfilled
const p1 = new Promise((resolve,reject)=>{ resolve('success'); }); p1.then((value)=>{ console.log(value); //success },(reason)=>{ console.log(reason); //不会执行,失败回调 });
Promise执行失败,then只会回调rejected
const p2 = new Promise((resolve,reject)=>{ reject('rejected...'); }); p2.then((value)=>{ console.log(value); //不会执行,成功回调 },(reason)=>{ console.log(reason); //rejected... });
Promise执行失败,then不处理失败回调会导致异常
const p3 = new Promise((resolve,reject)=>{ reject('rejected...'); }); //由于p3的then方法没有对失败rejected处理,导致异常, p3.then((value)=>{ console.log(value); }); //可通过catch方法捕获异常 p3.then((value)=>{ console.log(value); }).catch((reason)=>{ console.log(reason); //使用catch对异常进行捕获 });
-
then方法特点
-
在JavaScript事件(非延迟)队列运行完成之后才会调用回调函。
const p1 = new Promise((resolve,reject)=>{ resolve('success'); }); p1.then((value)=>{ console.log(value); }); console.log('a'); console.log('b'); setTimeout(()=>{ console.log('c'); },1000); //打印结果 //a //b //success //c
-
.then方法返回一个fulfilled 或rejected状态的Promise对象用于链式调用
new Promise((relove,reject)=>{ resolve(10); }).then((value)=>{ console.log(value); //10 //将结果处理后返回到下一个then方法中 return value*2; }).then((value)=>{ console.log(value); //20 //没有返回处理 }).then((value)=>{ console.log(value); //undefined 上一层没有返回,因此此处是undefined //返回失败 return Promise.reject('rejected'); }).then((value)=>{ console.log(value); //不会回调,上一层返回执行失败,走rejcted失败回调 },(reason)=>{ console.log(reason); //rejected });
-
-
注意
大多数浏览器中不能终止的 Promise 链里的 rejection,建议后面都跟上 .catch(error => console.log(error));
12. ES6 async函数
ES6async函数是ES7才有的与异步操作有关的关键字,和Promise
、Generator
有很大的关联
-
语法
async function name(params){ statements }
- name:函数名称
- params:要传递给哈书的参数名称
- statements:函数体语句
-
返回值
async函数返回一个Promise对象,可使用then方法添加回调函数
async function add(a,b){ return a+b; } add(1,2).then((value)=>{ console.log(value); //3 });
-
await表达式
-
await 操作符用于等待一个Promise对象,它只能在异步函数async函数内部使用。
-
正常情况下,await命令后面跟一个Promise对象,也可跟其他值:字符串、布尔值、数值、普通函数
-
await 针对所跟不同表达式的处理方法
- Promise对象:await会暂停执行,等待Promise对象resolve,然后恢复async函数的执行并返回解析值
- 非Promise对象:直接返回对应值
//Promise对象 let promise = new Promise((resolve) => { setTimeout(() => { console.log('promise...'); resolve(); }, 1000); }); //普通函数(内部有延迟) function fun1() { setTimeOut(()=>{ console.log('普通函数..'); },1000); } //字符串 const str = 'zcmain'; //布尔 const bool = false; //声明async函数 async function fun(){ /** *await 命令后面跟Promise对象,暂停执行后续业务,等待Promise返回resolve,然后恢复async函数 *执行后续业务 */ await promise; //await 命令后跟普通函数,不暂停,继续执行后续业务(即使时内部有延迟任务) await fun1(); //await 命令后跟字符串,直接返回 let str = await 'zcmain'; //await 命令后跟布尔值,直接返回 let bool = await false; } fun(); //promise... //zcmain //false //普通函数... //await 后面跟普通内部有延迟任务函数,并没有暂停,先执行了字符串和布尔值
-
=============================
11. 数据类型
ES6数据类型除了:Number
、String
Boolean
Object
null
undefined
还新增了Symbol
-
Symbol
Symbol表示独一无二的值,最大的用法是用来定义对象的唯一属性名!
12. Proxy 与 Reflect
Proxy与Reflect是ES6为操作对象而引入的API
Proxy:通过对象的代理对目标对象的读取、函数调用等操作进行拦截,比如添加一些需要额外的操作。
Reflect:用于获取目标对象的行为,它的方法与Proxy是对应的。
13. ES6 Generator函数
ES6引入Generator函数,可以通过yield关键字,把函数的执行流程挂起,为改变执行流程提供可能,从而为异步编程提供解决方案。
-
Generator函数组成
Generator函数区分于普通函数部分:
- 在function后面,函数名之前有*****标识符
- 函数内部有
yiled
表达式
其中**
*
用来表示函数为Generator函数,yiled
**用来定义函数内部状态//声明Generator函数(函数名前有 * 标识符,内部有yiled关键字) function *fun(){ console.log('one'); yiled '1'; console.log('two'); yiled '2'; console.log('three'); return '3'; }
-
执行机制
调用Generator函数和调用普通函数一样,函数名后面加上**()**即可,但是Generator函数不会像普通函数一样立即执行,而是返回一个指向内部状态的指针,因此要调用遍历对象的Iterator的next方法,指针就会从函数头部或上一次停下来的地方开始执行
fun.next(); //one
扩展
1. JS中this指向
在js中this对象是在运行时基于函数的执行环境绑定
的:
-
函数没有直接的挂载者(或称调用者),函数中this是指向window;
-
函数作为某个对象方法被调用时候,则this指向当前调用对象;
-
匿名函数(setTimeOut、setInterval)的执行环境是全局的则this指向全局window;
1.1 单独函数调用
函数没有直接的挂载者(或调用者),函数中this 指向 window
function fn(){
console.log(this);
function fn2(){
console.log(this);
function fn3(){
console.log(this);
}
fn3(); //fn3() 独立函数调用,不借助对象,this指向全局window
}
fn2(); //fn2() 独立函数调用,不借助对象,this指向全局window
}
fn(); //fn() 独立函数调用,不借助对象,this指向全局window
1.2 对象调用
函数作为某个对象的方法被调用时this指向调用对象,即调用宿主
let Obj = {
age:20,
fn:function(){
console.log(this);
console.log(this.age); //20
function fn1(){
console.log(this);
}
fn1(); //fn1() 独立函数调用,不借助对象,this指向全局window
}
}
//对象调用 fn函数借助Obj对象调用,this指向调用的对象Obj,因此内部this.name可访问Obj中的属性
Obj.fn();
箭头函数this指向
箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。
简单的说:箭头函数在定义时,this就继承了定义函数的对象
。
-
适合使用的场景
ES6 之前,JavaScript 的 this 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中,那么有了箭头函数,就不需要这样做了,直接使用 this 就行。
所以,当我们需要维护一个 this 上下文的时候,就可以使用箭头函数。
案例:如何通过回调函数访问对象的属性?
//全局age let age =20; let Person = { age:18, getAge:function(){ setTimeOut(function(){ console.log(this.age); //匿名函数内部this指向的是全局window }); } } Person.getAge(); // 20 this.age指向的是全局的age,并非Person对象中的age
- 使用
let that = this
实现回调函数访问对象中属性
//全局age let age = 20; let Person = { age:18, getAge:function(){ let that = this; //在getAge函数内部将当前this赋值给that变量,该this是指向Person对象的 setTimeOut(function(){ console.log(that.age); //回调匿名函数中通过使用that来访问Person属性 },0) } } Perosn.getAge(); //18
- 使用
箭头函数
实现回调函数访问对象中属性
//全局age let age = 20; let Person = { age:18, getAge:function(){ setTimeOut(()=>{ console.log(this.age); //箭头函数中this指向定义的函数的对象Person },0) } } Perosn.getAge(); //18
- 使用
-
不适合使用场景
箭头函数不适用于定义函数的方法,且该方法中包含
this
let Obj1 = { name: 'obj1', fun1: () => { //使用箭头函数定义函数方法,此时 this 指向外层对象即全局对象window。 console.log('fun1', this); }, fun2: function () { //使用传统function定义函数方法,此时this指向Obj1对象。 console.log('fun2', this); let Obj2 = { name: 'obj2', fun3: function () { //使用传统function定义函数方法,此时this指向Obj2 console.log('fun3', this); }, fun4: () => { //使用箭头函数定义函数方法,此时this指向外层对象,即fun2函数中this对象Obj1。 console.log('fun4', this); }, }; Obj2.fun3(); Obj2.fun4(); }, }; Obj1.fun1(); Obj1.fun2();
2. 解释执行和编译执行语言的优缺点
计算机只能执行低级语言中的指令(机器语言)(汇编语言的指令需要先转换成机器码才能执行),对于我们的使用高级语言开发出来的程序,想要计算机执行,必须要将高级语言翻译成低级语言!
高级语言翻译为低级语言的方法有两种:
- 解释:在程序运行时候将高级语言翻译成机器语言。
- 编译:在程序执行之前有个单独的编译过程,将程序翻译成机器语言,执行时候就不需要再进行翻译了。
从可执行效率来说**解释执行
和编译执行
**各自优缺点:
-
解释执行
- 优点
1.不依赖平台
2.开发速度快 - 缺点
1.执行速度低(运行时需要一句一句翻译,速度慢)
2. 浪费内存和CPU(执行过程中需要一句一句的翻译消耗内存)
3. 源码必须交给用户
Java
C#
JS
等都是解释型语言,虽然java程序要有个编译过程,但并不是将程序编译成机器语言,而是键它编译成字节码(可理解为一个中间语言),在运行时候,由JVM将字节码再翻译成机器语言。 - 优点
-
编译执行
- 优点
1.执行速度快(编译后的机器语言直接执行,用C/C++编写的程序运行速度要比java编写的相同程序快30%~70%)
2.消耗内存小 - 缺点
1.兼容性差(编译是面向特定平台,因此是平台依赖的)
2.安全性低(一个编译型程序可以访问内存任何区域,大部分病毒使用编译型语言开发)
3.开发难度高
C
C++
都是编译型语言 - 优点