- let
- const
- 解构赋值
- 字符串
- 数组
- 函数
- 对象
- Symbol
- Set
- WeakSet
- Map
- WeakMap
- Proxy
- reflect
- Proxy与Reflex结合实例
- class
- promise
- iterator
- Gernerator
- Decorators
- 模块
- 学习资料
let
/* let 声明变量 *//* es6相对于es5的全局和局部作用域,多了一个块作用域,块作用域里声明的变量声明周期只在本块内 */let a = 1;console.info(a); // 输出1for (let i=1;i<10;i++) {console.info(i); // 在块内是可以正常使用的}console.info(i); // 异常,ReferenceError: i is not defined// let是不能重复定义的
const
/* const 声明常量*//* const也是块作用域的 */const PI = 3.14159265const constObj = {key1: 'value1',key2: 'value2'}console.info(PI); // 输出3.14159265console.info(constObj) // 输出Object { key1: "value1", key2: "value2" }PI = 1 // 错误的,只读,不能修改constObj = {key3: 'value3'} // 错误的,只读,不能修改constObj.key1 = 1 // 可以修改,并不是因为常量可以修改,而是因为对象是引用类型的,这里只是修改对象的值,并没有修改对象的引用console.info(constObj) // 输出Object { key1: 1, key2: "value2" }
解构赋值
/* 解构赋值 *//* 左边一个结构,右边一个结构,左边与右边解开结构一一赋值 *//* 变量交换、方法返回数组中存有多个变量、 只提取数组中的某些值、重组数组等场景*/// 数组解构赋值let a,b,c;[a,b,c] = [1,2,3];console.info(a); //输出1console.info(b); //输出2console.info(c); //输出3[a,b,c =4] = [1,2];console.info(a); //输出1console.info(b); //输出2console.info(c); //输出4,如果没有默认值,匹配不上,就是undefinedlet d,e,f;[d,e,...f] = [1,2,3,4,5,6,7,8,9,10];console.info(d); //输出1console.info(e); //输出2console.info(f); //Array [ 3, 4, 5, 6, 7, 8, 9, 10 ]// 对象解构let obj1,obj2;({obj1,obj2} = {obj1: 'val1', obj2: 'val2'});console.log(obj1); //输出val1console.log(obj2); //输出val2
字符串
- 字符串unicode扩展
/* 字符串unicode扩展 */// \u表示后面是要输出一个unicoide编码对应的字符let unicoideStr1 = '\u0061'let unicoideStr2 = '\u20334'//第一个正常输出是因为在二字节范围内的编码(0x0000-0xffff),第二个已经超过了两个字节的unicode编码表,会把前四位当成一个字符,最后一位当成一个字符console.info(unicoideStr1,unicoideStr2) // a ″4//显示两个字节以上的unicode编码,需要把编码用{}包起来let unicoideStr3 = '\u{20334}'console.info(unicoideStr1,unicoideStr3) // a ?let spacStr = '?'// es5中把超过两个字节长度编码的字符处理为4个字节,就是两个字符,输出2console.info(spacStr.length) //输出2let spacStrTest = '?a'console.info(spacStrTest.length); //输出3console.info("第一个字符",spacStr.charAt(0)) //输出 第一个字符 乱码console.info("第二个字符",spacStr.charAt(1)) //输出 第一个字符 乱码console.info("第二个字符编码",spacStr.charCodeAt(0)) //第一个字符编码 55360console.info("第二个字符编码",spacStr.charCodeAt(1)) //第二个字符编码 57140// 显然,这不是想要的结果,因此es5中处理这种字符是不正确的console.info(spacStrTest.codePointAt(0)) //输出 131892console.info(spacStrTest.codePointAt(0).toString(16)) //输出 20334console.info(spacStrTest.codePointAt(1)) //输出 57140console.info(spacStrTest.codePointAt(2)) //输出 97//es5中根据编码取字符,大于两个字节的不能正常显示console.info(String.fromCharCode('0x20334')) //输出乱码//es6中根据编码取字符
// console.info(String.fromCodePoint('0x20334')) //输出 ?//es5的循环不能正确取值for(var i=0;i<spacStrTest.length;i++){console.info(spacStrTest[i]); //输出两个乱码和一个a}//es6的循环可以正确取值
// for(let char of spacStrTest){
// console.info(char); //输出 ? a
// }
- 字符串其他扩展
/* 字符串其他扩展 */let otherStrTest = 'string'//是否包含console.info(otherStrTest.includes('in')) //输出true//是否起始console.info(otherStrTest.startsWith('str')) //输出true//是否结尾console.info(otherStrTest.endsWith('ing')) //输出true//复制10次console.info(otherStrTest.repeat(10)) //输出stringstringstringstringstringstringstringstringstringstring (一共10次)//字符串模板let name = 'thatway'let age = '18'let man = `name is ${name},age is ${age}`console.info(man) //输出name is thatway,age is 18//padStart、padEnd 接收两个参数,第一个参数是长度,不够就补白,第二个是补白用的字符let simpleStr = '1'let leftPaddingStr = simpleStr.padStart(2,'0')let rightpaddingStr = simpleStr.padEnd(2,'0')console.info(leftPaddingStr, rightpaddingStr) //输出01 10// raw会ie所有字符转义,就是在特殊字符钱加一个\,使其正常输出console.info(String.raw('hello\nworld')) // 输出hello\nworldconsole.info('hello\nworld')// 输出hello//world
数组
/* 数组 */
// let arrTest1 = Array.of(1,2,3,4,5)
// console.info(arrTest1) //输出[1,2,3,4,5]// let arr = [1,2,3,4,5]
// let arrTest2 = Array.from(arr)
// console.info(arrTest2) //输出[1,2,3,4,5]
// let arrTest3 = Array.from(arr,function(item){return item*2})
// console.info(arrTest3) //输出[2,3,6,8,10]//fill,把数组中元素全部替换掉let arrFill1 = [1,2,3]console.info(arrFill1.fill(0)) //输出[0,0,0]//fill,把数组中元素从第1个开始替换到第3个let arrFill2 = [1,2,3,4,5]console.info(arrFill2.fill(0,1,3)) //输出[ 1, 0, 0, 4, 5 ]//遍历
// for(let [index,value] of ['1','2','3'].entries()){
// console.info(index,value)
// }//find,查找元素,注意,只返回第一个符合的let arrTest3 = [1,2,3,4,5]console.info(arrTest3.find(function(item){return item > 3})) //输出4console.info(arrTest3.findIndex(function(item){return item > 3})) //输出3//是否包含某个值let arrTest4 = [1,2,3,4,5,NaN]console.info(arrTest4.includes(3)) // 输出trueconsole.info(arrTest4.includes(NaN)) // 输出true
函数
/* 函数 *///参数默认值function test1(x,y='123'){console.info(x,y);}test1('abc') // 输出abc 123test1('abc','def') // 输出abc def//作用域let x = 'abc'function test2(x,y=x){console.info(x,y)}test2('123') //输出123 123test2() //输出 undefined undefinedfunction test3(z,y=x){console.info(z,y)}test3('123') //输出123 abctest3() //输出 123 undefined//rest参数,参数不确定个数,如果生命了rest参数,不能再声明其他参数了function test4(...params){console.info(params)}test4(1,2,3,4,5) //Array [ 1, 2, 3, 4, 5 ]test4(1) //Array [ 1 ]test4() //[]// ...数组 可以把数组里的值拆散成值console.info(...[1,2,3,4,5]) //1 2 3 4 5console.info('a',...[1,2,3,4,5])//a 1 2 3 4 5//箭头函数//es5的函数声明function arrow5(){console.info('arrow')}//es6的函数声明//方法体只有一行的时候可以不写{}let arrow6 = ()=>console.info('arrow')arrow5() //arrowarrow6() //arrowlet arrowTest = (x,y)=>{console.info(x)console.info(y)}arrowTest(1,2) //1 2//--//伪调用,优化嵌套、依赖函数,可提高性能let fun1 = (x)=>{console.info('fun1')}let fun2 = (x)=>{return fun1(x)}console.info(fun2('abc'))
对象
/* 对象 */let name= "thatway"let age= 18//es5的对象写法let man5 = {name:name,age:age}//es6中可以将属性名与变量名相同的简写成一个let man6 = {name,age}//结果是一样的console.info(man5,man6);//es5对象中的方法写法let man5_method = {play: function(){console.info("玩")}}//es6对象中的方法写法let man6_method = {play(){console.info("玩")}}//--// let param = 'sex'
// //属性表达式
// let man5_obj = {
// name: 'thatway',
// age: 18
// }
//
// //es6属性名可以用变量
// let man6_obj = {
// name: 'thatway',
// age: 18,
// [param]: 'boy'
// }
// console.info(man5_obj,man6_obj)// //比较两个对象是否是同一个,等同于===,注意引用类型的比较
// console.info(Object.is('abc','abc'))
Symbol
声明不想等的两个值,保证唯一性,babel不支持此API的编译,略过了,知道有这么回事先
Set
/* set */let list1 = new Set()list1.add("1");list1.add("2");console.info(list.size) //2let arr = [1,2,3,4,5]let list2 = new Set(arr)console.info(list2.size) //5let list3 = new Set()list3.add(1)list3.add(2)list3.add(1)//set中的值必须唯一,因此忽略第二个2,可以利用这个特性来去重//去重的时候不会做类型的转换,比如1和'1'是可以同时存在的console.info(list3.size) //2//set有add、has、delete、clear几个方法list3.has(1) //是否含有某个元素list3.delete(1) //删除1 返回true代表成功list3.clear() //清空//遍历for(let key of list3.keys()){console.info(key)}for(let value of list3.values()){console.info(value)}for(let [key,value] of list3.entries()){console.info(key,value)}list3.forEach(function(item){console.info(item)})//在webpack里为什么编译不通过呢,总觉得应该是配置的问题,有空查一下//补充,这个问题是因为babel只转换es6的句法,不转换新的API...比如比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,解决这个问题的办法是引入babel-polyfill,它提供了es6语法的兼容。$ npm install --save babel-polyfill
WeakSet
//WeakSet 和Set的区别是只能放对象,WeakSet是弱引用类型,可以防止内存泄露,其中的对象不存在引用时会被垃圾回收释放内存//WeakSet 没有size属性 只有三个方法add、delete、haslet list_w = new WeakSet();let obj = {title : '111'}list_w.add(obj)//异常,TypeError: [object WeakSet] is not iterable!//WeakSet不支持遍历
// for(let item of list_w){
// console.info(item)
// }//异常,TypeError: WeakSet value must be an object, got the number 1list_w.add(1)
Map
//map//map里放的是键值对,键可以是任何类型let mapTest1 = new Map()mapTest1.set("key","value")console.info(mapTest1.get("key")) // 输出value//用数组作为keylet arrKey = [1,2,3,4,5];mapTest1.set(arrKey,"value2")console.info(mapTest1.get(arrKey)) // 输出value2// 用数组构造函数let mapTest2 = new Map([['key1','abc'],['key2','def']]);console.info(mapTest2.get('key1')) // abc//map长度console.info(mapTest2.size) //2//其他方法mapTest2.delete("key")mapTest2.clear()//遍历与set一样
WeakMap
WeakMap和WeakSet的特性一样,键必须是对象。
Proxy
/* proxy */let objTest1 = {name: 'thatway',age: '18'} //代理objTest1,让用户去操作proxyTest,实际数据是存在于objTest1 //第一个参数是要代理的对象 //第二个参数是要拦截的各种配置let proxyTest = new Proxy(objTest1,{ //拦截对象的读取get(target,key){return target[key]+"哦"}, //拦截赋值操作set(target,key,value){return target['key']= value+"哦"}, //拦截in操作,是否存在has(target,key){if(key == 'name'){return false}}, //拦截deletedeleteProperty(target,key){if(key == 'name'){delete target[key]return true}else{return target[key]}},})console.info(proxyTest.name) //thatway哦proxyTest.name = "wp"console.info(proxyTest) //name值被设置为wp哦console.info(name in proxyTest); //falseconsole.info(delete proxyTest.name) //trueconsole.info(delete proxyTest.age) //trueconsole.info(proxyTest) //对象中没有了name,age还存在
proxy的操作
- get
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。
- set
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。
- has
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
- deleteProperty
deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
- ownKeys
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
- getOwnPropertyDescriptor
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
- defineProperty
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
- preventExtensions
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
- getPrototypeOf
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
- isExtensible
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
- setPrototypeOf
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
- apply
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
- construct
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
reflect
refect方法与proxy一一对应。
/* reflect *///字母是反射的意思//Reflect不用newconsole.info(Reflect.get(objTest,'name')) //thatwayconsole.info(Reflect.set(objTest,'key','value'))console.info(objTest)
reflect的设计目的,阮大神的文档中是这么说的:
1、 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
2、修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
3、让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
4、Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
Proxy与Reflex结合实例
//声明一个方法,返回值是一个代理对象,参数是实际对象和验证规则对象//其目的是为了给原对象做一些列数据格式验证,用户在使用对象时拿到的实际上是代理对象,而不是实际对象//这样的话就能够在用户填充数据时做一层拦截let validator = (obj,validatorConfig)=> {return new Proxy(obj,{_validatorConfig:validatorConfig,set(target,key,value,receiver){if(target.hasOwnProperty(key)){let validateAction = this._validatorConfig[key];if(!!validateAction(value)){return Reflect.set(target,key,value,receiver)}else{throw Error(`不能设置${key}到${value}`)}}else{throw Error(`${key}不存在`)}}});}//针对man对象的数据验证const manValidator = {name(val){return typeof val === 'string'},age(val){return typeof val === 'number' && val > 18}}//创建对象class Man{constructor(name,age=18){this.name= namethis.age= agereturn validator(this,manValidator)}}let manTest = new Man()
// manTest.name = 123 // Error: 不能设置name到123manTest.name = 'thatway'console.info(manTest) // 输出对象,其中name为thatway,age为18// manTest.age = '20'// Error: 不能设置age到20manTest.age = 20console.info(manTest) // 输出对象,其中name为thatway,age为20manTest.sex = 'boy' // Error: sex不存在
class
es6中定义类的关键字是class,不是Class
/* class *///定义类class Parent{constructor(name='thatway'){this.name= name}}//实例化let thatway = new Parent('wp');console.log(thatway); //打印 Object { name: "wp" }//继承类class Child extends Parent{}let child1 = new Child()console.info(child1) //Object { name: "thatway" }let child2 = new Child('tutu')console.info(child2) //Object { name: "tutu" }class Tutu extends Parent{constructor(name='child',type){//子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象super(name)this.type = type}}let tutu1 = new Tutu()console.info(tutu1) //Object { name: "child", type: undefined }let tutu2 = new Tutu('tutu','boy')console.info(tutu2)//Object { name: "tutu", type: "boy" }//getter、setterclass P{constructor(name='thatway'){this.name = name}set title(value){this.name = value}get title(){return this.name}}let p = new P()p.title = '123'console.info(p.title)//123console.info(p)//Object { name: "123" }//静态方法class S{constructor(name='thatway'){this.name = name}static sing(){console.info('金色的河蓝色的海,都有我们快乐的身影...')}}console.info(S.sing()) //金色的河蓝色的海,都有我们快乐的身影...//静态属性,静态属性不使用static关键字,目前的方案是直接赋值S.age = 18console.info(S.age) //18
promise
// 使js异步操作更加合理强大//es5中ajax回调模式function ajax5(callback){console.info("ajax5-1")setTimeout(function(){callback&&callback()})console.info("ajax5-2")}ajax5(function(){console.info("callback5");})//es6 promise方式let ajax6 = ()=> {console.info("ajax6-1")return new Promise((resolve,reject)=>{console.info("ajax6-2")setTimeout(()=>{resolve()},1000)})}ajax6().then(()=>{console.info("callback6")})let ajaxSencond = ()=>{return new Promise((resolve,reject)=>{return reject(new Error('second error'))})}//promise实例的then方法返回一个新的promise实例,所以可以链式调用,这样就可以将多个异步操作变得有序//第一个then返回的结果,会作为第二个then的参数//then是有顺序的ajax6().then(((data)=>{console.info("ajax6 succ")return "ajax6 rs"}),(data)=>{console.info("ajax6 error")}).then((data)=>{console.info("第二个then的resolve接收到的参数,可以拿着异步返回的结果做下面的逻辑了:"+data)ajaxSencond().then((data)=>{console.info("ajaxSencond succ")},(data)=>{console.info(data)})},(data)=>{console.info("ajax6 error")})//异常的捕获,建议用catchajaxSencond().then((data)=>{},(data)=>{console.info("异常在这里出现-回调")})ajaxSencond().then((data)=>{}).catch(function(data){console.info("异常在这里出现-catch")})//all 所有的promise都返回结果以后才会then,如果其中一个失败了,则直接用失败的结果let pro1 = new Promise((resolve,reject)=>{return resolve("data1")})let pro2 = new Promise((resolve,reject)=>{return resolve("data2")})Promise.all([pro1,pro2]).then(([data1,data2])=>{console.info(data1,data2)}).catch(function(error){console.info(error)}) //data1 data2let pro3 = new Promise((resolve,reject)=>{return reject(new Error('pro3 error'))})let pro4 = new Promise((resolve,reject)=>{return resolve("data4")})Promise.all([pro3,pro4]).then(([data3,data4])=>{console.info(data3,data4)}).catch(function(error){console.info(error)}) //Error: pro3 error//race 和all不一样的是,哪个先返回结果就用哪个的结果,其他的不用了let pro5 = new Promise((resolve,reject)=>{setTimeout(function(){return resolve("data5")},100)})let pro6 = new Promise((resolve,reject)=>{return resolve("data6")})Promise.race([pro5,pro6]).then((data)=>{console.log(data)}).catch((data)=>{console.log(data)}) //data6
iterator
/* Iterator *///数据集合结构的统一遍历方法//具有Symbol.iterator属性的数据结构就可以使用for of循环遍历function Obj(value) {this.value = value;this.next = null;}Obj.prototype[Symbol.iterator] = function() {var iterator = { next: next };var current = this;function next() {if (current) {var value = current.value;current = current.next;return { done: false, value: value };} else {return { done: true };}}return iterator;}var one = new Obj(1);var two = new Obj(2);var three = new Obj(3);one.next = two;two.next = three;for (var i of one){console.log(i); // 1, 2, 3}// 原生具备 Iterator 接口的数据结构如下。
//
// Array
// Map
// Set
// String
// TypedArray
// 函数的 arguments 对象
// NodeList 对象
Gernerator
/* Generator */// generator是es6中的另一个异步解决方法//基本定义let tell = function* (){yield 'a';yield 'b';return 'c'}let rs = tell()console.info(rs.next()) //Object { value: "a", done: false }console.info(rs.next()) //Object { value: "b", done: false }console.info(rs.next()) //Object { value: "c", done: true } 有return的时候done会变为trueconsole.info(rs.next()) //Object { value: undefined, done: true }//Generator返回的就是一个Iteratorlet obj = {}obj[Symbol.iterator] = function* (){yield '1';yield '2';return}for(let val of obj){console.info(val)}//Generator应用场景:状态机//1、2、3三种状态let state = function* (){while (true){yield 1;yield 2;yield 3;}}let status = state();for(let i = 0;i<= 10;i++){console.info(status.next()) //一直是1、2、3依次输出}//抽奖次数限制let action = (count)=> {//抽一次奖品console.info(`剩余${count}次`)}let timesController = function* (count){while(count > 0){count --yield action(count)}}let times = 5let lottry = timesController(times)lottry.next()lottry.next()lottry.next()lottry.next()lottry.next()lottry.next()//长轮询实例let ajax = function* (){//yield返回一个promise实例yield new Promise((resolve,reject)=> {//异步耗时1秒钟后返回结果 setTimeout(()=>{return resolve('ok')},1000)})}//查询方法let pull = ()=> {//拿到Generatorlet generator = ajax()//执行ajaxlet step = generator.next()// step.value是返回的promisestep.value.then((data)=>{//如果结果不是期望的,1秒以后再一次调用,直到结果是期望的 if(data != 'ok'){setTimeout(()=>{console.info("等待1秒后再轮询")pull()},1000)}else{console.info('结束了:'+data)}})}//开始拉取数据 pull()
Decorators
这个api需要插件支持
npm i babel-plugin-transform-decorators-legacy -D
.babelrc中添加插件
"plugins": ["transform-decorators-legacy"]
/* Decorator 修饰器 */// 是一个函数// 修改行为(可以修改或者扩展行为)// 类的行为 (只能在类上使用)// 类似于java的注解//声明一个修饰器let decoratorReadOnly = function(target,name,descriptor){descriptor.writable = falsereturn descriptor}//修饰某一个类的行为class Test{@decoratorReadOnlygetName(){return 'thatway'}}let test = new Test()test.getName = function(){return 'new'}//TypeError: "getName" is read-only//也可以放在类前修饰类let typeDecorator = function(target,name,descriptor){target.name = 'a'}@typeDecoratorclass Empty{}console.info('修饰类',Empty.name) //"name" is read-only//日志埋点let log = (type)=> {return function(target,name,descriptor){let src_method = descriptor.valuedescriptor.value =(...args)=>{src_method.apply(target,args)console.info(`log ${type}`)}}}class logTestObj {@log('method1')method1(){console.info('method1')}@log('method2')method2(){console.info('method2')}}let o = new logTestObj()o.method1()o.method2()
core-decorators包已经封装了常见的修饰符
npm i core-decorators
模块
模块主要涉及到import和export的语法,有几种不同情况下不同的写法。
学习资料
关于es6的入门,阮一峰大神已经整理的非常易懂了:
阮大神的babel介绍和ES6手册
http://www.ruanyifeng.com/blog/2016/01/babel.html
http://es6.ruanyifeng.com