JavaScript ES6新特性介绍
ES6是一次重大的革新,比起过去的版本,改动比较大。ECMAScript 2015(ES2015),第 6 版,也被称作是 ECMAScript 6(ES6),添加了类和模块的语法,箭头函数,迭代器和生成器和生成器表达式,静态类型数组等
let、const
在ES6以前,JS只有var一种声明方式,且存在变量提升现象,但是在ES6之后,就多了let跟const这两种方式。用var定义的变量没有块级作用域的概念,而let跟const则会有,它们的区别如下表:
var | let | const | |
变量提升 | √ | × | × |
全局变量 | √ | × | × |
重复声明 | √ | × | × |
重新赋值 | √ | √ | × |
暂时死区 | × | √ | √ |
块作用域 | × | √ | √ |
只声明不初始化 | √ | √ | × |
【注:暂时死区
由于let/const没有变量提升(提升到作用域顶部),因此通过let/const定义的变量不会被提升到作用域顶部——也就是此时的块级作用域,因此在声明之前无法访问——若在声明之前使用将报错。】
let 关键词声明的变量不具备变量提升(hoisting)特性,
let 和 const 声明只在最靠近的一个块中(花括号内)有效,
const 在声明时必须被赋值。
ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部)
let和var声明的区别:
var x = '全局变量';
{
let x = '局部变量';
console.log(x); // 局部变量
}
console.log(x); // 全局变量
let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了:
const x = 10
a = 20 //报错
如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行:
const student = { name: 'LiMing' }
student.name = 'ZhangHua';// 不报错
student = { name: 'ZangHua' };// 报错
关于let声明语句,参见:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let
const声明语句,参见:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/const
类(Class)
在ES6之前,如果我们要生成一个实例对象,传统的方法就是写一个构造函数,例子如下:
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.information = function () {
return 'My name is ' + this.name + ', I am ' + this.age
}
但是在ES6之后,我们只需要写成以下形式:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
information() {
return 'My name is ' + this.name + ', I am ' + this.age
}
}
关于类,可参见:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
箭头函数(Arrow Functions)
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;
早期的函数,如:
let add=function(a,b){
return a+b;
};
用箭头函数,可写为为:
let add=(a,b)=>a+b;
箭头函在数语法上要比普通函数简洁得多。箭头函数省去了function关键字,采用箭头=>来定义函数。函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中。
箭头函数最直观的三个特点:
不需要 function 关键字来创建函数;
省略 return 关键字;
继承当前上下文的 this 关键字。
关于箭头函数的参数的说明:
① 如果箭头函数没有参数,直接写一个空括号即可。
② 如果箭头函数的参数只有一个,也可以省去包裹参数的括号。
③ 如果箭头函数有多个参数,将参数依次用逗号(,)分隔,包裹在括号中即可。
// 没有参数
let fun1 = () => {
console.log(123);
};
// 只有一个参数,可以省去参数括号 (name)
let fun2 = name => {
console.log(`Hello ${name} !`)
};
// 有多个参数
let fun3 = (val1, val2, val3) => {
return [val1, val2, val3];
};
关于箭头函数的函数体的说明:
① 如果箭头函数的函数体只有一句代码,就是简单返回某个变量或者返回一个简单的JS表达式,可以省去函数体的大括号{ }。
let f = val => val;
// 等同于
let f = function (val) { return val };
let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
return num1 + num2;
};
② 如果箭头函数的函数体只有一句代码,就是返回一个对象,可以像下面这样写:
// 用小括号包裹要返回的对象,不报错
let getTempItem = id => ({id: id, name: "Temp"});
// 但绝不能这样写,会报错。
// 因为对象的大括号会被解释为函数体的大括号
let getTempItem = id => {id: id, name: "Temp"};
③ 如果箭头函数的函数体只有一条语句并且不需要返回值(最常见是调用一个函数),可以给这条语句前面加一个void关键字
let fn = () => void doesNotReturn();
箭头函数与普通函数的区别
箭头函数与普通函数的区别
①箭头函数不绑定this关键字,箭头函数没有自己的this关键字,箭头函数中的this指向的是函数定义位置的上下文this,换句话说,箭头函数没有自己的this,它会捕获自己在定义时(注意,是定义时,不是调用时)所处的外层执行环境的this,并继承这个this值。所以,箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变。
var id = 'Global';
function fun1() {
// setTimeout中使用普通函数
setTimeout(function(){
console.log(this.id);
}, 2000);
}
function fun2() {
// setTimeout中使用箭头函数
setTimeout(() => {
console.log(this.id);
}, 2000)
}
fun1.call({id: 'Obj'}); // 'Global'
fun2.call({id: 'Obj'}); // 'Obj'
上面这个例子,对象obj的方法a使用普通函数定义的,普通函数作为对象的方法调用时,this指向它所属的对象。所以,this.id就是obj.id,所以输出'OBJ'。
但是方法b是使用箭头函数定义的,箭头函数中的this实际是继承的它定义时所处的全局执行环境中的this,所以指向Window对象,所以输出'GLOBAL'。
并且,箭头函数继承而来的this指向永远不变!
上面的例子,就完全可以说明箭头函数继承而来的this指向永远不变。对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。
②箭头函数不能作为构造函数使用(不能用new)
先了解一下构造函数的new都做了些什么?简单来说,分为四步:
1)JS内部首先会先生成一个对象;
2)再把函数中的this指向该对象;
3) 然后执行构造函数中的语句;
40 最终返回该对象实例。
因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!
let Fun = (name, age) => {
this.name = name;
this.age = age;
};
// 报错
let p = new Fun('cao', 24);
③箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部函数执行环境中的值。可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表!
ES6引入 rest参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
关于剩余参数,参见
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Rest_parameters
④箭头函数不能使用yeild关键字,因此箭头函数不能用作Generator函数。
关于箭头函数,可参见:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
迭代器(Iterator)和生成器(Generator)
下面是一段标准的for循环代码,通过变量i来跟踪colors数组的索引,循环每次执行时,如果i小于数组长度len则加1,并执行下一次循环
下面是一段标准的for循环代码,通过变量i来跟踪a数组的索引,循环每次执行时,如果i小于数组长度len则加1,并执行下一次循环
let a = ["aaa","bbb","ccc"];
for (let i = 0; i< a.length; i++){
console.log(a[i]);
}
i就是我们用来记录迭代位置的变量,但是在ES6开始,JavaScrip引入了迭代器这个特性,迭代器的出现旨在消除这种复杂性并减少循环中的错误,并且新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...),甚至连异步编程都可以使用迭代器。
迭代器是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value,表示下一个将要返回的值;另一个是done,它是一个布尔类型的值,当没有更多可返回数据时返回true。
生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号可以紧挨着function关键字,也可以在中间添加一个空格,例如:
// 生成器
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
示例中,createlterator()前的星号表明它是一个生成器;yield关键字也是ES6的新特性,可以通过它来指定调用迭代器的next()方法时的返回值及返回顺序。生成迭代器后,连续3次调用它的next()方法返回3个不同的值,分别是1、2和3。
在生成器函数中,每当执行完一条yield语句后函数就会自动停止执行,在上面这段代码中,执行完语句yield 1之后,函数便不再执行其他任何语句,直到再次调用迭代器的next()方法才会继续执行yield 2语句。
关于迭代器(Iterator)和生成器(Generator),简要介绍这些,想了解更多,可参见:
https://www.cnblogs.com/xiaohuochai/p/7253466.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators
待续