ES6语法的新特性
之前面试被问到ES6新特性,箭头函数,generator、Module等等,表示刚开始上手用ES6,目前还没有很深入,所以这次想对ES6一些常用的特性进行整理。
let和const
主要是实现块级作用域的。因为在原生JS中,没有块级作用域。只有在函数中,才构成一个作用域,但是利用函数是可以实现一个类似于块级作用域的。详见这里。
不过,为了更方便的解决类似于下面这种问题:
setTimeout(function(){
for( var i = 0; i < 10; i++)
{
console.log(i);
}
})
ES6引入了两个新的特性,let
和const
。
let
和var
有什么区别呢?
1. `let`声明的变量有块级作用域,不能作为全局对象的全局属性被访问,即不能通过window来访问;
2. `let` 声明的变量不能被重复声明,而`var`没有关系;
3. 像 `for(let i ......)`这种形式,会在每次迭代对`i`进行绑定。
const与let类似,唯一的不同就在于const
声明变量时必须初始化,并且初始化之后就不能再对值进行修改了,否则会报错!
箭头函数
箭头函数出现的目的是为了让JS代码显得更简洁一些。
// 六种语言中的简单函数示例
function (a) { return a > 0; } // JS
[](int a) { return a > 0; } // C++
(lambda (a) (> a 0)) ;; Lisp
lambda a: a > 0 # Python
a => a > 0 // C#
a -> a > 0 // Java
我们都知道函数式编程很普遍,而且原生JS来实现比较冗长,所以ES6新引入了一种箭头函数的写法。
那么箭头函数和普通函数有什么区别呢?主要区别就是this
的指向。在箭头函数里面是没有自己的this
的,里面的this
继承自外围作用域。而普通函数是有自己的作用域的,所以才会在引入闭包的时候,this
一般都指向全局window
对象。
var obj = {
name: 'heihei',
sayName: function(){
return function(){
console.log(this);
}
}
}
var obj1 = {
name: 'Hellen',
sayName: function(){
return ()=>{ console.log(this) };
}
}
另一点不同,就是箭头函数里面是访问不到arguments参数列表的,所以只能通过不定参数和默认参数这两个机制来实现。可参考这篇文章。
class类
在原生ES5中,我们通常利用构造函数来创建对象,而ES6引入一种与Java,C++等更接近的方法来创建对象-class。
我们对比下构造函数和class有什么不同。
function Circle( radius ) {
this.radius = radius;
this.area = function(){
return Math.pow(this.radius, 2) * Math.PI;
}
}
var c = new Circle(4);
var area = c.area();
console.log(area);
class Circle {
constructor( radius ) {
this.radius = radius;
};
area() {
return Math.pow(this.radius, 2) * Math.PI;
}
}
var c = new Circle(4);
var area = c.area();
console.log(area);
在写法上,除了类的定义与构造函数不同,在用法上是相同的语法。不同的地方我们看下下面两张图就明白了。
这是利用构造函数构造的对象:
利用Class创建的对象:
Iterator(遍历器)
在原生JS中,遍历对象/数组有这么几种方式:
- for循环
//最简单也最直观的循环遍历
var arr = [2,6,5];
for(var i = 0; i < arr; i++) {
console.log(arr[i]);
}
- forEach
//写法比上面的简单很多;
//但是对于对象不使用,并且不能跳出循环,即用break,continue,return等
var arr = [2,6,5];
arr.forEach(function(data) {
console.log(data);
})
- for … in
//对于数组而言,是按照arr['0'],arr['1']来访问的;
//不一定按顺序访问的
//专门适用于对象的遍历
var arr = [2,6,5];
for(var index in arr) {
console.log(arr[index]);
}
而Iterator
的出现就是为解决上面的一些缺点出现的,为的是给不同的数据结构产生统一的接口。只要部署一个Iterator
接口,就可以为for ... of
服务,从而实现遍历。
Iterator
的过程是这样的:
- 创建一个指针对象,指向数据结构的起始位置;
- 调用指针对象的
next
方法,指针指向数据结构的第一个成员; -
一次类推,直到返回数据结构的最后一个成员。
Iterator
的next
方法返回的是一个对象:{ value: 成员对象的值, done:false/true }
其实在数组、字符串,以及ES6的Map和Set中,都有默认的Iterator
接口,因为有遍历器接口,所以才能调for ... of
来实现遍历。那我们看看默认的Iterator
接口是什么样的?
上图是一个数组对象的原型链上的方法。其中有一个Symbol.iterator
函数,这个函数其实就是一个遍历器接口。我们来使用一下:
这个函数返回的是一个对象表示数组Iterator。并且在这个对象里有next方法,这跟我们前面说的是相吻合的。
每一次调用next方法之后,就会返回一个对象,value表示数据结构当前位置的数值,done表示是否到最后一个成员。
所以只要一个数据结构,有这个接口,都可以调用for…of方法来实现。
因为Object里面没有Iterator接口,所以不能直接用for…of实现遍历,那怎么办呢?
其实可以先调Object.keys将对象转成数组。
后期还会续上generator生成器对象(迭代器对象)以及modules等新特性。