ES6改良ES5中的5大“缺陷”

前言

ECMAScript 6 (ES6) 新特性可以分为:

新增语法(例如:class)

增强 JavaScript 功能(例如:import)

以及改良 JS “缺陷” (例如:let 关键字)。

大部分博客将这三类新特性混在一起介绍,往往让 ES6 新手一脸懵逼。因此我决定写下这篇文章仅仅介绍改良 JS “缺陷”的这部分特性。

好,让我们开始吧。

1. 块级作用域

ES5 只有函数作用域(例如,我们必须将代码包在函数内来限制作用域),这导致很多问题。ES6 提供 let 和 const 来代替 var 声明变量,新的声明方式支持用大括号表示的块级作用域。

那么问题来了,为什么要实现块级作用域呢?

a.防止变量在作用域外被访问

{
let a=10; 
var b=1;
}
a// ReferenceError: a is not defined.
b// 1

b.防止重复声明变量

//请仔细感受下,在之前命名中,冲突了,完全不会报错,而是被覆盖了。
var i = 0;
var i = 1;
var add = function (a, b) {
 return a + b;
};
var add = function (a, b) {
 return a + b;
};
//ES6 会报冲突错误
"use strict"
let i = 0;
let i = 1;//Duplicate declaration error
const add = function (a, b) {
 return a + b;
};
const add = function (a, b) {//Duplicate declaration error
 return a + b;
};

ES6 不允许在同一个作用域内用 let 或 const 重复声明同名变量。这对于防止在不同的 js 库中存在重复声明的函数表达式十分有帮助。

c.不再需要立即执行的函数表达式(IIFE)
//**

  • 请仔细关注以下区别哦
  • IIFE是缩写,全拼Imdiately Invoked Function Expression,立即执行的函数表达式。
    */
// IIFE写法
(function () {
  var tmp = "hello world";
  ...
}());
console.log(tmp);//Reference Error

// 块级作用域写法
{
   var tmp = "hello world";
  ...
}
console.log(tmp);//Reference Error

d.循环体中的闭包不再有问题

// ES5 写法
"use strict";
var arr = [];
for (var i = 0; i < 3; i++){
    arr.push(function(){
        return i; //refers global i;
    });
}
console.log(i);//3.因为是全部变量,所以返回值是3,而且下面的循环所打印出的值,你也会觉得很奇怪。为什么不是0,1,2呢?当然是因为i是全局变量了。~。~
for (var j = 0; j < 3; j++){
    console.log(arr[j]()); //prints 3, 3 and 3
}

>//为了规避这样的问题,达到我们预期的效果,ES6帮我们做到了。
"use strict";
var arr = [];
for (let i = 0; i < 3; i++){
    arr.push(function(){
        return i; //refers local i;
    });
}
console.log(i);//Reference error
for (var j = 0; j < 3; j++){
    console.log(arr[j]()); //prints 0, 1 and 2
}

2.词法作用域的 “this” (通过箭头函数)

在 ES5 中,“this” 会随着函数调用位置和调用方式改变,这种变化无常给很多开发者带来过痛苦的经历。 ES6 通过箭头函数带来的词法作用域的 “this” 消除了这个问题。
词法作用域的 “this” 特性让变量的 “this” 总是指向词法声明时的那个对象。

//我们希望打印出hello world 
//“this” 问题和 ES5 中的两个解决方法:
function add(id,callback){
    callback();
}
var helloworld = {
    id: '1',
    fName:'hello',
    lName:'world',
    print: function() {
     console.info(this.id);//1
     //调用外部函数
     add(this.id,function(){//因为此时的this并没有指向helloworld对象
            console.info(this.fName + this.lName); //NaN;
        });
    }
}
helloworld.print();

>//解决方法1:.bind(this)
function add(id,callback){
    callback();
}
var helloworld = {
    id: '1',
    fName:'hello',
    lName:'world',
    print: function() {
     console.info(this.id);//1
     //调用外部函数
     add(this.id,function(){//因为此时的this并没有指向helloworld对象
            console.info(this.fName + this.lName); //helloworld;
        }.bind(this));
    }
}
helloworld.print();

>//解决方法2:var self = this;
function add(id,callback){
    callback();
}
var helloworld = {
    id: '1',
    fName:'hello',
    lName:'world',
    print: function() {
     console.info(this.id);//1
     var self = this;
     //调用外部函数
     add(this.id,function(){//因为此时的this并没有指向helloworld对象
            console.info(self.fName + self.lName); //helloworld;
        });
    }
}
helloworld.print();

