为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
C C++

前言

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 errorconst 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 errorfor (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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消