追本溯源,ES6 和 ES5 的对比实现

词语连线

让咱们先来玩一个游戏,根据 ES5,ES2015, ES2016,ES6,ES2017 这几个词的关系,进行连线。javascript

若是你有点“蒙圈”,不妨先来看看 Javascript 的发展历史。前端

  • 2011 年,标准化组织 ECMA 发布 ECMAScript 5.1 版
  • 发布后,开始着手制定 ECMAScript 6.0 版本,即 ES6
  • 为了让标准的升级成为常规流程,决定在每一年的 6 月份正式发布一次,做为当年的正式版本:
    • 2015 年 6 月发布 《ECMAScript 2015 标准》(简称 ES2015),它是 ES6 的第一个版本:
    • 2016 年 6 月发布 《ECMAScript 2016 标准》(简称 ES2016),进行了小幅修订
    • 2017 年 6 月发布 ES2017 标准

那么上面游戏的答案是: java

关系

ES6 与 ES5

做为一个在 JS 高程《JavaScript 高级程序设计》 指引下走进前端的“老”同窗,我经常被 ES5 和 ES6 的写法“困扰”,担忧写出来的代码不够“新”。时间与经验的沉淀促进语言的“进化”,那么观察一个特性/语法的变迁,或许是一件有意思的事情。浏览器

今天咱们就来看看,ES6 中一些语法若是用 ES5 该如何实现。bash

let

ES5 只有全局做用域函数做用域,ES6 经过 let 语法增长了块级做用域,加强了一些场景下的合理性。服务器

ES6函数

for (let i = 0; i < 3; i++) {
    // 
}
console.log(i); // ReferenceError: i is not defined
复制代码

ES5学习

(function() {
    for (var i = 0; i < 3; i++) {
    // 
    }
})();
console.log(i); // ReferenceError: i is not defined
复制代码

const

ES5 中是没有常量的,一般用大写的命名规范来“约定”常量,却不能保证不被更改。ES6 中新增了 const。ui

ES6this

const a = '123';

try {
  a = '456';
} catch(err) {
  // err: TypeError: Assignment to constant variable
  console.log(a); // '123'
}
复制代码

ES5

Object.defineProperties(window, {
  a: {
    value: '123',
    writable: false
  }
});

a = '456';
console.log(a); // '123'
复制代码

Class

与其余新增的语法特性不一样,ES6 中的 Class 实质上是 JavaScript 现有的基于原型的继承的语法糖。

声明一个动物类

ES6

class Animal {
  constructor(type, name) {
    this.type = type;
    this.name = name;
  }
  static eat() {
  }
  speak() {
    console.log(this.name + ' say:');
  }
}
复制代码

ES5

function Animal(type, name) {
    this.type = type;
    this.name = name;
}
Animal.eat = function () {
}
Animal.prototype.speak = function () {
    console.log(this.name + ' say');
}
复制代码

实例化一个小明同窗

var a = new Animal('people', 'xiaoming');
a.speak();
复制代码

声明猫猫类(继承动物类)

ES6

class Cat extends Animal {
    constructor(name) {
        super('cat', name);
    }
   
    speak() {
        super.speak();
        console.log('meow');
    }
}
复制代码

ES5

function Cat(name) {
    Animal.call(this, 'cat', name);
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

// 静态方法
Cat.eat = Animal.eat;

Cat.prototype.speak = function () {
  Animal.prototype.speak.call(this);
  console.log('meow');
};

复制代码

实例化一只名为“卷卷”的喵,而后喵一声

var juanjuan = new Cat('juanjuan');
juanjuan.speak(); // juanjuan say: meow
复制代码

对比一下,ES6 更优美吧~

Module

ES6 import/export

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;

// 引用
import { pi, sum } from 'lib/math';
console.log('2π = ' + math.sum(math.pi, math.pi));
复制代码

ES5 require/exports

// lib/math.js
exports.sum = sum;
function sum(x, y) {
  return x + y;
}
var pi = exports.pi = 3.141593;

// 引用
var math = require('lib/math');
console.log('2π = ' + math.sum(math.pi, math.pi));
复制代码

可是,上面这两种写法并不“等价”。这要从 javascript 的模块体系提及。

javascript 一直没有模块体系,因而社区制定了一些模块加载方案,比较通用的是 CommonJS 和 AMD 两种, 前者用于服务器,后者用于浏览器。(为了兼容还引入了UMD)。

CommonJS 和 AMD 模块都是在运行时肯定模块关系。即总体加载模块(模块下的全部方法),生成一个对象,而后在从这个对象上读取方法。这种加载称为“运行时加载”

// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
复制代码

然而 ES6 模块不是对象。

// ES6模块
import { stat, exists, readFile } from 'fs';
复制代码

上面代码的实质是从 fs 模块加载 3 个方法,其余方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 能够在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。固然,这也致使了无法引用 ES6 模块自己,由于它不是对象。

关于模块这部分,其实值得展开研究。(我还没整太明白)

结尾

语法的背后,是语言自己的“特性”,他们的出现是为了解决一些场景的问题。追本溯源是一个很好的学习方式。咱们,下期再见~

相关文章
相关标签/搜索