JavaScript之ES6


在前端技术快速发展的今天,越来越多的前端新词汇涌现出来,特别是NodeJs出来之后,出现了ES6,ECMAScript 2015 ,TypeScript,它们语法上与JS相似,但又有区别,一开始接触还真有些头疼,本文将系统的整理一下它们之间的关系与区别。ECMAScript 6.0(以下简称ES6)是JavaScript语言(现在是遵循ES5标准)的下一代标准,已经在2015年6月正式发布了。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ECMAScript 和 JavaScript 的关系

一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScript 和 ActionScript)。日常场合,这两个词是可以互换的。

ECMAScript 2015与ES6 与的关系

ECMAScript 2015(简称 ES2015)这个词,也是经常可以看到的。它与 ES6 是什么关系呢?2011 年,ECMAScript 5.1 版发布后,就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本。但是,因为这个版本引入的语法功能太多,而且制定过程当中,还有很多组织和个人不断提交新功能。事情很快就变得清楚了,不可能在一个版本里面包括所有将要引入的功能。常规的做法是先发布 6.0 版,过一段时间再发 6.1 版,然后是 6.2 版、6.3 版等等。但是,标准的制定者不想这样做。他们想让标准的升级成为常规流程:任何人在任何时候,都可以向标准委员会提交新语法的提案,然后标准委员会每个月开一次会,评估这些提案是否可以接受,需要哪些改进。如果经过多次会议以后,一个提案足够成熟了,就可以正式进入标准了。这就是说,标准的版本升级成为了一个不断滚动的流程,每个月都会有变动。标准委员会最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的 6 月份,草案就自然变成了新一年的版本。这样一来,就不需要以前的版本号了,只要用年份标记就可以了。ES6 的第一个版本,就这样在 2015 年 6 月发布了,正式名称就是《ECMAScript 2015 标准》(简称 ES2015)。2016 年 6 月,小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准。根据计划,2017 年 6 月发布 ES2017 标准。因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。

JavaScript 与 TypeScript 的关系

TypeScript是Javascript的超集,实现以面向对象编程的方式使用Javascript。当然最后代码还是编译为Javascript。因此TypeScript 实际上是增强了Javascript语言,利用TypeScript 使得JavaScript开发大型项目成为可能,特别是Node出来后这一优势在服务端开发更为明显,下面来比较一下TypeScript与Javascript各自的特点

JavaScript的特点

  1. JavaScript 是一种基于对象的语言,可以创建对象同时使用现有对象。但是 Javascript 并不支持其它面向对象语言所具有的继承和重载功能。
  2. JavaScript 的语法简单,使用的变量为弱类型。
  3. JavaScript 语言较为安全,使用单线程模型,不存在数据共享同步问题
  4. JavaScript 语言具有动态性。JavaScript 是事件驱动的,只根据用户的操作做出相应的反应处理。

TypeScriptt的特点

  1. TypeScript 是 Microsoft 推出的开源语言,使用 Apache 授权协议
  2. TypeScript 增加了静态类型、类、模块、接口和类型注解
  3. TypeScript 可用于开发大型的应用
  4. TypeScript 易学易于理解

TypeScript 可以使用 JavaScript 中的所有代码和编码概念,TypeScript 是为了使 JavaScript 的开发变得更加容易而创建的。例如,TypeScript 使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序。TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。可根据实际情况决定需要不要引用TypeScript ,Angular2是基于typescript来开发的JS框架。

JavaScript 的组成

我们开发中使用的js语言由三部分构成,而ECMAScript只是其中一部分的规范,其他两部分是专门针对浏览器端而使用的,如果仅仅是用于后端开发则只需要ECMAScript就够了

  1. ECMAScript (核心):描述了该语言的语法和基本对象,如类型、运算、流程控制、面向对象、异常等。
  2. DOM (文档对象模型):描述处理网页HTML内容的方法和接口。
  3. BOM (浏览器对象模型):描述与浏览器进行交互的方法和接口。

其中Node.Js就只有ES,目前浏览器比较流行的版本就是ES6(ES2015),老浏览器的版本基本上都是ES5。所以alertdocument不能在Node运行是因为Node没有DomBom

ES6的新特性

ES6的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度很大,所以ES6中的特性比较多。在这里列举几个常用的:

类(class)

对熟悉Java,object-c,c#等纯面向对象语言的开发者来说,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。

class Animal {
    // 构造函数,实例化的时候将会被调用,如果不指定,那么会有一个不带参数的默认构造函数.
    constructor(name,color) {
      this.name = name;
      this.color = color;
    }
    // toString 是原型对象上的属性
    toString() {
      console.log('name:' + this.name + ',color:' + this.color);
    }
  }

 var animal = new Animal('dog','white');//实例化Animal
 animal.toString();

 console.log(animal.hasOwnProperty('name')); //true
 console.log(animal.hasOwnProperty('toString')); // false
 console.log(animal.__proto__.hasOwnProperty('toString')); // true

 class Cat extends Animal {
  constructor(action) {
    // 子类必须要在constructor中指定super 函数,否则在新建实例的时候会报错.
    // 如果没有置顶consructor,默认带super函数的constructor将会被添加、
    super('cat','white');
    this.action = action;
  }
  toString() {
    console.log(super.toString());
  }
 }

 var cat = new Cat('catch')
 cat.toString();

 // 实例cat 是 Cat 和 Animal 的实例,和Es5完全一致。
 console.log(cat instanceof Cat); // true
 console.log(cat instanceof Animal); // true

let与const

ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部),在之前JS是没有块级作用域的,constlet填补了这方便的空白,const与let都是块级作用域。

// 使用var定义的变量为函数级作用域
{
  var a = 10;
}

console.log(a); // 输出10



