ES6学习之二
本文的学习来自技术胖大神的教程:https://jspang.com/
1扩展运算符和rest运算符
扩展运算符和rest运算符,它们都是…(三个点)。
它们有很多相似之处,甚至很多时候不用特意去区分。
它们可以很好的解决参数和对象数组未知情况下的编程,让代码更健壮和简洁。
1.1对象扩展运算符(…)
当编写一个方法时,允许它传入的参数是不确定的。这时候可以使用对象扩展运算符来作参数。
例:
function test(...arg){ console.log(arg[0]); console.log(arg[1]); console.log(arg[2]); console.log(arg[3]); } test(1,2,3);
说明可以传入多个值,并且就算方法中引用多了也不会报错。
1.2扩展运算符的用处
例1:声明两个数组arr1和arr2,然后把arr1赋值给arr2,然后改变arr2的值:
let arr1=['a','b','c']; let arr2=arr1; console.log(arr2); arr2.push('d'); console.log(arr1);
可以看到arr1的值也改变了,因为这是对内存堆栈的引用,而不是真正的赋值。
利用对象扩展运算符简单的解决这个问题:
let arr1=['a','b','c']; let arr2=[...arr1]; console.log("arr2:",arr2); arr2.push('d'); console.log("arr1:",arr1); console.log("arr2:",arr2);
可以看到arr1并没有改变。
1.3 rest运算符
例:
function test(first,...arg){ console.log(arg.length); } test(1,2,3,4,5,6);
第一个参数1传给了first,剩下的参数传给了arg数组,所以arg长度为5
1.4循环输出rest运算符
可以用for…of循环,for…of的循环可以避免开拓内存空间,增加代码运行效率:
function test(first,...arg){ for(let val of arg){ console.log(val); } } test(1,2,3,4,5,6);
2 ES6新增的数字操作
2.1二进制声明(不是ES6特性)
二进制的英文单词是Binary,二进制的开始是0(零),然后第二个位置是b(大小写都可以实现),然后跟上二进制的值就可以了。
2.2八进制声明(不是ES6特性)
八进制的英文单词是Octal,也是以0(零)开始的,然后第二个位置是O,然后跟上八进制的值就可以了。
例:
//二进制声明 let binary=0B010101; console.log('二进制binary:',binary); //八进制声明 let octal=0O777; console.log('八进制octal:',octal);
2.3数字验证
可以用Number.isFinite( )进行数字验证,只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false
console.log(Number.isFinite(888)); console.log(Number.isFinite(11/4)); console.log(Number.isFinite('abc')); console.log(Number.isFinite(NaN)); console.log(Number.isFinite(undefined));
2.4 NaN验证
NaN是特殊的非数字,可以使用Number.isNaN()来进行验证
console.log(Number.isNaN(0));
console.log(Number.isNaN(NaN));
2.5判断是否为整数
Number.isInteger() console.log(Number.isInteger(100)); console.log(Number.isInteger(99.99));
2.6转换
整数转换Number.parseInt()
浮点型转换Number.parseFloat()
let a='9.99'; let b='aaa'; console.log(Number.parseInt(a)); console.log(Number.parseFloat(a)); console.log(Number.parseInt(b)); console.log(Number.parseFloat(b));
2.7整数取值范围
整数的操作是有一个取值范围的,就是-(253-1)到253-1。
在计算时会经常超出这个值,所以要进行判断,ES6提供了两个常数,就不用去计算了,直接用就可以:
//最大安全整数 console.log(Number.MAX_SAFE_INTEGER); //最小安全整数 console.log(Number.MIN_SAFE_INTEGER);
安全整数判断:
let c=Math.pow(2,53); let d=Math.pow(2,53)-1; console.log(Number.isSafeInteger(c)); console.log(Number.isSafeInteger(d));
3新增的数组知识
3.1 JSON数组格式转换:Array.from()方法
JSON的数组格式就是为了前端快速的把JSON转换成数组的一种格式
例:
let json={ 0:1, 1:2, 2:'abc', length:3 }
这就是一个标准的JSON数组格式(注意key值就是0,1,2...,不能随意起别的键名),跟普通的JSON对比是在最后多了一个length属性,没有length就只是字符串,有length才是数组。
这种特殊的json格式可以使用ES6的语法转变成数组:
let json={ 0:1, 1:2, 2:'abc', length:3 } let arr=Array.from(json); console.log(arr);
3.2 Array.of()方法
可以把一堆文本或者变量转换成数组。(之前可以用eval来进行转换,但是eval的效率很低,会拖慢程序,所以尽量不要用)
例:
let arr=Array.of(1,2,3,4); console.log(arr); let arr2=Array.of('a','b','c'); console.log(arr2);
3.3 find()方法
find()是一个实例方法,实例方法就是必须有一个已经存在的数组来调用,而前面两个方法是用Array对象直接调用的。
(java中曾学过静态方法和实例方法,所以这里很好理解)
find方法是从数组中查找,需要传入一个匿名函数,函数需要传入三个参数:
value:表示当前查找的值。
index:表示当前查找的数组索引。
arr:表示当前数组。
在函数中如果找到符合条件的数组元素就进行return,并停止查找。如果没有满足条件的,就返回undefinded。
例1:
let arr=[1,2,3,4,5]; console.log(arr.find(function(val,index,arr){ return val>3; }));
例2:
let arr1=['a','b','c']; console.log(arr1.find(function(val,index,arr){ return val=='d'; }));
3.4 fill()方法
fill()也是一个实例方法,它的作用是把数组进行填充,它接收三个参数,第一个参数是填充的变量,第二个是开始填充的位置,第三个是填充到的位置。
例:
let arr=[1,2,3,4,5]; arr.fill('abc',2,4); console.log(arr);
3.5数组的遍历
1) for…of循环
这种形式比ES5的for循环要简单而且高效,比普通for少声明一个i,这个i用完就没意义了。
(for of就类似java中的增强for)
例:
let arr=[1,2,3,4,5]; for(let item of arr){ console.log(item); }
输出数组索引:
let arr=[1,2,3,4,5]; for(let index of arr.keys()){ console.log(index); }
同时输出数组的内容和索引:
let arr2=['你好','abc','12345']; for(let [index,item] of arr2.entries()){ console.log(index+':'+item); }
2)forEach
forEach循环会自动省略为空的数组元素,相当于直接筛空了。
let arr=['abc','123','Hello'];
arr.forEach((val,index)=>console.log(index,val));
(这里用了箭头函数,先用一下,后面会再详细写)
3)filter
let arr=['abc','123','Hello'];
arr.filter(x=>console.log(x));
4)some
let arr=['abc','123','Hello'];
arr.some(x=>console.log(x));
5)map
map起到一个替换的作用:
let arr=['abc','123','Hello'];
console.log(arr.map(x=>'替换后的值'));
3.6数组转换字符串
1)join()方法
join()方法就是在数组元素中间,加了一些间隔,开发中很有用处。
let arr=['abc','123','Hello','你好'];
console.log(arr.join('-'));
2)toString()方法
转换时只是用逗号隔开了:
let arr=['abc','123','Hello','你好'];
console.log(arr.toString());
3.7 entries()方法
entries()生成的是Iterator形式的数组,这种形式的好处是可以在需要时用next()手动跳转到下一个值。Entries的意思是条目。
例:
let arr=['abc','你好','学习es6']; let list=arr.entries(); console.log(list); console.log(list.next().value);
当想生成一些不规则的循环时,就可以用entries,这就叫手动循环。(上面的for of循环叫自动循环)
例:
let arr=['abc','你好','学习es6']; let list=arr.entries(); console.log(list.next().value); console.log('---------------'); console.log(list.next().value); console.log('***************'); console.log(list.next().value); console.log('@@@@@@@@@@@@@@@'); console.log(list.next().value);
3.8 in的用法
in是用来判断对象或者数组中是否存在某个值的。
例:对象判断
let obj={ a:'你好', b:'你是谁' } console.log('a' in obj); console.log('你好' in obj);
说明判断的是key值
例:数组判断:
以前会使用length属性进行判断,其存在弊端:
let arr=[,,,];
console.log(arr.length);
数组中其实全是空值,却输出了长度为3,这是不准确的。
用ES6的in就可以解决这个问题:
let arr=[,,,]; console.log(0 in arr); let arr2=[1,2,3,]; console.log(0 in arr2);
注意:这里的0指的是数组下标位置是否为空。
4新增的函数知识
4.1参数的默认值
function fun1(a,b=1){ return a+b; } console.log(fun1(2));
4.2主动抛出错误
function fun2(a){ if(a==1){ throw new Error("这里报错了!"); } return a+b; } console.log(fun2(1));
4.3严谨模式
之前严谨模式必须写在代码最上边,相当于全局使用,在ES6中可以写在函数体中,相当于针对函数来使用。
例:
function fun3(a,b=2){ 'use strict' return a+b; } console.log(fun3(1));
说明使用了默认值,再使用严谨模式的话,就会有冲突。
把默认值去掉:
function fun3(a,b){ 'use strict' return a+b; } console.log(fun3(1,2));
4.4获得需要传递的参数个数
function fun4(a,b){ return a+b; } console.log(fun4.length);
如果加上默认值:
function fun4(a,b=1){ return a+b; } console.log(fun4.length);
这说明length表示必须传入的参数个数。
4.5对象的函数解构
实际作用:后端经常返回来JSON格式的数据,直接把这个JSON格式数据当作参数,传递到函数内部进行处理。
例:
let json={ a:'abc', b:'123' } function fun({a,b}){ console.log(a,b); } fun(json);
这样方便了很多,不用一个个传递参数了。
4.6数组的函数解构
let arr=['abc','123','哈哈']; function test(a,b,c){ console.log(a,b,c); } test(...arr);
4.7最后的参数可以带逗号
之前是不能有,有了报错,现在没关系了
function show(a,b,c,){ console.log(a,b,c); } show(1,2,3,);
这个在实际开发中会带来方便。
4.8箭头函数
箭头函数使用频率非常高,所以单独总结了:
https://www.cnblogs.com/hzhjxx/p/12046043.html
5 ES6中对象
5.1对象赋值
原来怎么赋值的:
let name='小笼包'; let age=18; let obj={ name:name, age:age } console.log(obj);
ES6允许把声明的变量直接赋值给对象。不用写key值了,直接把变量放进去,这样就大大简化了:
let name='小笼包'; let age=18; let obj={name,age} console.log(obj);
5.2对象Key值构建
有时候前台不知道key值的名称,是要从后台取的,这时可以用[ ] 的形式,进行对象的构建:
let key='name'; let obj={ [key]:'前台定义的值' } console.log(obj);
5.3 is()方法
把两个对象进行比较。
之前可以用===来比较:
let obj1={name:'名字'}; let obj2={name:'名字'}; console.log(obj1.name===obj2.name);
现在可以用is方法:
let obj1={name:'名字'}; let obj2={name:'名字'}; console.log(Object.is(obj1.name,obj2.name));
这两种比较的区别:
===为同值相等,is()为严格相等
console.log(+0===-0);
console.log(Object.is(+0,-0));
console.log(NaN===NaN);
console.log(Object.is(NaN,NaN));
5.4 assign()合并对象
let a={ name:'名字' } let b={ age:20 } let c={ like:'web' } let d=Object.assign(a,b,c); console.log(d);
如果有重复的key值,后面的会覆盖前面的:
let a={ name:'名字' } let b={ age:20, name:'名字2' } let c={ like:'web', age:22, } let d=Object.assign(a,b,c); console.log(d);
6 Symbol在对象中的应用
6.1用Symbol构建对象的Key
let sy=Symbol(); let obj={ [sy]:'sy的值' } console.log(obj[sy]); obj[sy]='新值'; console.log(obj[sy]);
6.2 Symbol对象元素的保护作用
在对象中有很多值,但是循环输出时,并不希望全部输出,就可以使用Symbol进行保护
let obj={ name:'名字', like:'吃' } let age=Symbol(); obj[age]=18; console.log(obj); for(let item in obj){ console.log(obj[item]); }
可以看到,age被保护了。
7 Set和WeakSet数据结构
Set的定义和一些常用方法可以看这里:https://www.cnblogs.com/hzhjxx/p/11460420.html
7.1set的遍历
1)用for...of
let setArr=new Set(['abc','Hello','123']); for(let item of setArr){ console.log(item); }
2)forEach:
let setArr=new Set(['abc','Hello','123']); setArr.forEach((val)=>console.log(val));
7.2 WeakSet
注意不能直接在new 的时候放入值,会报错,要先把对象定义好,再用add方法添进去。
let weakObj=new WeakSet(); let obj={ name:'大美女', age:'18' } weakObj.add(obj); console.log(weakObj);
WeakSet里边的值不允许有重复的对象:
let weakObj=new WeakSet(); let obj={ name:'大美女', age:'18' } let obj2={ name:'大美女', age:'18' } weakObj.add(obj); weakObj.add(obj2); console.log(weakObj);
因为obj和obj2不是同一个对象。
如果是同一个:
let weakObj=new WeakSet(); let obj={ name:'大美女', age:'18' } let obj2=obj; weakObj.add(obj); weakObj.add(obj2); console.log(weakObj);
8 Map数据结构
先看一个json对象的取值:
let json={ name:'大美女', age:18 } console.log(json.name);
这种方法其实是需要遍历整个对象进行寻找的,所以效率要比数组低。
8.1声明Map
let map=new Map();
用set方法添加值:
let json={ name:'大美女', age:18 } let map=new Map(); map.set(json,'me'); console.log(map);
map的灵活性就在此,key值和value值可以没规律,可以是任意类型。
8.2方法和属性
let json={ name:'大美女', age:18 } let map=new Map(); //添加值 map.set(json,'me'); map.set('me2',json); //长度 console.log(map.size); //取值 console.log(map.get(json)); console.log(map.get('me2')); //查找 console.log(map.has(json)); //删除 map.delete(json); console.log(map); //清空 map.clear(); console.log(map);
Map应用广泛,因为他更灵活,更高效。
9用Proxy进行预处理
当操作一个对象或者方法时会有几种动作,例如在运行函数前初始化一些数据,在改变对象值后做一些善后处理。这些都算钩子函数。
Proxy的存在就可以给函数加上这样的钩子函数,可以理解为在执行方法前预处理一些代码。也可以简单的理解为是函数或者对象的生命周期。
Proxy的应用可以使函数更加强大,业务逻辑更加清楚,而且在编写自己的框架或者通用组件时非常好用。
9.1声明Proxy
new Proxy({},{});
第一个花括号相当于方法的主体,后边的花括号就是Proxy代理处理区域,相当于写钩子函数的地方。
9.2 get属性
get属性是在得到某对象属性值时预处理的方法,它接收三个参数:
target:得到的目标值
key:目标的key值,相当于对象的属性
property:这个不太常用
9.3 set属性
set属性是要改变Proxy属性值时,进行的预先处理。它接收四个参数:
target:目标值。
key:目标的Key值。
value:要改变的值。
receiver:改变前的原始值。
例:
let pro=new Proxy({ add:function(val){ return val+10; }, name:'这是我的名字' },{ get:function(target,key,property){ console.log('运行get'); return target[key]; }, set:function(target,key,value,receiver){ console.log('运行set'); return target[key]=value; } }); console.log(pro.name); pro.name='新名字'; console.log(pro.name);
9.4 apply
apply是对方法的预处理
let target = function () { return 'Hello'; }; var handler = { apply(target, ctx, args) { console.log('apply运行了'); return Reflect.apply(...arguments); } } var pro = new Proxy(target, handler); console.log(pro());
10 promise对象的使用
promise很好的解决了回调地狱的问题。
ES5的时候,在多层嵌套回调时,代码层次过多,很难进行维护和二次开发。
promise可以完美解决这个问题。
promise这个单词是“承诺”,当它成功时执行一些代码,当它失败时执行一些代码。它更符合人类的行为思考习惯。
promise执行多步操作非常好用。模仿一个多步操作的过程:
let state=1; function step1(resolve,reject){ console.log('第一步:做饭'); if(state==1){ resolve('做饭完成'); }else{ reject('做饭失败'); } } function step2(resolve,reject){ console.log('第二步:吃饭'); if(state==1){ resolve('吃饭完成'); }else{ reject('吃饭失败'); } } function step3(resolve,reject){ console.log('第三步:洗碗'); if(state==1){ resolve('洗碗完成'); }else{ reject('洗碗失败'); } } new Promise(step1). then(function(val){ console.log(val); return new Promise(step2); }). then(function(val){ console.log(val); return new Promise(step3); }). then(function(val){ console.log(val); return val });
如果某一步出错:
promise可以让代码更加的结构化。在现在的开发中使用率算是最高的。
11 class类的使用
11.1类的声明
class Coder{ name(val){ console.log(val); } } let me=new Coder; me.name('你的名字');
当有多个方法时:
class Coder{ name(val){ return val; } skill(val){ console.log(this.name('张三')+'的技能是'+val); } } let me=new Coder; me.skill('web开发');
注意方法和方法中间不要写逗号,this指类本身。
11.2类的传参
用constructor( )进行传参。传递参数后可以直接使用this.xxx进行调用。
class Coder{ constructor(a,b){ this.a=a; this.b=b; } add(){ return this.a+this.b; } } let me=new Coder(1,2); console.log(me.add());
11.3 class的继承
class Coder{ name(val){ console.log(val); } } class Htmler extends Coder{ } let me=new Htmler; me.name('我是一个切图人员');
注意在写类的时候和ES5中的对象和构造函数要区分开,不要学混了。
12模块化操作
模块化操作主要包括两个方面:
export:模块的输出。
import:模块的引入。
谷歌浏览器地址栏中输入:
chrome://flags/,然后搜索javascript,把Experimental JavaScript设为启用:
引入js文件时,要加上type="module"
这样就可以直接预览效果了。
12.1 export的用法
export可以把变量,函数,对象进行模块化,提供外部调用接口,让外部进行引用。
12.1.1把一个变量模块化
temp.js:
export var a='abc';
index.js:
import {a} from './temp.js';
console.log(a);
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script type="module" src="index.js"></script> </body> </html>
12.1.2多变量的输出
temp.js:
var a='aaa'; var b='bbb'; var c='ccc'; export {a,b,c}
index.js:
import {a,b,c} from './temp.js';
console.log(a,b,c);
12.1.3函数的模块化输出
temp.js:
export function test(a,b){ return a+b; }
index.js:
import {test} from './temp.js';
console.log(test(10,20));
12.1.4 as的用法
有时候不想暴露模块里的变量名称,而给模块起一个更语义化的名称,可以使用as来操作。
temp.js:
var a='张三'; var b=18; export { a as name, b as age }
index.js:
import {name,age} from './temp.js';
console.log(name,age);
12.2 export和export default的区别
加上default相当于是一个默认的入口。在一个文件里export default只能有一个。
例1:写两个export:
temp.js:
export var a ='aaa'; export function add(a,b){ return a+b; }
index.js:
import {a,add} from './temp.js';
console.log(a,add(10,20));
例2:export default
export default var a=12345'; import str from './temp';
这时import可以不写{},而且可以自定义命名。
(这里在chrome预览时报错,以后还是找个方式打包一下,等学完webpack再修改这里。)
总结
开发时尽量使用ES6语法,多用才熟练。如果生产环境不兼容,可以写完再用babel等工具转换。