浅析es6知识点
-
Module 的语法
import
和export
命令只能在模块的顶层,不能在代码块之中(比如,在if
代码块之中,或在函数之中)。require
是运行时加载模块,import是编译阶段执行的
import
命令能够接受什么参数,import()
函数就能接受什么参数,两者区别主要是后者为动态加载。import()
类似于 Node 的require
方法,区别主要是前者是异步加载,后者是同步加载。
2. Module 的加载实现
浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>
标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
<script src="path/to/myModule.js" defer></script> <script src="path/to/myModule.js" async></script>
defer
与async
的区别是:
- 前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,
defer
是“渲染完再执行”,async
是“下载完就执行”。 - 另外,如果有多个
defer
脚本,会按照它们在页面出现的顺序加载,而多个async
脚本是不能保证加载顺序的。
<script type="module" src="foo.js"></script>
浏览器对于带有type="module"
的<script>
,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>
标签的defer
属性。
- defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
- 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
- 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
- async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
- 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics
ES6 模块与 CommonJS 重大差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
注:CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。除非写成一个函数,才能得到内部变动后的值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import
,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
注:CommonJS 加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
ES6 模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。
ES6 模块之中,顶层的this
指向undefined
;CommonJS 模块的顶层this
指向当前模块,这是两者的一个重大差异。
3.ES6箭头函数this指向
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。 - 不可以使用
yield
命令,因此箭头函数不能用作Generator函数。 -
function Timer() { this.a1 = 0; this.a2 = 0; // 箭头函数 setInterval(() => this.a1++, 1000); // 普通函数 setInterval(function () { this.a2++; }, 1000); } var timer = new Timer(); setTimeout(() => console.log('a1: ', timer.a1), 3100); setTimeout(() => console.log('a2: ', timer.a2), 3100); // a1: 3 // a2: 0
上面代码中,
Timer
函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this
绑定定义时所在的作用域(即Timer
函数),后者的this
指向运行时所在的作用域(即全局对象)。 this
指向的固定化,并不是因为箭头函数内部有绑定this
的机制,实际原因是箭头函数根本没有自己的this
,导致内部的this
就是外层代码块的this
。正是因为它没有this
,所以也就不能用作构造函数。- 另外,由于箭头函数没有自己的
this
,所以当然也就不能用call()
、apply()
、bind()
这些方法去改变this
的指向
4.Rest参数
- Rest参数接收函数的多余参数,组成一个数组,放在形参的最后
-
function func(a, b, ...theArgs) { // ... }
-
Rest参数和arguments对象的区别
- rest参数只包括那些没有给出名称的参数,arguments包含所有参数
- arguments对象不是真正的array,而rest参数是Array的实例,可以直接应用sort, map, forEach, pop等方法
- arguments对象拥有一些自己额外的功能
- rest参数之后不能再有其它参数(即,只能是最后一个参数),否则会报错
- 函数的length属性,不包括rest参数
- Rest参数接收函数的多余参数,组成一个数组,放在形参的最后