- const声明变量不能改变,如果声明的是一个引用类型,则不能改变它的内存地址(比如应用一个对象,可以改变对象里面的值,但是不能重新把变量赋值)
const obj = {name:'zs', age:14}
obj.name = 'ls';// ok
obj = {name:'ls', age:14};// fail
- 对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错
let foo;
let {foo} = {foo: 1}; // SyntaxError: Duplicate declaration "foo"
let baz;
let {bar: baz} = {bar: 1}; // SyntaxError: Duplicate declaration "baz"
let foo;
({foo} = {foo: 1}); // 成功
let baz;
({bar: baz} = {bar: 1}); // 成功
- let/const的特点,ES6规定它们不属于顶层全局变量的属性(let/const定义的变量不再绑定到window对象上了)
const name = 'zs';
var age = 14;
console.log(window.name);//undefined
console.log(window.age);// 14
- let/const声明的变量x是在一个叫script作用域下的,而var声明的变量因为变量提升所以提升到了全局变量window对象中,这使我们能放心的使用新语法,不用担心污染全局的window对象
- 箭头函数对于使用function关键字创建的函数有以下区别
-
箭头函数没有arguments(建议使用更好的语法,剩余运算符替代)
-
箭头函数没有prototype属性,没有constructor,即不能用作与构造函数(不能用new关键字调用)
-
箭头函数没有自己this,它的this是词法的,引用的是上下文的this,即在你写这行代码的时候就箭头函数的this就已经和外层执行上下文的this绑定了(这里个人认为并不代表完全是静态的,因为外层的上下文仍是动态的可以使用call,apply,bind修改,这里只是说明了箭头函数的this始终等于它上层上下文中的this)
- iterator迭代器
-
它却是另外4个ES6常用特性的实现基础(解构赋值,剩余/扩展运算符,生成器,for of循环)
-
对于可迭代的数据解构,ES6在内部部署了一个[Symbol.iterator]属性,它是一个函数,执行后会返回iterator对象(也叫迭代器对象,也叫iterator接口),拥有[Symbol.iterator]属性的对象即被视为可迭代的
image.png
-
默认具有iterator接口的数据结构有以下几个,注意普通对象默认是没有iterator接口的(可以自己创建iterator接口让普通对象也可以迭代)
-
Array
-
Map
-
Set
-
String
-
TypedArray(类数组)
-
函数的 arguments 对象
-
NodeList 对象(是一个类似数组的对象,它的成员是节点对象。Node.childNodes、document.querySelectorAll()返回的都是NodeList实例对象.)
-
iterator迭代器是一个对象,它具有一个next方法所以可以这么调用

- next方法返回又会返回一个对象,有value和done两个属性,value即每次迭代之后返回的值,而done表示是否还需要再次循环,可以看到当value为undefined时,done为true表示循环终止
- 解构赋值
let {a:name}= {a:'zs'};
console.log(name); // zs
数组解构的原理其实是消耗数组的迭代器,把生成对象的value属性的值赋值给对应的变量
- 剩余/扩展运算符
-
剩余/扩展运算符同样也是ES6一个非常重要的语法,使用3个点(...),后面跟着一个数组,它使得可以"展开"这个数组
-
扩展运算符
只要含有iterator接口的数据结构都可以使用扩展运算符
扩展运算符可以和数组的解构赋值一起使用,但是必须放在最后一个
image.png
-
剩余运算符
剩余运算符最重要的一个特点就是替代了以前的arguments
function fn1(a,b,c){
console.log(arguments[0], arguments[1], arguments[2])
}
fn1(1,2,3);
function fn2(...rest){
console.log(rest)
}
fn2(1,2,3,['5','6'],{name:'zs'}) ;// [1,2,3,[['5','6']],{name:'zs'}]
剩余运算符和扩展运算符的区别就是,剩余运算符会收集这些集合,放到右边的数组中,扩展运算符是将右边的数组拆分成元素的集合,它们是相反的
在对象中使用扩展运算符
这个是ES9的语法,ES9中支持在对象中使用扩展运算符,之前说过数组的扩展运算符原理是消耗所有迭代器,但对象中并没有迭代器,我个人认为可能是实现原理不同,但是仍可以理解为将键值对从对象中拆开,它可以放到另外一个普通对象中
let obj = {a:1, b:2};
let obj2 = {...obj, c:3}; // {a:1, b:2, c:3}
- 另外一个ES6新增的API相似,即Object.assign,它们都可以合并对象,但是还是有一些不同Object.assign会触发目标对象的setter函数,而对象扩展运算符不会
-
使用扩展运算符可以快速的将类数组转为一个真正的数组
image.png
-
合并多数组
image.png
- for ... of循环
-
for ... of是作为ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构并且返回各项的值,和ES3中的for ... in的区别如下(这就是不能用for of遍历普通对象的原因,对象没有Symbol.iterator属性)
-
for ... of遍历获取的是对象的键值,for ... in 获取的是对象的键名
-
for ... in会遍历对象的整个原型链,性能非常差不推荐使用,而for ... of只遍历当前对象不会遍历原型链
-
对于数组的遍历,for ... in会返回数组中所有可枚举的属性(包括原型链),for ... of只返回数组的下标对于的属性值
-
函数默认值配合解构赋值

