我们可以使用babel官网提供的在线编译工具,将ES6编译为对应的ES5代码,观察两者直接的不同有助于我们更加了解ES6的知识。

http://babeljs.io/repl/

新的变量声明方式 let/const

在ES5中,我们使用var来声明一个变量。新的变量声明方式带来了一些不一样的特性。其中最重要的就是具备了块级作用域并且不再有变量提升。

通过两个简单的例子来说明。

{
    let a = 20;
}

console.log(a);  // a is not defined

以上代码会被编译为:

{
    let _a = 20;
}

console.log(a);  // a is not defined
// demo02
// ES5
console.log(a);   // undefined
var a = 20;

// ES6
console.log(a); // a is not defined
let a = 20;

通过demo02的例子我们可以看出,变量提升好像确实不存在了。那么这是什么原因导致的呢?

我们知道通过var声明的变量,会分别经历两个步骤,首先给变量赋值一个undefined,并将整个操作提升到了作用域的前面。因此会有:

var a = 20;

// 等同于
var a = undefined;  // 该操作提升
a = 20;

但是当我们通过let/const声明变量时,其实提升的操作仍然存在,但是将不会给这个变量赋值为undefined。也就是说,虽然声明提前了,但是该变量并没有任何引用。所以当我们进行如下操作时,会报referenceError。即引用错误。

console.log(a);   // ReferenceError: a is not defined
let a = 20;

正是因为不会默认赋值为undefined,加上let/const会存在自己的作用域,因此会出现一个叫做暂时性死区的现象。例如下面的例子。

var a = 20;
if (true) {
    console.log(a);  // ReferenceError: a is not defined
    let a = 30;
}

此处虽然a已经声明过了,但是由于暂时性死区的存在,仍然无法正常访问。因此我们在自己的代码中,仍然要注意到这些异常,尽量将声明主动放置于代码的前面。

使用ES6,我们需要全面使用let/const来替换掉之前非常常用的var。那么现在的问题是,什么时候用let,什么时候用const呢?

我们常常使用let来声明一个引用可以被改变的变量,而使用const来声明一个引用不能被改变的变量。

例如我们会使用let来声明一个值总是会变的变量。

let a = 20;
a = 30;
a = 40;
console.log(a);

这里需要注意的是,a值的改变,其实是引用改变了。

我们会使用const来声明一个常量。

const PI = 3.1415;
const MAX_LENGTH = 100;

// 试图改变引用
PI = 3; // Untaught TypeError: Assignment to constant variable

除此之外,当我们声明一个引用类型的数据时,也会使用const。尽管我们可能会改变该数据的值,但是必须保持它的引用不变。

const a = [];
a.push(1);
console.log(a); // [1]
const b = {}
b.max = 20;
b.min = 0;
console.log(b); // { max: 20, min: 0 }

下面大家可以领会一个这个例子。想想能不能这样用?想完之后,在浏览器中运行试试看吧。

const arr = [1, 2, 3, 4];
arr.forEach(function(item) {
    const temp = item + 1;
    console.log(temp);
})

箭头函数(arrow function)

与function相比,箭头函数是一个用起来更加舒服的语法。我们一起来看看箭头函数的基本使用。

// es5
var fn = function(a, b) {
    return a + b;
}

// es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号
const fn = (a, b) => a + b;

// es5
var foo = function() {
    var a = 20var b = 30;
    return a + b;
}

// es6
const foo = () => {
   const a = 20;
   const b = 30;
   return a + b;
}

需要注意的是,箭头函数只能替换函数表达式,也就是使用var/let/const声明的函数。而直接使用function声明的函数是不能使用箭头函数替换掉的。

除了写法的不同,箭头函数还有一个非常重要的特性需要我们掌握。

学习过之前的this专题的话我想大家应该能够知道,函数内部的this指向,与它的调用者有关。或者使用call/apply/bind也可以修改内部的this指向。

通过下面的例子简单复习一下。

var name = 'TOM';
var getName = function() {
    console.log(this.name);
}

var person = {
    name: 'Alex',
    getName: getName
}

var other = {
    name: 'Jone'
}

getName();  // 独立调用,this指向undefined,并自动转向window
person.getName(); // 被person调用,this指向person
getName.call(other); // call 修改this,指向other

明白了this的指向,那么就能够很简单的知道这几个不同的方法调用时会输出什么结果。但是当我们将最初声明的getName方法修改为箭头函数的形式,输出结果会发生什么变化呢?我们来看一下:

var name = 'TOM';

// 更改为箭头函数的写法
var getName = () => {
    console.log(this.name);
}

var person = {
    name: 'Alex',
    getName: getName
}

var other = {
    name: 'Jone'
}

getName();
person.getName();
getName.call(other);

通过运行我们发现,三次调用都输出了TOM。
这也正是我要跟大家分享的箭头函数的不同。箭头函数中的this,就是声明函数时所处上下文中的this,并且不会被其他方式所改变。

因此这个例子中,getName在全局上下文中声明,那么this就会指向window对象。所以输出的结果全是TOM。

在实践中,我们常常会遇到this在传递过程中的改变给我们带来的困扰。例如:

var Mot = function(name) {
    this.name = name;
}
Mot.prototype = {
    constructor: Mot,
    do: function() {
        console.log(this.name);
        document.onclick = function() {
            console.log(this.name);
        }
    }
}

new Mot('Alex').do();

这个例子中当我们调用do方法时,我们期望点击document时,仍然也会输出‘Alex’。但是很遗憾,在onclick的回调函数中,this的指向其实已经发生了变化,它指向了document,因此此时我们肯定就得不到我们想要的结果。通常的解决方案我相信大家应该知道,这里也可以使用箭头函数来避免这样的困扰。

var Mot = function(name) {
    this.name = name;
}
Mot.prototype = {
    constructor: Mot,
    do: function() {
        console.log(this.name);
        // 修改为箭头函数即可
        document.onclick = () => {
            console.log(this.name);
        }
    }
}

new Mot('Alex').do();

除此之外,arguments还有一个需要大家注意的不同。在箭头函数中,没有arguments对象。

var add = function(a, b) {
    console.log(arguments);
    return a + b;
}

add(1, 2);

// 结果输出一个类数组对象
/*
[
    0: 1,
    1: 2,
    length: 2,
    callee: ƒ (a, b),
    Symbol(Symbol.iterator): ƒ values()
]
*/
var add = (a, b) => {
    console.log(arguments);
    return a + b;
}
add(1, 2);   // arguments is not defined

results matching ""

    No results matching ""