// 使用let与const定义的变量为块级作用域
{
  let a = 10;
}

console.log(a); //-1 or Error"ReferenceError: a is not defined"

模板字符串

在ES6之前,我们往往这么处理模板字符串:通过”"和”+”来构建模板

$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");

而对ES6来说

  1. 基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
  2. ES6反引号(``)直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page, 
including student's ${name}, ${seatNumber}, ${sex} and so on.`);

箭头函数(Arrow Functions)

这是ES6中最令人激动的特性之一。箭头函数就是函数的一种简写形式,但=>不只是关键字function的简写,它还带来了其它好处。箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题。有经验的JavaScript开发者都熟悉诸如var self = this;或var that = this这种引用外围this的模式。但借助=>,就不需要这种模式了。

// ES5
var add = function (a, b) {
    return a + b;
};
// ES6使用箭头函数
var add = (a, b) => a + b;

// ES5
[1,2,3].map((function(x){
    return x + 1;
}).bind(this));

// ES6使用箭头函数
[1,2,3].map(x => x + 1);

模块化(Module)

ES5不支持原生的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。

let myName="laowang";
let myAge=20;
let myfn=function(){
    return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
    myName,
    myAge,
    myfn
}
/******************************接收的代码调整为**********************/
import {myfn,myAge,myName} from "./test.js";
console.log(myfn());//我是laowang!今年20岁了
console.log(myAge);//20
console.log(myName);//laowang

如果你不想暴露模块当中的变量名字,可以通过as来进行操作

let myName="laowang";
let myAge=20;
let myfn=function(){
    return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
    myName as name,
    myAge as age,
    myfn as fn
}
/******************************接收的代码调整为**********************/
import {fn,age,name} from "./test.js";
console.log(fn());//我是laowang!今年20岁了
console.log(age);//20
console.log(name);//laowang

解构赋值

解构赋值,解构赋值语法是JavaScript的一种表达式,可以方便的从数组或者对象中快速提取值赋给定义的变量。就是怎么快速地从对象和数组中获取到你想要的数据,先来看对象的解构赋值。

1. 获取数组中的值

var foo = ["one", "two", "three", "four"];

var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

//如果你要忽略某些值,你可以按照下面的写法获取你想要的值
var [first, , , last] = foo;
console.log(first); // "one"
console.log(last); // "four"

//你也可以这样写
var a, b; //先声明变量

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

2.获取对象中的值

const student = {
  name:'Ming',
  age:'18',
  city:'Shanghai'  
};

const {name,age,city} = student;
console.log(name); // "Ming"
console.log(age); // "18"
console.log(city); // "Shanghai"

延展操作符(Spread operator)

延展操作符...可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造对象时, 将对象表达式按key-value的方式展开。

1.改变函数的调用

在es6以前将数组作为函数的参数传递进去的时候,通常使用的方法是function.prototype.apply,例如:

var arr=['jack','rose'];
var str=['niulang','zhinv'];
Array.prototype.push.apply(arr,str);
console.log(arr);//jack,rose,niulang,zhinv

这样写很麻烦,有了延展操作符,它可以将数组(可遍历对象)进行展开然后作为函数的参数进行调用。

var arr=['jack','rose'];
var str=['niulang','zhinv'];
arr.push(...str);
console.log(arr);//jack,rose,niulang,zhinv

2 .数组构造

在延展运算符之前,我们如果想让一个数组成为另一个数组的一部分,需要用到之前学到的cancat,push等等。现在延展操作符允许我们简单地操作就能实现。

var arr=['jack','rose'];
var str=['niulang',...arr,'zhinv'];
console.dir(str)//['niulang','jack','rose','zhinv']

3.数组解构

var foo = ["one", "two", "three", "four"];

var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

//如果你要忽略某些值,你可以按照下面的写法获取你想要的值
var [first, , , last] = foo;
console.log(first); // "one"
console.log(last); // "four"

//你也可以这样写
var a, b; //先声明变量

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

4.对象解构

const student = {
  name:'Ming',
  age:'18',
  city:'Shanghai'  
};

const {name,age,city} = student;
console.log(name); // "Ming"
console.log(age); // "18"
console.log(city); // "Shanghai"

Promise

Promise异步编程的一种解决方案,比传统的解决方案callback更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

1.不使用ES6

// 嵌套两个setTimeout回调函数
setTimeout(function()
{
    console.log('Hello'); // 1秒后输出"Hello"
    setTimeout(function()
    {
        console.log('Hi'); // 2秒后输出"Hi"
    }, 1000);
}, 1000);

2.使用ES6

var waitSecond = new Promise(function(resolve, reject)
{
    setTimeout(resolve, 1000);
});

waitSecond
    .then(function()
    {
      console.log("Hello"); // 1秒后输出"Hello"
      return waitSecond;
    })
    .then(function()
    {
        console.log("Hi"); // 2秒后输出"Hi"
    });

Author: 顺坚
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 顺坚 !
评论
 Previous
浏览器的V8引擎 浏览器的V8引擎
V8引擎是前端开发工程师进阶的必经之路,它是JS的解释器。对于CPU硬件是无法直接运行高级语言的,需要把高级语言翻译成机器码,而V8引擎是专门作为JavaScript而创造的解释器。在Java中JVM也有类似的作用,但与JVM不同的是。V8
2020-03-01
Next 
分布式算法之paxos算法 分布式算法之paxos算法
Paxos是用于一种分布式系统并且具有容错性的一致性算法,是目前业界公认能解决分布式系统一致性的问题算法之一。它晦涩难懂的程度完全可以跟它的重要程度相匹敌。Paxos于1990年由Lamport提出,但直到1998才正式被计算机科学界接受
2020-02-27
  TOC