ES6特性
前言
ES全称ECMAScript,ECMAScript是ECMA制定的标准化脚本语言。目前JavaScript使用的ECMAScript版本为ECMAScript-262。
ECMAScript 标准建立在一些原有的技术上,最为著名的是 JavaScript (网景) 和 JScript (微软)。它最初由网景的 Brendan Eich 发明,第一次出现是在网景的 Navigator 2.0 浏览器上。Netscape 2.0 以及微软 Internet Explorer 3.0 后序的所有浏览器上都有它的身影。
ECMAScript版本 | 发布时间 | 新增特性 |
---|---|---|
ECMAScript 2009(ES5) | 2009年11月 | 扩展了Object、Array、Function的功能等 |
ECMAScript 2015(ES6) | 2015年6月 | 类,模块化,箭头函数,函数参数默认值等 |
ECMAScript 2016(ES7) | 2016年3月 | includes,指数操作符 |
ECMAScript 2017(ES8) | 2017年6月 | sync/await,Object.values(),Object.entries(),String padding等 |
ES6 特性
下面会介绍几个比较常用的新特性:
- 变量let和const
- 箭头函数
- 解构赋值
- 模板字符串
- 默认参数
- 对象属性简写
- Promise
- 类Class
- 模块Module
1、变量let、const和var
let
- let为JavaScript新增了块级作用,let声明的变量只在其所在代码块有效;
不存在变量提升,必须先定义后使用
console.log(foo);//ReferenceError let foo = 3;
暂时性死区 只要块级作用域内存在let命令,他声明的变量就是绑定在这个区域,不再受外部影响;ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ结束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
- 不允许重复声明
const
- const用来声明常量,一旦声明,其值就不能改变;
- const一旦声明常量就必须立即赋值。只声明不赋值就会报错
- 和let命令相同:只能在所声明的块级作用域内有效
- const命令声明的常量也不能提升,同样存在暂时性死区,只能在声明后使用
- onst也不能重复声明常量
const命令只是保证变量指向的地址不变,并不保证改地址的数据不变
const foo = {}; foo.prop = 123; foo.prop;//123
2、箭头函数
ES6允许使用箭头(=>)定义函数。
=>不只是关键字function的简写,它还带来了其它好处。箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题。有经验的JavaScript开发者都熟悉诸如var self = this;或var that = this这种引用外围this的模式。但借助=>,就不需要这种模式了。
如果箭头函数不需要参数或者需要多个参数,就是用圆括号代表参数部分。
let sum = (num1,num2) => { return num1+num2 }
箭头函数的特点:
- 函数体内的this对象就是定义时所在的对象,而不是他使用时所在的对象
- 不可以当做构造函数,也就是说不可以使用new命令,否则会抛出一个错误
- 不可以使用arguments对象,该对象在函数体内不存在,如果要使用可以用rest参数代替
- 不可以使用yield命令,因此箭头函数不能用作Generator函数
//箭头函数中this指向固定 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
3、解构赋值
ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为 解构
变量解构
数组解构
let [x,y,z] = new Set(["a","b","c"]); x //a let [foo = true] = []; foo //true let [x,y='b'] = [a]; let [x,y='b'] = [a,undefined]; //以上两种输出都是 x //a y //b
对象解构
对象的解构与数组的解构有一个重要的不同;数组的元素是按次序排列的,变量的取之由他的位置决定;而对象的属性是没有次序的,变量必须与属性同名,才能取到正确的值
let {bar,foo} = {foo:"aaa",bar:"bbb"}; bar // bbb foo //aaa let {baz,foo} = {foo:"aaa",bar:"bbb"}; baz //undefined 如果变量名与属性名不一致 let {first:f, last:l} = {first:"hello", last:"bye"}; first //undefined f //hello last //undefined l //bye
也就是说对象解构赋值的内部机制,是先找到同名属性,然后在赋值给对应的变量,真正被赋值的是后者,而不是前者
函数解构
参数默认值可以与解构赋值的默认值结合使用
function foo(x,y=5){ console.log(x,y); } foo({});//undefined,5 foo(1);//1,5 foo(1,2)//1,2
4、模板字符串
模版字符串是增强版的字符串,用反引号(`)标识;他可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
let name="Tom",time="today"; `hello ${name},how arw your ${time}`
大括号里面可以放任意的JavaScript表达式,可以进行运算,以及引用对象属性。
let x = 1; let y = 2; `${x}+${y}=${x+y}`//1+2=3 let obj = {x:1,y:3}; `${obj.x+obj.y}` // 4
模版字符串中还能调用函数
function fn(){ return "hehe"; } `test ${fn()}` // test hehe
多行字符串
//ES5中 var roadPoem = 'Then took the other, as just as fair,nt' + 'And having perhaps the better claimnt' + 'Because it was grassy and wanted wear,nt' + 'Though as for that the passing therent' + 'Had worn them really about the same,nt'; //ES6 let roadPoem = `Then took the other, as just as fair, And having perhaps the better claim Because it was grassy and wanted wear, Though as for that the passing there
5、默认参数
我们以前不得不通过下面方式来定义默认参数
var link = function (height, color, url) { var height = height || 50; var color = color || 'red'; var url = url || 'http://azat.co'; ... }
一切工作都是正常的,直到参数值是0后,就有问题了,因为在JavaScript中,0表示false,它是默认被hard-coded的值,而不能变成参数本身的值。当然,如果你非要用0作为值,我们可以忽略这一缺陷并且使用逻辑OR就行了!但在ES6,我们可以直接把默认值放在函数申明里:
let link = function(height = 50, color = 'red', url = 'http://azat.co') { ... }
6、对象属性简写
ES5中对象必须包含属性和值
const name='Ming',age='18',city='Shanghai'; const student = { name:name, age:age, city:city }; console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}
使用ES6的话就会变得非常简洁
const name='Ming',age='18',city='Shanghai'; const student = { name, age, city }; console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}
7、Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
下面是一个简单的用setTimeout()实现的异步延迟加载函数:
setTimeout(function(){ console.log('Test!'); }, 1000);
在ES6中,我们可以用promise重写:
let wait1000 = new Promise((resolve, reject)=> { setTimeout(resolve, 1000); }).then(()=> { console.log('Yay!'); });
const promise = new Promise((resolve, reject)=> { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then((value)=> { // success }, (error)=> { // failure });
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
8、类Class
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 2);
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class
改写,就是下面这样。
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } }
上面代码定义了一个“类”,可以看到里面有一个constructor
方法,这就是构造方法,而this
关键字则代表实例对象。也就是说,ES5 的构造函数Point
,对应 ES6 的Point
类的构造方法。
Point
类除了构造方法,还定义了一个toString
方法。注意,定义“类”的方法的时候,前面不需要加上function
这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
9、Modules模块
众所周知,在ES6以前JavaScript并不支持本地的模块。人们想出了AMD,RequireJS,CommonJS以及其它解决方法。现在ES6中可以用模块import 和export 操作了。
在ES5中,你可以在 <script>中直接写可以运行的代码(简称IIFE),或者一些库像AMD。然而在ES6中,你可以用export导入你的类。下面举个例子,在ES5中,module.js有port变量和getAccounts 方法:
module.exports = { port: 3000, getAccounts: function() { ... } }
在ES5中,main.js需要依赖require('module') 导入module.js:
var service = require('module.js'); console.log(service.port); // 3000
但在ES6中,我们将用export 和 import。例如,这是我们用ES6 写的module.js文件库:
export var port = 3000; export function getAccounts(url) { ... }
如果用ES6来导入到文件main.js中,我们需用import 语法,例如:
import {port, getAccounts} from 'module'; console.log(port); // 3000
或者我们可以在main.js中把整个模块导入, 并命名为 service:
import * as service from 'module'; console.log(service.port); // 3000