1、什么是ECMAScript6?和JavaScript什么关系?
1.1 什么是ECMAScript6?
首先说一下什么是ECMA(European Computer Manufacturers Association)欧洲计算机制造商协会。
如果说ECMA是一种组织,那么ECMAScript 就是这个组织推出的一个脚本(script)的标准。
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。
1.2 ECMAScript6和JavaScript的关系?
JavaScript的核心就是ECMAScript,包括ActionScript
但是JavaScript由ECMAScript、DOM和BOM三部分组成
2、常用特性有?
- let、const 及var的不同
- Destructuring 解构赋值
- arrow function 箭头函数
- template string 模板字符串
- class、extends、super
- default、rest
- 总结
2.1 let、const和var之间的异同
- let变量 有块级作用域,没有var的变量提升,不能重复声明覆盖
- var变量 有函数作用域,有变量提升,可以重复声明覆盖
- const常量 声明后不可再修改的,不可以重复声明覆盖
es6里面不建议使用var了,因为var定义的变量没有块级作用域,
还会出现变量提升的情况,这样经常会导致你意想不到的错误,而let就不会这样.
const是定义那些不可以被重新赋值的变量,let是定义普通的变量。
var的变量提升和let比较,也就说let修复了var存在的一些bug。
console.log(name1); // 可以输出到控制台
console.log(name2); // 不可以输出到控制台
var name1 = "Tom"; // var有变量提升
let name2 = "Jerry"; // let没有变量提升
var变量只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。第一种场景就是你现在看到的内层变量覆盖外层变量。而let则实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效
var name = "Tom";
console.log(name); // Tom
var name = "Tom2";
console.log(name); // Tom2
while (true) {
var name = "Jerry";
console.log(name); //Jerry
break;
}
console.log(name); //Jerry
let变量不可以重复声明覆盖
let name = "Tom";
let name = "Tom2";//此处报错
Uncaught SyntaxError: Identifier 'name' has already been declared
let变量是块级作用域
let name = "Tom";
while (true) {
//let是块级作用域 变量name只在此代码块内有效
let name = "Jerry";
console.log(name); //Jerry
break;
}
console.log(name); //Tom
另外一个var带来的不合理场景就是用来计数的循环变量泄露为全局变量
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
按钮的onclick事件场景
因为var不是块级作用域,所以此次声明的变量i,在整个作用域内有效,
针对这种情况,可以用闭包或let变量声明来处理按钮点击事件的问题。
var btns = document.querySelectorAll("button");
console.log(btns.length);
for(var i=0;i<btns.length;i++){
console.log(i);
btns[i].onclick = function(){
alert("点击了第"+i+"个按钮!");
}
}
//修改成let声明的方式
var btns = document.querySelectorAll("button");
console.log(btns.length);
for(let i=0;i<btns.length;i++){
console.log(i);
btns[i].onclick = function(){
alert("点击了第"+i+"个按钮!");
}
}
const也是用来声明常量,一旦声明,常量的值就不可变化
const PI = Math.PI
PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only
const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:
const moment = require('moment');
注:在代码中,建议首选const声明,如果某个变量值是需要修改的,可以选择let变量声明。有利于js中的模块化开发。
2.2 Destructuring 解构赋值
解构赋值:就是从对象或数组中提取值,对变量进行赋值,被称为解构(Destructuring)
2.2.1 从对象中提取值
let cat = "Tom";
let mouse = "Jerry";
let zoo = { cat: cat, mouse: mouse}
console.log(zoo);
console.log(zoo.cat + " and " + zoo.mouse);
//同样也可以写成下面的格式
let cat = "Tom";
let mouse = "Jerry";
let zoo = { cat, mouse}
console.log(zoo);
console.log(zoo.cat + " and " + zoo.mouse);
//还有这种格式
let cat2 = { type: 'animal', many: 2}
let { type, many } = cat2;
console.log(type,many); // animal 2
2.2.2 从数组中提取值
//完全解构
let [a,b,c] = [1,2,3];
console.log(a+","+b+","+c); // 1,2,3
let [foo,[[bar],baz]] = [1,[[2],3]];
console.log(foo+","+bar+","+baz); // 1,2,3
let [x,,y] = [1,2,3];
console.log(x + "," + y); //1,3
let [head,...tail] = [1,2,3,4,5];
console.log(head); // 1
console.log(tail); // [2,3,4,5]
//不完全解构
let [x,y] = [1,2,3];
console.log(x+","+y);// 1,2
let [a,[b],d] = [1,[2,3],4];
console.log(a+","+b+","+d); // 1,2,4
2.2.3 解构赋值的用处
- 交换变量的值
- 从函数返回多个值
- 函数参数的定义
- 提取JSON数据
- 函数参数的默认值
- 遍历Map结构
- 输入模块的指定方法
1)交换变量的值
let x = 1;
let y = 2;
[x,y] = [y,x];
console.log(x + "," + y); // 2,1
- 从函数返回多个值
函数只能返回一个值,如果要返回多个值,就要把值放在数组或对象里面返回。有了解构赋值就非常简单了
function example() {
return [1,2,3];
}
let [a,b,c] = example();
console.log(a,b,c); // 1 2 3
function example2() {
return {
x: 111,
y: 222,
z: 333
}
}
let {x,y,z} = example2();
console.log(x,y,z); // 111 222 333
- 函数参数的定义
function f([x,y,z]) {
return x + y + z;
}
console.log(f([1,2,3])); // 6
function f1({x,y,z}){
return x + y +z;
}
console.log(f1({y:3,z:2,x:1})); // 6
- 提取JSON数据
let jsonData = {
id: 2,
status: 'ok',
data: [123,456]
};
let { id,status,data: number } = jsonData;
console.log(id+","+status+","+number); // 2,ok,123,456
- 函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};
- 遍历Map结构
const map = new Map();
map.set('first','hello');
map.set('second','world');
for(let [key,value] of map){
console.log(key + " is " + value);
}
- 输入模块的指定方法
加载模块时,往往需要指定哪些输入方法。使用解构赋值如下:
const { sourcemap,sourcenode } = require('source-map');
2.3 箭头函数
2.3.1 箭头函数
使用“=>”来定义函数.如下是相同的两种函数声明方式
//箭头函数
var f = v => v;
//普通函数
var f = function f(v) {
return v;
}
2.3.2 箭头函数的参数
- 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
//等同于如下
var f = function() {
return 5;
}
//需要多个参数
var f = (num1,num2) => num1 + num2;
//等同于
var f = function (num1,num2){
return num1 + num2;
}
//如果箭头函数多与一条语句,就使用大括号将他们扩起来
var f = () => {
let a = 1;
let b = 3;
return a + b;
}
//由于{}被解释为代码块,箭头函数直接返回一个对象,需要在对象外面加上括号,否则会报错
let retObject = id => ({ id:1, name: "Tom"});
//变量函数可以与解构赋值结合使用
const f = ({ first,second }) => first + " " + second;
//等同于
const f = function(person) {
return person.first + " " + person.second;
}
- 使用箭头函数,有时一行代码就能定义一个简单的工具函数。如下:
const square = n => n * n;
console.log(square(2));
console.log(square(3));
箭头函数的一个用处,就是用来简化回调函数
- 箭头函数能处理this指向的问题
长期以来,JavaScript语言的this对象一直是一个令人头痛的问题,在对象方法中使用this,必须非常小心。但是使用箭头函数后,要担心的问题就解决了。
使用普通函数
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
setTimeout(function(){
//此处this指向的是全局对象,而不是animal对象
console.log(this.type + " says " + say);
console.log(this); // windows对象
},1000)
}
}
var animal = new Animal();
animal.says('hello'); // undefined says hello
使用箭头函数
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
//使用箭头函数
setTimeout( () => {
//此处this指向的Animal对象
console.log(this.type + " says " + say);
console.log(this); //Animal对象
},1000);
}
}
var animal = new Animal();
animal.says('hello'); // animal says hello
不使用箭头函数
//this重指向
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
//此处this指向的Animal对象
console.log(this);
const self = this;
setTimeout(function(){
//此处self.type就是构造方法中的值
console.log(self.type + " says " + say);
console.log(this); // windows对象
},1000)
}
}
var animal = new Animal();
animal.says('hello'); // animal says hello
var animal = new Animal();
animal.says('koook'); // animal says koook
//方法bind this,生成一个新的对象
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
//此处this指向的Animal对象
console.log(this);
// const self = this;
setTimeout(function(){
//此处self.type就是构造方法中的值
console.log(this.type + " says " + say);
console.log(this); // Animal对象
}.bind(this),1000)
}
}
var animal = new Animal();
animal.says('hello'); // animal says hello
箭头函数使用注意点
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当做构造函数,不可以使用new命令,否则会抛出一个错误
- 不可以使用arguments对象,该对象在函数体内不存在。如果要使用,可以用rest参数代替
- 不可以使用yield命令,因此箭头函数不能用作Generator函数
2.4 模板字符串(template string)
当我们要插入大段的html内容到文档中时,传统的写法非常麻烦;如下:
//不使用模板字符串
const tempStr = "There are <b>" + '3' + "</b> " +
"items in your basket, " +
"<em>" + '$50.00' +
"</em> are on sale!";
const tempNode = document.getElementById('app');
console.log(tempNode);
tempNode.innerHTML = tempStr;
//使用模板字符串
const tempStr = `
There are <b>3</b> items
in your basket, <em>$50.00</em>
are on sale!`;
const tempNode = document.getElementById('app');
console.log(tempNode);
tempNode.innerHTML = tempStr;
2.5 class extends super
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
//定义Animal类
class Animal {
//构造方法内定义的方法和属性是实例对象自己的
//构造方法为定义的方法和属性是所有实例对象共享的
constructor() {
this.type = 'animal';
}
says (say) {
console.log(this.type + " says " + say);
}
}
//实例化Animal对象
let animal = new Animal();
//animal对象调用says方法
animal.says('hello');// animal says hello
//定义Cat类,并继承Animal类
class Cat extends Animal {
//子类的构造方法先调用父类的构造方法
constructor() {
//指代父类的实例(即父类的this对象)
super();
this.type = 'cat';
}
}
//实例化Cat对象
let cat = new Cat();
//cat对象调用父类的says方法
cat.says('miaomiao');// cat says miaomiao
上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Cat类,该类通过extends关键字,继承了Animal类的所有属性和方法。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
2.6 default、rest参数
2.6.1 default的写法
//es5及之前的default写法
function animal(type) {
type = type || 'cat';
console.log(type);
}
animal(); // cat
animal("mouse"); // mouse
//es6的default写法
const animal2 = (type='cat') => console.log(type);
animal2(); // cat
animal2("dog"); // dog
2.6.2 rest参数
rest参数(...变量名),用于获取函数的多余参数,这样就不需要使用Arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add (...values) {
let sum = 0;
for(let val of values) {
sum += val;
}
console.log(sum);
return sum;
}
add(1,2,3);
rest参数之后,不能再有其它参数
//error
function f(a,...b,c) {
...
}