参考网上资料,这里简单记录下ES6部分知识,后续学习了继续补充,希望全面拥抱ES6。
一、let\const与var
let,const用于声明变量,用来替代老语法的var关键字,与var不同的是,let/const会创建一个块级作用域(通俗讲就是一个花括号内是一个新的作用域)。
区别:
1.let/const关键字声明变量的for循环和var声明的有些不同。var在for循环中声明一次变量;而let\const每次循环都会声明一次。
2.暂时性死区:在变量声明之前无法使用该变量,弥补了var的缺陷(var有变量提升)。
3.Const: 声明变量的时候必须赋值,否则会报错;而且该const声明的变量不能被改变(这里不能改变特指的是赋值为String类型和Number类型,而赋值给个引用类型如数组则可以改变)。
const xx = "123"; //String类型
undefined
xx = "456"
VM383:1 Uncaught TypeError: Assignment to constant variable.
at <anonymous>:1:4
(anonymous) @ VM383:1
const yy = []; //引用类型
undefined
yy.push(1)
1
console.log(yy)
VM447:1 [1]
undefined
4.Let\const没有显式的声明块级作用域,不会被提升至全局变量,它不属于全局变量window对象中(而var声明的变量会被提升至window对象中),而是在一个script作用域下面,这样的好处是不用担心污染全局的window对象。
二、箭头函数与function
允许使用箭头(=>)定义函数。
区别:
1.箭头函数没有arguments。
2.箭头函数没有prototype属性,没有constructor,即不能作为构造函数使用。
3.箭头函数没有自己的this,它的this属于上下文。
4.箭头函数替代了以前需要显式的声明一个变量保存this的操作,使得代码更加的简洁。
5.总的来说箭头函数是为了解决如何绑定this,而不止是为了省略function这几个关键字。
三、iterator迭代器
对于可迭代的数据解构,ES6在内部部署了一个[Symbol.iterator]属性,它是一个函数,执行后会返回iterator对象(也叫迭代器对象,也叫iterator接口),拥有[Symbol.iterator]属性的对象即被视为可迭代的。
理解:
1.默认具有iterator接口的数据结构有:
Array;Map;Set;String;TypedArray;函数的arguments对象;NodeList对象。
2.可迭代的数据结构会有一个[Symbol.iterator]方法。
3.[Symbol.iterator]执行后返回一个iterator对象。
4.iterator对象有一个next方法。
5.next方法执行后会返回一个有value,done属性的对象。value代表值,done代表是否完成循环。
如:
var a = [1,2,3],
it = a[Symbol.iterator]();
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 3, done: false}
it.next() // {value: undefined, done: true}
四、解构赋值
解构赋值可以直接使用对象的某个属性,而不需要通过属性访问的形式使用。
理解:
1.数组解构的一个用途是交换变量,避免以前要声明一个临时变量值存储值。
五、剩余、扩展运算符
使用3个点(…),后面跟着一个数组,它使得可以"展开"这个数组。
理解:
1.如let x = [1,2,3]; let y = [...x,4,5,6];console.log(y) //1,2,3,4,5,6
2.扩展运算符:扩展运算符可以和数组的解构赋值一起使用,但是必须放在最后一个,其原理就是消耗所有迭代器,注意对象中没有迭代器。如:
Let [first,...xArr] = [1,2,3] // => first //1;xArr // [2,3]
3.剩余运算符:最重要的一个特点就是替代了以前的arguments。如:
Function func1(...xxx){console.log(xxx)};func1(7,8,9,10); => // [7,8,9,10]。
区别:
剩余运算符和扩展运算符的区别就是,剩余运算符会收集这些集合,放到右边的数组中(特别适用在函数的形参当中),扩展运算符是将右边的数组拆分成元素的集合,它们是相反的(特别适用在数组的解构赋值中)。
用途:
1.适用扩展运算符可以快速的将一个类数组转化为一个真正的数组。
如:let arr = [...nodeList]
2.合并多个数组:如let x = [1];let y = [2];let z = [...x,...y]; // [1,2]
。在es5里面就只有适用concat() API。
3.函数柯里化。(这个暂时不太清楚原理只是记录下来)
六、对象属性、方法的简写
1.允许当对象的属性和值相同时,省略属性名。
如:let x = 1; let x = {x};
2.对象属性简写经常与解构赋值一起使用。
如:let func1 = () => ({x:1,y:2,z:3});let {x,y,z} = func1();//x:1,y:2,z:3
3.允许当一个对象的属性的值是一个函数(即是一个方法),可以使用简写的形式。
如:ES5中:var obj = {func1:function(){}};ES6中 let obj2 = {func1(){}}或者let obj3 = {func:()=>{}}。
七、For…of循环
允许遍历一个含有iterator接口的数据结构并且返回各项的值。
与for…in的区别:
1.for … of遍历获取的是对象的键值(value),for … in 获取的是对象的键名(key)。
2.for … in会遍历对象的整个原型链,性能非常差不推荐使用,而for … of只遍历当前对象不会遍历原型链。
3.对于数组的遍历,for … in会返回数组中所有可枚举的属性(包括原型链),for … of只返回数组的下标对于的属性值。
for(最原始的写法)、
forEach(ES5,但是它不支持使用break、continue和return语句)、
for…of(ES6,循环数组的元素值)这三个是循环数组(对象数组)的;
for…in循环数组索引、对象的属性,但使用 for…in 原型链上的所有属性都将被访问,用 hasOwnProperty() 方法解决。
八、Promise
JS的异步编程。
JS是单线程的,因为多个线程改变DOM的话会导致页面紊乱,所以设计为一个单线程的语言,但是浏览器是多线程的,这使得JS同时具有异步的操作。
与回调函数的区别:
1.多重回调,导致回调地狱。
2.不清楚回调是否都是异步调用。
3.超时的话,可能多次执行你的回调函数业务逻辑。
理解:
1.Promise是一个构造函数,通过new关键字创建一个Promise的实例。
2.Promise并不是取代回调,而是对回调的一种提升,这个要弄清楚,它的实现依然是回调。
3.Promise支持链式调用。
4.Promise本身是一个状态机,具有pending(等待),resolve(决议),reject(拒绝)这3个状态,当请求发送没有得到响应的时候会pending状态,并且一个Promise实例的状态只能从pending => resolve 或者从 pending => reject,即当一个Promise实例从pending状态改变后,就不会再改变了(不存在resolve => reject 或 reject => resolve)。简单来说就是只会执行resolve或者执行reject,而且只要改变了pending就不会再改变。
5.Promise实例必须主动调用then方法,才能将值从Promise实例中取出来。
6.Promise.then(res=>{},err=>{}) 或者 Promise.then(res=>{}).catch(err=>{})。
// 创建Promise实例(注意1:Promise 新建后就会立即执行,注意2:调用resolve或reject并不会终结 Promise 的参数函数的执行,但是一般不会这样写,都会写在then()里面)
const p = new Promise(function(resolve,reject){
console.log('我是马上执行')
if(true){
resolve('我是resolve状态')
}else{
reject()
}
console.log('resovle,reject不会阻碍我执行')
})
p.then(function(value) {
// success
console.log(value)
}, function(error) {
// failure
});
输出:
"我是马上执行" // 注意1:Promise 新建后就会立即执行
"resovle,reject不会阻碍我执行" //注意2:调用resolve或reject并不会终结 Promise 的参数函数的执行
"我是resolve状态" //最后根据状态机来决定
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
结合ajax应用:
function ajax(method,url,data){
var request = new XMLHttpRequest();
return new Promise(function(resolve,reject){
request.open(method,url,true);
request.send();
request.onreadystatechange = function(){
if(request.readyState === 4){
if(request.status === 200 ){
resolve(request.responseText)
}else{
reject(request.status)
}
}
}
})
}
var p = ajax(‘get','......')
p.then(function(success){
console.log(success)
}).catch(function(err){
console.log(err)
})
前面有使用then和catch链式串行执行异步操作,也可以并行执行若干异步操作,如API:Promise.all()。在执行完所有异步操作后才执行回调函数且返回值为所有执行完异步操作后值的数组集合。若其中一个异步操作被拒绝了则直接返回被拒绝也就是reject,不得等其他执行完成,相应值也不是数组只是一个被reject所返回的值。
let p1 = new Promise(function(resolve,reject){
resolve(1);
})
let p2 = new Promise(function(resolve,reject){
resolve(2);
})
let p3 = Promise.all([p1,p2]);
p3.then(value=>{
console.log(value); //[1,2]
})
有并行所有异步完成再回调,因此也有并行看谁先执行完就回调的操作,如API:Promise.race(),只要其中一个异步操作状态变了那么就执行回调不用等其他操作,且返回值为最先执行完的值不是数组。
let p1 = new Promise(function(resolve,reject){
setTimeout(functionI(){
resolve('我是p1,我是第一个')
},200);
})
let p2 = new Promise(function(resolve,reject){
setTimeout(functionI(){
resolve(’我是p2,我是第二个‘)
},400);
})
let p3 = Promise.all([p1,p2]);
p3.then(value=>{
console.log(value); // '我是p1,我是第一个'
})
九、Module
1.Module使用import关键字导入模块,export关键字导出模块。
2.Module是静态的,也就是说它是在编译阶段运行,和var以及function一样具有提升效果。
3.Module支持使用export {<变量>}导出具名的接口,或者export default导出匿名的接口。如:let x = 10; let y = 20;
export {x} 和 export default y;
注意在导入的代码中
Import {x} form ‘xx.js’;import y from ‘xx.js’
中{x}是一个变量的引用而y是一个固定值。即若在xx.js中x值被改变了,那么引入代码中的x改变,而xx.js中y值被改变了,引入代码中的y不会改变。
4.Module通过export {<变量>}输出的是一个变量的引用,export default输出的是一个值的拷贝。
与commonjs的区别:
1.CommonJs输出的是一个值的拷贝。
2.CommonJs运行在服务器上,被设计为运行时加载,即代码执行到那一行才回去加载模块,而ES6 Module是静态的输出一个接口,发生在编译的阶段。
3.CommonJs在第一次加载的时候运行一次,之后加载返回的都是第一次的结果,具有缓存的效果,ES6 Module则没有。
解决动态加载模块:
把import作为一个函数可以实现动态加载模块,它返回一个Promise,Promise被resolve时的值为输出的模块。如:
Import(‘xx.js’).then(res=>{})
Vue中使用场景:
Vue中路由的懒加载的ES6写法就是使用了这个技术,使得在路由切换的时候能够动态的加载组件渲染视图。
十、函数默认值
1.允许在函数的参数中设置默认值。
2.简单来说就是当参数没给值时或者undefined,赋个默认的值给它,如:
function func(x = 2){return x}; func() //2
十一、Proxy
Proxy作为一个"拦截器",可以在目标对象前架设一个拦截器,他人访问对象,必须先经过这层拦截器,Proxy同样是一个构造函数,使用new关键字生成一个拦截对象的实例,ES6提供了非常多对象拦截的操作,几乎覆盖了所有可能修改目标对象的情况(Proxy一般和Reflect配套使用,前者拦截对象,后者返回拦截的结果,Proxy上有的的拦截方法Reflect都有)。
如:
Let obj = {};
Obj = new Proxy(obj,{
Set(target,key,val){
Console.log(‘访问到这一层’)
Return Reflect.set(target,key,val)
}
})
Vue应用场景:
3.0 将带来一个基于 Proxy 的 observer 实现,它可以提供覆盖语言 (JavaScript——译注) 全范围的响应式能力,消除了当前 Vue 2 系列中基于 Object.defineProperty 所存在的一些局限,如: 对属性的添加、删除动作的监测 对数组基于下标的修改、对于 .length 修改的监测 对 Map、Set、WeakMap 和 WeakSet 的支持。
十二、Object.assign
ES6新增的Object静态方法允许我们进行多个对象的合并。
应用场景:
1.Vue中重置data中的数据。
如:Object.assign(this.$data,this.$options.data())
2.给对象合并需要的默认属性。
如:let options = Object.assign({},DEFAULTS,options)
3.在传参的时候可以多个数据合并成一个对象传给后端。