前端学习让前端飞程序员

ES6常用特性

2017-12-15  本文已影响790人  方千竹

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、常用特性有?

2.1 let、const和var之间的异同
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 解构赋值的用处

1)交换变量的值

let x = 1;
let y = 2;
[x,y] = [y,x];
console.log(x + "," + y); // 2,1

  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
  1. 函数参数的定义
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
  1. 提取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
  1. 函数参数的默认值
jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff
};
  1. 遍历Map结构
const map = new Map();
map.set('first','hello');
map.set('second','world');

for(let [key,value] of map){
    console.log(key + " is " + value);
}
  1. 输入模块的指定方法

加载模块时,往往需要指定哪些输入方法。使用解构赋值如下:

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 箭头函数的参数

  1. 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
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;
}

  1. 使用箭头函数,有时一行代码就能定义一个简单的工具函数。如下:
const square = n => n * n;
console.log(square(2));
console.log(square(3));

箭头函数的一个用处,就是用来简化回调函数

  1. 箭头函数能处理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

箭头函数使用注意点

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) {
    ...
}
上一篇下一篇

猜你喜欢

热点阅读