一、var和let命令
作用域
ES5的作用域只有全局作用域和函数作用域,这会带来很多问题,比如常用的for
循环中var
声明的i
只作为计数来控制循环,但是循环结束后i
任然存在,因为i
的作用域是全局的。所以我们循环计数的标识往往会变成i j k ......
,这是我们不希望看到的现象。
for (var i = 0; i < 5; i++) { }
console.log(i); // 5
ES6中新增了let
命令,和var
类似都用来声明变量,但let
的作用域是ES6新增的块级作用域,仅在当前的代码块中有效。
for (let i = 0; i < 5; i++) { }
console.log(i); // error
let j = 1;
if (true) {
let j = 2;
console.log(j); // 2
}
console.log(j); // 1
变量提升
ES5还存在变量提升的问题,即“函数及变量的声明都将被提升到函数的最顶部”,在一些规范的JavaScript代码中会发现大量的变量都集中在js脚本文件的顶部进行声明,这是为了避免变量提升现象导致的隐患。
console.log(m); // 输出为 undefined ,并没有报错,这说明 m 是存在的
var m = 1;
// 其实上述代码因为变量提升而被执行为:
var m;
console.log(m);
m = 1;
为了避免变量提升带来的隐患,let
禁止在变量声明前使用。
console.log(m); // undefined
var m = 1;
console.log(n); // error
let n = 1;
二、const
const
同样是ES6新增的命令,与var let
声明变量不同,const
声明的是常量,const
声明的常量无法改变其值。
const PI = 3.1415;
PI = 1; // error
三、箭头函数
ES5中我们常见的匿名函数是这样的。
function() {
console.log(1);
}
箭头函数简化了这种写法,去掉了function
并在()
和{}
之间添加了=>
。
() => {
console.log(1);
}
如果函数是参数运算等情况的话那么可以进一步简化。
function (x) {
return x * x;
}
// 等同于
x => x * x;
除了简化ES5写法之外箭头函数与匿名函数最大的不同在于修正了
this
的错误指向,详见廖雪峰的官方网站。
四、数据结构Map
Map
是ES6新增的数据结构,类似于对象Object
,要正确和方法.map()
进行区分。
首先创建一个Object
实例来仔细观察一下Object
,其实是我们常说的键值对的集合,然而传统的Object
只接受字符串作为键名。
const element = document.getElementById('myDiv');
person=new Object();
person.firstname="Bill";
person.lastname="Gates";
person[element] = "abc";
console.log(person);
/**
* {
* [object HTMLDivElement]: "abc"
* firstname: "Bill"
* lastname: "Gates"
* }
**/
console.log(person['[object HTMLDivElement]']); // "abc"
可以看到字符串'[object HTMLDivElement]'
作为键成功的匹配到了对应的值abc
,然而我们存入的其实是一个DOM节点,怎么会被字符串匹配出来?这说明Object
的结构是字符串 - 值
,也就是将键作为字符串存储起来,而我们希望的结构是值 - 值
,数据结构Map
解决了这个问题。
const element = document.getElementById('myDiv');
person=new Map();
person.firstname="Bill";
person.lastname="Gates";
person.set(element, "abc");
console.log(person.get(element)); // "abc"
利用Map
的set() get()
方法进行添加和读取成员,除此之外还包含size() has() delete() clear()
操作方法和keys() values() entries() forEach()
遍历方法。
五、for…of循环
for...of
循环在数组中较为常用,上文提到的Map
结构中也可以使用for...of
循环。
const arr = [1, 2, 3];
for(let item of arr) {
console.log(item); // 1 2 3
}
六、Class(类)
Class
让定义类更加简单,直接看例子。
function Student(name) {
this.name = name;
}
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}
let x = new Student("LiHua");
x.hello(); // Hello,LiHua!
传统的ES5我们可以通过上述构造函数的方法生成实例对象,而ES6中可以通过Class
来定义类,使其更接近传统面向对象语言。
class Student {
constructor(name) {
this.name = name;
}
hello() {
alert('Hello, ' + this.name + '!');
}
}
比较一下会发现包含构造函数constructor
和函数hello
的类Student
避免了代码的分散,并且更接近传统面向对象语言,降低了学习难度。
Class类的继承
新建一个Class
类想要继承之前类的方法也变得更加简单,只需要通过extends
命令即可。
// 父类
class Student {
constructor(name) {
this.name = name;
}
hello() {
alert('Hello, ' + this.name + '!');
}
}
// 子类
class PrimaryStudent extends Student {
constructor(name, grade) {
super(name); // 用super调用父类的构造方法
this.grade = grade;
}
myGrade() {
alert('I am at grade ' + this.grade);
}
}
let x = new PrimaryStudent("LiHua", "Three");
x.hello();
x.myGrade();
七、export 与 import
export
与import
的引入为代码带来了模块化的可能,将庞大的代码拆成一块一块的组件,通过拼装组合进行使用来完成一些大型项目。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
// 通过 export 输出
import {firstName, lastName, year} from './profile.js';
// 通过 import 引入
更多拓展阅读 <阮一峰 ECMAScript 6 入门>