>//解决方法3,引用ES6的箭头函数,此时也就是我们的 *<strong>重点</strong>*
function add(id,callback){
    callback();
}
var helloworld = {
    id: '1',
    fName:'hello',
    lName:'world',
    print: function() {
     console.info(this.id);//1
     var self = this;
     //调用外部函数
     add(this.id,() => {//在 ES6 中简单使用箭头函数就可以自动获得词法作用域的 “this”
            console.info(self.fName + self.lName); //helloworld;
        });
    }
}
helloworld.print();

3.处理 “arguments”

在 ES5 中,“arguments” 表现得像一个数组(例如:我们可以通过length来遍历它),但是它只是一个伪数组。一切数组方法,比如 sort、slice 等等都用不了。必须经过处理才能使用。

在 ES6 中,我们可以使用一个新的特性叫做 rest 参数。它的形式为…参数名(比如:…args)。rest 参数是一个真正的数组,所以我们可以对它使用所有数组上可用的方法。

//从小到大排序
//ES5的排序
 function sortFun(){
    var args = Array.prototype.slice.call(arguments);
    return args.sort(function(a,b){
        return a - b;
    })
}
console.info(sortFun(10,5,3));//[2,5,10]

//现在看看ES6的威力
function sortFun(...args){
 return args.sort((a,b) => a - b );
}
console.info(sortFun(10,5,3));//[2,5,10]

4. 类的引入

从概念上讲,在 ES6 之前的 JS 中并没有和其他面向对象语言那样的“类”的概念。长时间里,人们把使用 new 关键字通过函数(也叫构造器)构造对象当做“类”来使用。

由于 JS 不支持原生的类,而只是通过原型来模拟,各种模拟类的方式相对于传统的面向对象方式来说非常混乱,尤其是处理当子类继承父类、子类要调用父类的方法等等需求时。

ES6 带来了新的语法,与其他各种编程语言类似的语法,使得面向对象变得非常简单。

直接对比代码吧:
  //ES5的继承实现
//parent class 父类
var Person = function (id,x,y){
    this.id = id;
    this.live(x,y)
}
//用原型链的方式继承
Person.prototype.live = function(x,y){
    return{
        this.x = x;
        this.y = y;
    }
}
//child class 子类
var Son = function(id,x,y,favors){//添加爱好
    Person.call(this,id,x,y);
    this.favors = favors;
}
Son.prototype = Object.create(Person.prototype);
Son.prototype.constructor = Person;
Son.daPlane = function() {
    return new Son("a",0,0,100);
}
Son.prototype.live = function(x,y) {
    return Person.prototype.live.call(this);
}
var defaultSon = Son.daPlane(); //调用静态方法
var myson = new Son('a',0,0,"打飞机");//新建实例
console.info(myson.live()); //{x:0,y:0}


//ES6的继承实现 直接面向对象
//parent class 父类
class Person ={
    constructor(id,x,y){ //constructor 
        this.id = id
        this.live(x,y)
    }
    live(x,y){ // prototype function
        this.x = x;
        this.y = y;
    }
}

//child class 子类
class Son extends Person{
   constructor(id,x,y,favors){ //constructor 
        super(id,x,y) // 通过super方式继承
        this.favors = favors
    }
  static daPlane() {
        return new Son("a",0,0,100);
    }
   live(x,y){
        return super.live(x,y) // 通过super方式继承
    }   
}
var defaultSon = Son.daPlane(); //调用静态方法
var myson = new Son('a',0,0,"打飞机");//新建实例
console.info(myson.live()); //{x:0,y:0}

5. 强制性的严格模式

严格模式(use strict) 有助于防止问题用法,并且它也有助于安全使用 JavaScript。在 ES5 中, 严格模式是可选项,但是在 ES6 中,许多特性要求必须使用严格模式。 因此大多数开发者和 babel 之类的工具默认添加 use strict 到 JS 文件的头部,以确保整个 JS 文件的代码都采用严格模式,这个习惯有助于我们书写更好的 JavaScript。

友情提示:
Babel – 一个转换 ES6 代码为 ES5 代码的工具
我们的 ES6 代码最终要运行在浏览器里。Babel 是最流行的将 ES6 转为 ES5 的工具。它拥有许多使用方式,例如命令行、node 模块以及在线编译。
Babel 传送门

    原文作者:心羽空间
    原文地址: https://www.jianshu.com/p/2573debff334
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