ECMAScript 6入门(2)

上篇文章我们介绍了ES6的多行字符串表示方法,map,set,for...of循环,rest参数以及let和const,这次我们接着来看看ES6有哪些过人之处。

ECMAScript 6为数组增添的新方法:map(),reduce(),filter()

①map()

语法:arr.map(函数);

功能:相当于让arr的每个数据执行了一次()中的方法,例:

function add(a){
    return  b =a*a;
}
var arr = [1,2,3,4];
var newArr = arr.map(add);
console.log( newArr);
ECMAScript 6入门(2)_第1张图片
image

②reduce()

语法:arr.reduce(函数);

功能:把一个函数作用在arr的每一个元素上,它必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算。例:要把[1,2,3,4,5,6]变换成整数123456,就可以用reduce()做到

function changeNumber(x,y){
      return  x * 10 + y;
 }
var arr = [1,2,3,4,5,6];
var newArr = arr.reduce(changeNumber);
console.log(newArr);

③filter()

语法:arr.filter(函数);

功能:用于把Array的某些元素过滤掉,然后返回剩下的元素,和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。例:利用filter()删除数组中的偶数项

function deleteOushu(x){
     return x % 2 !== 0;
}
var arr = [1,2,3,4,5,6,7,8,9,10];
var newArr = arr.filter(deleteOushu);
console.log(newArr);
ECMAScript 6入门(2)_第2张图片
image

filter()接收的回调函数可以有多个参数。第一个参数表示Array的某个元素。另外两个参数,表示元素的位置和数组本身:

var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {
    console.log(element); 
    // 依次打印'A', 'B', 'C'
    console.log(index); 
    // 依次打印0, 1, 2
    console.log(self); 
    // self就是变量arr
    return true;
});

我们还可以利用filter()巧妙的去除数组的重复项:

var arr = ["a","b","c","b","a","d"];
var newArr = arr.filter(function(element,index,self){
    return self.indexOf(element)===index;
});
ECMAScript 6入门(2)_第3张图片
y.png

箭头函数

ES6标准新增了一种新的函数:Arrow Function(箭头函数)

x => x * x;

这个箭头函数等价于

function (x){
    return  x * x ;
}

箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }和return:

x => { 
    if(x>0) {
        return x * x;
    } 
    else{
        return -x * x;
    } 
}; 

如果参数不是一个,就需要用括号()括起来:

(x,y) => x * x + y * y;

无参数时就用():

() => 1;

可变参数:

(x, y, ...rest) => {
    var i, sum = x + y;
    for (i=0; i

如果要返回一个对象就要注意,如果是单表达式,这么写的话会报错:

x => { foo: x }    // SyntaxError

因为和函数体的{ ... }有语法冲突,所以要改为:

x => ({ foo: x })

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj。由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,this取决于箭头函数在哪定义,this不可变,在函数内部这个执行环境中相当于常量。
箭头函数没有arguments,不能通过arguments对象访问传入参数,只能使用显示命名或者其他特性完成。
箭头函数不能使用new关键字来实例化对象,不然会报错。

箭头函数跟普通函数的区别,什么是匿名函数
(1)不需要function
(2)省略return
(3)继承当前上下文的关键字(定义时所在对象)
( 4)不能用作构造函数
(5)不能用yeild
(6)this固有化
(7)不能使用call,apply,bind去改变

匿名函数:没有名字的函数表达式,可以实现动态编程。

generator(生成器)

一个generator看上去像一个函数,但可以返回多次。函数执行过程中,如果没有遇到return语句(函数末尾如果没有return,就是隐含的return undefined;),控制权无法交回被调用的代码。generator跟函数很像,定义如下:

function* foo(x) { 
    yield x + 1;
    yield x + 2;
    return x + 3;
}

和函数不同的是generator由function定义(注意多出的号),并且除了return语句,还可以用yield返回多次。例:编写一个产生斐波那契数列的函数,可以这么写:

function fib(max) {
    var t=0; 
    var a=0;
    var b=1;
    var arr=[0, 1];
    while (arr.length < max) { 
        t = a + b;
        a = b;
        b = t;
        arr.push(t);
    }
    return arr;
}
fib(5);      // 测试结果[0, 1, 1, 2, 3]

函数只能返回一次,必须返回一个Array。generator就可以一次返回一个数,不断返回多次。用generator改写如下:

function* fib(max) {
    var t=0;
    var a=0;
    var b=1;
    var n=1;
    while (n < max) {
        yield a;
        t = a + b;  
        a = b;
        b = t;
        n++;
    }
    return a;
}
fib(5);    // fib{[[GeneratorStatus]]: "suspended",[[GeneratorReceiver]]:Window}

fib(5)仅仅是创建了一个generator对象,还没有去执行它
调用generator对象有两个方法,一是不断地调用next()方法:

var f = fib(5);
f.next();    // {value: 0, done: false}
f.next();    // {value: 1, done: false}
f.next();    // {value: 1, done: false}
f.next();    // {value: 2, done: false}
f.next();    // {value: 3, done: true}

value就是yield的返回值,done表示这个generator是否已经执行结束了。为true时value就是return的返回值,并且generator对象就已经全部执行完毕,不要再继续调用next()了。
第二个方法是直接用for ... of循环迭代generator对象,这种方式不需要我们自己判断done:

for (var x of fib(5)) {
    console.log(x);    // 依次输出0, 1, 1, 2, 3
}       

class继承

继承中我们看到了JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链。新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
我们先回顾用函数实现Student的方法:

function Student(name) {
    this.name = name;
} 
Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

如果用新的class关键字来编写Student,可以这样写:

class Student {
    constructor(name) {
        this.name = name;
    } 
    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

比较一下就可以发现,class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),这样就避免了Student.prototype.hello = function () {...}这样分散的代码。
最后,创建一个Student对象

var xiaoming = new Student('小明'); 
xiaoming.hello();

用class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Student派生一个PrimaryStudent需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

class PrimaryStudent extends Student { 
    constructor(name, grade) { 
        super(name); 
        // 记得用super调用父类的构造方法! 
        this.grade = grade; 
    } 
    myGrade() {
    alert('I am at grade ' + this.grade);
    }
}

注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,例如,PrimaryStudent需要name和grade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。
PrimaryStudent已经自动获得了父类Student的hello方法,我们又在子类中定义了新的myGrade方法。
ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

你可能感兴趣的:(ECMAScript 6入门(2))