ES6学习心得

ECMAScript 6.0(简称ES6)是 JavaScript 一个制定于2015年的新标准,它于2015年6月正式发布。

它的目的是想让JavaScript成为一个企业级的开发语言,可以去开发大型的复杂应用。

我的ES6学习心得将分为以下三个部分来阐述:

1.ECMAScript 6.0简介

2.ECMAScript 6.0新特性

3.ECMAScript 6.0性能总结与个人体会

4.参考文献

 

1.ECMAScript 6.0简介

想要弄清楚ECMAScript 6.0,那么我们要先弄清楚它是什么,它怎么来的。

(1)ECMAScript

ECMA是“European Computer Manufactures Association”的缩写,中文称欧洲计算机制造联合会。这个组织的目标是评估,开发和认可电信和计算机标准。

1996年的时候,Javascript的创造公司将Javascript提交给ECMA,希望这种语言能够成为国际标准。

次年,ECMA 发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言Javascript的标准,并将这种语言称为 ECMAScript,这个版本就是1.0版。

所以我们这里总结一下ECMAscript与Javascript之间的关系:

ECMAscript是Javascript的标准/规格

Javascript是ECMAscript的实现(另外的 ECMAScript 语言还有 Jscript 和 ActionScript)

但是我们一般日常说的ECMAScript就是Javascript的意思,这两个词是可以互换的,不过还是要清楚它们的区别。

  

(2)ECMAScript 6.0(ES6) / ECMAScript 2015(ES2015)

2011年,ECMAScript 5.1版发布后,就开始制定6.0版了。因此,ES6 这个词的原意,就是指 ECMAScript 5.1版的下一个版本。

因为ECMA标准委员会会在每年6月份更新并正式发布一次新的ECMAScript标准,作为当年的正式版本。

然后接下来的时间就是在这个版本的基础上面做改动,一直到下年的6月份。这样一来其实就不需要用版本号来区分了了,用年份标记即可。

ES6 的第一个版本,就这样在2015年6月发布了,正式名称就是《ECMAScript 2015标准》(简称 ES2015)。

2016年6月,小幅修订的《ECMAScript 2016标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准。根据计划,2017年6月发布 ES2017 标准。

因此,ECMAScript6.0既是一个版本名称,也是一个类别。版本名称是指5.1版以后的 ECMAScript 的下一代标准,类别是指它涵盖了ES2015、ES2016、ES2017等等。

而ES2015 则是正式名称,特指该年发布的正式版本的语言标准。

 

2.ECMAScript 6.0新特性

之前刚开始接触Javascript的时候ES6还没推出,用的还是ES5的标准,那么新推出的ES6有哪些新特性呢?

下文我将总结一些在ES6的学习中接触到的一些比较常用的语法特性,具体有以下几项:

(1)let, const

(2)class, extends, super

(3)arrow function

(4)template string

(5)destructuring

(6)default, reat

 

--------------------华丽丽的分界线---------------------

 

(1)let, const

let, const的作用与var类似,都是用于声明变量的,但是它们减少了var在作用域以及赋值方面的缺陷,下面我们来看一下。

①var在作用域方面的缺陷

var name = 'zach'

