前言
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