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