while (true) {
    var name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //obama

使用var两次输出都是obama,这是因为ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。第一种场景就是你现在看到的内层变量覆盖外层变量。而let则实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。

let name = 'zach'

while (true) {
    let name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //zach

另外一个var带来的不合理场景就是用来计数的循环变量泄露为全局变量,看下面的例子:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代码中,变量i是var声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的function在运行时,会通过闭包读到这同一个变量i,导致最后输出的是最后一轮的i的值,也就是10。而使用let则不会出现这个问题。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

 上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

另外,for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

上面代码输出了3次abc,这表明函数内部的变量i和外部的变量i是分离的。

 

 

②var在赋值方面的缺陷

从上面的例子我们可以看出,var的赋值是可以很轻易地被改变的

var name = 'zach'

while (true) {
    var name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //obama

这就带来了一个问题,一些程序中比较重要的变量很容易就会被他人,特别是你毫不知情的队友改变了,这就给我们大型程序的开发带来了麻烦。

当然上文仅仅是我设想的一个场景,我们的程序中更多的时候是需要一些约定俗成,值不变的常量,比如说pi。

在ES6中,我们添加了const特性来声明常量。一旦声明,常量的值就不能改变。

const PI = Math.PI

PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

当我们尝试去改变用const声明的常量时,浏览器就会报错。
const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:

const monent = require('moment')

 

(2)class, extends, super

 ES6还有一大特点就是它跟我们所熟知的入门级语言C、C++更像了,因为它引入了class(类)这个概念。

在ES5的年代,js是没有特定的class这个概念的,所以我们之前一般通过构造函数定义并生成新对象,如下图:

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(类)这个概念。

在ES6中,通过class关键字,我们可以定义类。

但其实ES6的class仅仅是个名字/写法而已,因为它的绝大部分功能,ES5都可以做到。

不过新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,这样就可以更接近企业级的开发语言。

上面的代码用ES6的“类”改写,就是下面这样。

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。

Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

ES6的类,完全可以看作构造函数的另一种写法。

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

 

Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。下面定义了一个Cat类,该类通过extends关键字,继承了Animal类的所有属性和方法。

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        console.log(this.type + ' says ' + say)
    }
}

let animal = new Animal()
animal.says('hello') //animal says hello

class Cat extends Animal {
    constructor(){
        super()
        this.type = 'cat'
    }
}

let cat = new Cat()
cat.says('hello') //cat says hello

super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

 

 

(3)arrow function

 ES6带来的还有一个新特性,就是简洁。

arrow function应该是ES6最最常用的一个新特性了,用它来写function比原来的写法要简洁清晰很多:

function(i){ return i + 1; } //ES5
(i) => i + 1 //ES6

如果function有多行内容,则需要用{}将其括起来:

function(x, y) { 
    x++;
    y--;
    return x + y;
}//ES5
(x, y) => {x++; y--; return x+y}//ES6

 

另外ES5在使用this的时候也需要小心混淆:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout(function(){
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}

 var animal = new Animal()
 animal.says('hi')  //undefined says hi

运行上面的代码会报错,这是因为setTimeout中的this指向的是全局对象。

一般用ES5的解决方法有传递this或者bind(this)这些, 但现在我们有了箭头函数,就不需要这么麻烦了:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout( () => {
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}
 var animal = new Animal()
 animal.says('hi')  //animal says hi

当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。

 

 

但是在这里我们要说的是,arrow function也并不是一劳永逸的,特别是在你并不知道this指向哪的时候,你需要慎用arrow function。

因为arrow function是 Lexical scoping(词法作用域),this指向定义Arrow Function时外围, 而不是运行时的对象。

词法作用域: 变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。

下面我们将根据文章:Do ES6 Arrow Functions Really Solve “this” In JavaScript? 来分析。

如下代码:

// run this in node v4 to see the "expected" behavior

this.test = "attached to the module";

var foo = {
  test: "attached to an object"
};

// a method to create methods
foo.method = function(name, cb){
  this[name] = cb;
};

// use an arrow function and get
// lexical analysis of "this"
foo.method("bar", () => {
  // not what you expected, maybe?
  console.log(this.test); 
});

foo.bar();

输出为:“attached to the module”而不是“attached to an object”

因为

就像我们所说的,arrow function是 Lexical scoping,因为在foo.method("bar", () => {的外围并没有包含着它的代码,所以,foo.bar()中的this.test值将永远是“attached to the module”。

 

 

(4)template string

 这个新特性可以帮助我们插入大段的html内容到文档中去。传统的写法一般比较麻烦,所以之前我们通常会引用一些模板工具库,比如mustache等等。代码如下:

$("#result").append(
  "There are <b>" + basket.count + "</b> " +
  "items in your basket, " +
  "<em>" + basket.onSale +
  "</em> are on sale!"
);

我们要用一堆的'+'号来连接文本与变量,而使用ES6的新特性模板字符串``后,我们可以直接这么来写:

$("#result").append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

在ES6中,我们用`来标识开始和结尾,用${}来引用变量,而且所有的空格和缩进都会被保留在输出之中。

 

 

(5)destructuring

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

当使用ES5的时候,我们一般是这么写的:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

用ES6完全可以像下面这么写:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat, dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

反过来可以这么写:

let dog = {type: 'animal', many: 2}
let { type, many} = dog
console.log(type, many)   //animal 2

 

 

(6)default, reat

 default很简单,意思就是默认值。大家可以看下面的例子,调用animal()方法时忘了传参数,传统的做法就是加上这一句type = type || 'cat'来指定默认值。

function animal(type){
    type = type || 'cat'  
    console.log(type)
}
animal()

如果用ES6我们可以直接这么写:

function animal(type = 'cat'){
    console.log(type)
}
animal()

 

最后一个rest语法也很简单,直接看例子:

function animals(...types){
    console.log(types)
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]

而如果不用ES6的话,我们则得使用ES5的arguments

 

 

3.ECMAScript 6.0性能总结与个人体会

总体来看,ES6正在朝着更简洁、更友好、可读性更强、更模块化的方向发展,这些特性都能支持其成为一个企业级的开发语言,可以去开发大型的复杂应用。

本文章从ES6的来历,讲到ES6的一些新特性。但以上仅仅列出了ES6中一些比较常用的新特性,而且分析的并不够深入,所以我们还是需要再去深一步学习和摸索的。“实践是检验真理的唯一方法”,以后在实战项目中遇到的问题我会再来写博客分析的。

 

 

4.参考文献

http://www.jianshu.com/p/ebfeb687eb70

http://es6.ruanyifeng.com/#docs/function

http://cnodejs.org/topic/57266e375a26c4a841ecbf40

https://derickbailey.com/2015/09/28/do-es6-arrow-functions-really-solve-this-in-javascript/

http://www.cnblogs.com/jiangyi666/p/5998914.html

http://blog.csdn.net/pengju_guo/article/details/7292080

posted @ 2017-05-31 16:15  丝瓜儿  阅读(1541)  评论(0编辑  收藏  举报