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 传送门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容