-
第一行给func函数传入了2个空对象,所以函数的第一第二个参数都不会使用函数默认值,然后函数的第一个参数会尝试解构对象,提取变量x,因为第一个参数传入了一个空对象,所以解构不出变量x,但是这里又在内层设置了一个默认值,所以x的值为10,而第二个参数同样传了一个空对象,不会使用函数默认值,然后会尝试解构出变量y,发现空对象中也没有变量y,但是y没有设置默认值所以解构后y的值为undefined
-
第二行第一个参数显式的传入了一个undefined,所以会使用函数默认值为一个空对象,随后和第一行一样尝试解构x发现x为undefined,但是设置了默认值所以x的值为10,而y和上文一样为undefined
-
第三行2个参数都会undefined,第一个参数和上文一样,第二个参数会调用函数默认值,赋值为{y:10},然后尝试解构出变量y,即y为10
-
第四行和第三行相同,一个是显式传入undefined,一个是隐式不传参数
-
第五行直接使用传入的参数,不会使用函数默认值,并且能够顺利的解构出变量x,y
- Proxy
-
Proxy作为一个"拦截器",可以在目标对象前架设一个拦截器,他人访问对象,必须先经过这层拦截器,Proxy同样是一个构造函数,使用new关键字生成一个拦截对象的实例,ES6提供了非常多对象拦截的操作,几乎覆盖了所有可能修改目标对象的情况(Proxy一般和Reflect配套使用,前者拦截对象,后者返回拦截的结果,Proxy上有的的拦截方法Reflect都有)
image.png
image.png
实际应用:
handler.apply
apply可以让我们拦截一个函数(JS中函数也是对象,Proxy也可以拦截函数)的执行,我们可以把它用在函数节流中

调用拦截后的函数

vue
- 尤大预计2019年下半年发布Vue3.0,其中一个核心的功能就是使用Proxy替代Object.defineProperty
- Vue内部使用Object.defineProperty进行的数据劫持,而这个API无法探测到对象根属性的添加和删除,以及直接给数组下标进行赋值,所以不会通知渲染watcher进行视图更新,而理论上这个API也无法探测到数组的一系列方法(push,splice,pop),但是Vue框架修改了数组的原型,使得在调用这些方法修改数据后会执行视图更新的操作
- Proxy就没有这个问题,并且还提供了更多的拦截方法,完全可以替代Object.defineProperty,唯一不足的也就是浏览器的支持程度了
image.png
掘金翻译的尤大Vue3.0计划
参考资料 :
https://juejin.im/post/5bb719b9f265da0ab915dbdd
https://juejin.im/post/5c6234f16fb9a049a81fcca5
- ES6 module
(requier,module.exports是commonjs的 exports其实指向的是module.exports.)
( import export es6的)
- export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
- 上面代码输出变量foo,值为bar,500毫秒之后变成baz。
这一点与CommonJS规范完全不同。CommonJS模块输出的是值的缓存,不存在动态更新
- ES6模块加载的实质
- ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
网友评论