ES6常用特性

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

  • let、const 及var的不同
  • Destructuring 解构赋值
  • arrow function 箭头函数
  • template string 模板字符串
  • class、extends、super
  • default、rest
  • 总结
2.1 let、const和var之间的异同
  • let变量 有块级作用域,没有var的变量提升,不能重复声明覆盖
  • var变量 有函数作用域,有变量提升,可以重复声明覆盖
  • const常量 声明后不可再修改的,不可以重复声明覆盖
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 解构赋值的用处

  • 交换变量的值
  • 从函数返回多个值
  • 函数参数的定义
  • 提取JSON数据
  • 函数参数的默认值
  • 遍历Map结构
  • 输入模块的指定方法

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

箭头函数使用注意点

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当做构造函数,不可以使用new命令,否则会抛出一个错误
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要使用,可以用rest参数代替
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数
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) {
    ...
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容