ES6(ECMAScript2015)的出现,无疑给前端开发人员带来了新的惊喜,它包含了一些很棒的新特性,可以更加方便的实现很多复杂的操作,提高开发人员的效率。ES6在我们的日常编码中出现的频次这里就不在多说,直接正文。
常用ES6特性
1 2 3 4 5 6 7 8 9 10 |
let, // 类似var,可声明块级作用域变量 const, // 类似var,只能声明常量 class, // 类的支持 extends, // 继承 super, // 设置调整this指针 arrow functions, // 箭头操作符 template string, // 字符串模板 destructuring, // 解构 default, // 参数默认值 rest arguments // 不定参数,拓展参数 |
let, const
可以把let看成var,let为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。
const用来定义常量,即无法被更改值的变量。
1 2 3 4 5 6 7 |
var name = 'Jack'; while (true) { var name = 'Tom'; console.log(name); // Tom break; } console.log(name) //Tom |
使用var 两次输出都是Tom,这是因为ES5只有全局作用域和函数作用域,没有块级作用域。导致内层变量覆盖外层变量。当然在实际编码过程中还会遇到其他问题。
1 2 3 4 5 6 7 |
let name = 'Jack'; while (true) { let name = 'Tom'; console.log(name); // Tom break; } console.log(name) //Jack |
另外一个var带来的不合理场:
1 2 3 4 5 6 7 |
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10 |
变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i的值。而使用let则不会出现这个问题。
1 2 3 4 5 6 |
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6 |
用const声明一个常量:
1 2 |
const PI = Math.PI; PI = 23; // VM355:2 Uncaught TypeError: Assignment to constant variable.(…) |
当我们尝试去改变用const声明的常量时,浏览器就会报错。
const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:
1 |
const monent = require(‘moment’) |
class, extends, super
原型、构造函数,继承
class 让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Animal { constructor(){ // 构造方法 this.type = 'animal'; // this关键字则代表实例对象 } says(say){ console.log(this.type + ' says ' + say) } } let animal = new Animal(); animal.says('hello') //animal says hello class Cat extends Animal { constructor(){ super(); this.type = 'cat' } } let cat = new Cat() cat.says('hello') //cat says hello |
上面的代码首先用class 定义了一个“类”,其中构造方法constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
然后又定义了一个Cat类,该类通过extends关键字继承了Animal类的所有属性和方法。
Class之间通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
arrow function 箭头操作符
箭头操作符和lambda表达式有异曲同工之妙,它简化了函数的书写。操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//ES5 function(i){ return i + 1; } //ES6 (i) => i + 1 //ES5 function(x, y) { x++; y--; return x + y; } //ES6 (x, y) => {x++; y--; return x+y} |
arrow function 另一个有趣的功能
1 2 3 4 5 6 7 8 9 10 11 12 |
class Animal { constructor(){ this.type = 'animal'; } says(say){ setTimeout(function(){ console.log(this.type + ' says ' + say) // 这里的this 我们都是到已经不是当前对象,而是全局对象 }, 1000); } } var animal = new Animal(); animal.says('hi'); |
解决他的方案,
第一种是将this传给_this,再用_this来指代this
1 2 3 4 |
var _this = this; setTimeout(function(){ console.log(_this.type + ' says ' + say) }, 1000); |
第二种就是用bind(this)了
1 2 3 |
setTimeout(function(){ console.log(this.type + ' says ' + say) }.bind(this), 1000); |
但现在我们有了箭头函数,就不需要这么麻烦了:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Animal { constructor(){ this.type = 'animal'; } says(say){ setTimeout( () => { console.log(this.type + ' says ' + say); }, 1000); } } var animal = new Animal(); animal.says('hi'); |
箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。
template string
当我们要插入大段的html内容到文档中时,传统的写法用’+’号来连接文本与变量;而使用ES6的新特性模板字符串用反引号(`)来标识起始,用${}来引用变量,而且所有的空格和缩进都会被保留在输出之中。
1 2 3 4 5 6 7 8 9 |
// //ES5 $("#result").append( "There are <b>" + basket.count + "</b> " + "items in your basket, " + "<em>" + basket.onSale + "</em> are on sale!"); //ES6 $("#result").append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `); |
destructuring 解构
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// ES5 let cat = 'ken'; let dog = 'lili'; let zoo = {cat: cat, dog: dog}; console.log(zoo); //Object {cat: "ken", dog: "lili"} // ES6 let cat = 'ken'; let dog = 'lili'; let zoo = {cat, dog}; console.log(zoo) //Object {cat: "ken", dog: "lili"} // ES6 let dog = {type: 'animal', many: 2}; let { type, many} = dog; console.log(type, many) //animal 2 |
自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。
1 2 3 4 5 6 7 8 9 |
var [x,y]=getVal(),//函数返回值的解构 [name,,age]=['wayou','male','secrect'];//数组解构 function getVal() { return [ 1, 2 ]; } console.log('x:'+x+', y:'+y);//输出:x:1, y:2 console.log('name:'+name+', age:'+age);//输出: name:wayou, age:secrect |
default, rest rest arguments 参数默认值,不定参数,拓展参数
默认参数值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// ES5 function animal(type){ type = type || 'cat' console.log(type) } animal() // ES6 function animal(type = 'cat'){ console.log(type) } animal() |
不定参数
1 2 3 4 |
function animals(...types){ console.log(types) } animals('cat', 'dog', 'fish') //["cat", "dog", "fish"] |
拓展参数:另一种形式的语法糖,它允许传递数组或者类数组直接做为函数的参数而不用通过apply。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var people=['Wayou','John','Sherlock']; //sayHello函数本来接收三个单独的参数人妖,人二和人三 function sayHello(people1,people2,people3){ console.log(`Hello ${people1},${people2},${people3}`); } // ES6 //但是我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数 sayHello(...people);//输出:Hello Wayou,John,Sherlock // ES5 //而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法 sayHello.apply(null,people);//输出:Hello Wayou,John,Sherlock |
PHP程序员雷雪松 发表于: 2016/12/22 10:06