ES6特性

58次阅读

共计 5889 个字符,预计需要花费 15 分钟才能阅读完成。

前言

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
  1. let 为 JavaScript 新增了块级作用,let 声明的变量只在其所在代码块有效;
  2. 不存在变量提升,必须先定义后使用

    console.log(foo);//ReferenceError
    let foo = 3;
  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
    }
  4. 不允许重复声明
const
  1. const 用来声明常量,一旦声明,其值就不能改变;
  2. const 一旦声明常量就必须立即赋值。只声明不赋值就会报错
  3. 和 let 命令相同:只能在所声明的块级作用域内有效
  4. const 命令声明的常量也不能提升,同样存在暂时性死区,只能在声明后使用
  5. onst 也不能重复声明常量
  6. 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}

箭头函数的特点:

  1. 函数体内的 this 对象就是定义时所在的对象,而不是他使用时所在的对象
  2. 不可以当做构造函数,也就是说不可以使用 new 命令,否则会抛出一个错误
  3. 不可以使用 arguments 对象,该对象在函数体内不存在,如果要使用可以用 rest 参数代替
  4. 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数
// 箭头函数中 this 指向固定
function foo() {setTimeout(() => {console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({id: 42});
// id: 42

3、解构赋值

ES6 允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为 解构

变量解构
  1. 数组解构

    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
  2. 对象解构

    对象的解构与数组的解构有一个重要的不同;数组的元素是按次序排列的,变量的取之由他的位置决定;而对象的属性是没有次序的,变量必须与属性同名,才能取到正确的值

    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

正文完
 0