ES6

一、let、const

没有变量提升

暂时性死区,必须引用前声明

块级作用域内才可以使用

不可以重复声明,否则会报错

二、箭头函数

ES6允许使用“箭头”(=>)定义函数

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。

使用注意点

由于箭头函数没有自己的 this ,所以当然也就不能用 call() apply() bind() 这些方法去改变 this 的指向。

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:argumentssupernew.target


三、promise

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

1.Promise的立即执行性

 Promise对象表示未来某个将要发生的事件,但在创建(new)Promise时,作为Promise参数传入的函数是会被立即执行的,只是其中执行的代码可以是异步代码。有些同学会认为,当Promise对象调用then方法时,Promise接收的函数才会执行,这是错误的。因此,代码中”create a promise”先于”after new Promise”输出。then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行

promise.Trick>promise函数回调>setTimeout

2.Promise 三种状态

Promise的内部实现是一个状态机。Promise有三种状态:pending,resolved,rejected当Promise刚创建完成时,处于pending状态;当Promise中的函数参数执行了resolve后,Promise由pending状态变成resolved状态;如果在Promise的函数参数中执行的不是resolve方法,而是reject方法,那么Promise会由pending状态变成rejected状态。

3.Promise 状态的不可逆性

 Promise状态的一旦变成resolved或rejected时,Promise的状态和值就固定下来了,不论你后续再怎么调用resolve或reject方法,都不能改变它的状态和值。因此,p1中resolve(“success2”)并不能将p1的值更改为success2,p2中reject(“reject”)也不能将p2的状态由resolved改变为rejected.

4.链式调用

Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象。这两个参数的返回值可以是以下三种情况中的一种:

(1)、return 一个同步的值 ,或者 undefined(当没有返回一个有效值时,默认返回undefined),then方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。 
(2)、return 另一个 Promise,then方法将根据这个Promise的状态和值创建一个新的Promise对象返回。 
(3)、 throw 一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。 
  根据以上分析,代码中第一个then会返回一个值为2(1*2),状态为resolved的Promise对象,于是第二个then输出的值是2。第二个then中没有返回值,因此将返回默认的undefined,于是在第三个then中输出undefined。第三个then和第四个then中分别返回一个状态是resolved的Promise和一个状态是rejected的Promise,依次由第四个then中成功的回调函数和第五个then中失败的回调函数处理。

5.Promise then() 回调异步性

 Promise接收的函数参数是同步执行的,但then方法中的回调函数执行则是异步的,因此,”success”会在后面输出

四、symbol类型

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值,它是JavaScript的第七种数据类型

var s = Symbol();
typeof s
//'symbol'

var sy=Symbol('foo')
\\Symbol(foo)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

基本数据类型:number、string、boolean、null、undefined

引用数据类型:Object、function、Date、Array、REGEXP

Symbol函数前不能使用 new;生成Symbol是一个原始类型的值,Symbol值不是对象,所以不能添加基本属性,它类似于字符串的数据类型

Symbol 接受字符串,方便对实例的描述 
如果参数为对象就会被 toString 转换为字符串后调用

var sd={}
var sg=Symbol(sd)
//Symbol([object Object])
  • 1
  • 2
  • 3

Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的

var sy1=Symbol();
var sy2=Symbol();
sy1==sy2
//false
sy1===sy2
//false

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Symbol值不能与其他类型的值进行运算,会报错。

var sy1=Symbol();
var str='sada'+sy1
\\TypeError: Cannot convert a Symbol value to a string
  • 1
  • 2
  • 3
  • 4

消除魔术字符串

魔术字符串:在代码之中多次出现,与代码形成强耦合的某一个具体的字符串或者数值

Symbol作为属性名不会出现for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回 
Object.getOwnPropertySymbols可以返回Symbol作为对象属性 
Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名

Symbol.for()

var sy1=Symbol.for('sy')
var sy2=Symbol.for('sy')var 
sy1===sy2
//true

var sm1=Symbol('ha')
var sm2=Symbol.for('ha')
sm1===sm2
//false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Symbol.keyFor() 
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

var sm1=Symbol('ha')
var sm2=Symbol.for('ha')
var key1=Symbol.keyFor(sm1)
//undefined
var key2=Symbol.keyFor(sm2)
//ha
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

需要注意的是,Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值

内置的Symbol值

Symbol.hasInstance

指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法

class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}

[1, 2, 3] instanceof new MyClass() // true
五、class

定义类

ES6中的类实际就是一个函数,且正如函数的定义方式有函数声明和函数表达式两种方式一样,类的定义也有两种方式,分别为:

  • 类声明
  • 类表达式

类声明

类声明是定义类的一种方式,使用class关键字后跟一个类名,就可以定义一个类。如下:

class Foo {
    constructor() {
        // ..
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
不存在变量提升(hoist)

类声明和函数声明不同的一点是,函数声明存在变量提升现象,而类声明不会。即,类必须先声明,然后才能使用,否则会抛出ReferenceError异常。

var foo = new Foo(); // Uncaught ReferenceError: Foo is not defined(...)
class Foo {
    // ...
}
  • 1
  • 2
  • 3
  • 4

这种规定的原因与类的继承有关,必须保证子类在父类之后定义。

let Foo = class {};

class Bar extends Foo {
}
  • 1
  • 2
  • 3
  • 4

上面的代码不会报错,因为class Bar继承Foo时,Foo已经有定义了。但是,如果存在Class提升,上面代码就会报错,因为Class Bar会被提升到代码头部,而表达式式Foo是不会提升的,所以导致Class Bar继承Foo的时候,Foo还没有定义。

类表达式

类表达式就定义类的另外一种方式,就像函数表达式一样,在类表达式中,类名是可有可无的。若定义的类名,则该类名只有的类的内部才可以访问到。

// 方式一
const MyClass = class {};

// 方式二:给出类名
const MyClass = class Me {
    getClassName() {
        return Me.name;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面方式二定义类的同时给出了类名,此时,Me类名只可以在Class的内部代码可用,指代当前类。MyClass的name属性值为给出的类名。

let my = new MyClass();
my.getClassName(); // Me
Me.name; // Uncaught ReferenceError: Me is not defined(…)
MyClass.name; // Me
  • 1
  • 2
  • 3
  • 4

采用类表达式,可以写出立即执行的Class。如下:

let person = new class {
    constructor(name) {
        this.name = name;
    }

    sayName() {
        console.log(this.name);
    }
}('Zhang San');

person.sayName(); // Zhang San
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

类体和方法定义

类的成员需要定义在一对大括号内{},大括号内的代码的大括号本身组成了类体。类成员包括类构造器和类方法(包括静态方法和实例方法)。

严格模式

类体中的代码都强制在严格模式中执行,即默认”use strict”。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。

构造器(constructor方法)

constructor方法是一个特殊的类方法,它既不是静态方法也不是实例方法,它仅在实例化的时候被调用。一个类只能拥有一个名为constructor的方法,否则会抛出SyntaxError异常。

如果没有定义constructor方法,这个方法会被默认添加,即,不管有没有显示定义,任何一个类都有constructor方法。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。

class Point {}

class ColorPoint extends Point {
    constructor() {}
}

let cp = new ColorPoint(); // ReferenceError
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

原型方法

定义类的方法时,方法名前面不需要加上function关键字。另外,方法之间不需要用逗号分隔,加了会报错。

class Bar {
    constructor() {}

    doStuff() {}

    toString() {}

    toValue() {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

类的所有方法都是定义在类的prototype属性上的,上面的写法等同于下面:

Bar.prototype = {
    doStuff() {},
    toString() {},
    toValue() {}
};
  • 1
  • 2
  • 3
  • 4
  • 5

所以,在类的实例上调用方法,实际上就是调用原型上的方法。

class B {}
let b = new B();

b.constructor === B.prototype.constructor; // true
  • 1
  • 2
  • 3
  • 4

上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。 
由于类的方法都是定义在prototype上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。

class Point {
    constructor() {
        // ...
    }
}

Object.assign(Point.prototype, {
    toString() {},
    toValue() {}
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。

class Point {
    constructor(x, y) {
        // ...
    }

    toString() {
        return '(' + x + ', ' + y + ')';
    }
}

Object.keys(Point.prototype); // []
Object.getOwnPropertyNames(Point.prototype); // ["constructor", "toString"]
Object.getOwnPropertyDescriptor(Point, 'toString');
// Object {writable: true, enumerable: false, configurable: true}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

静态方法

static关键字用来定义类的静态方法。静态方法是指那些不需要对类进行实例化,使用类名就可以直接访问的方法。静态方法经常用来作为工具函数。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.sqrt(dx*dx + dy*dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

静态方法不可以被实例继承,是通过类名直接调用的。但是,父类的静态方法可以被子类继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod(); // "hello"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

静态方法也可以用super关键字调用。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod(); // "hello too"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

extends关键字

extends关键字用于实现类之间的继承。子类继承父类,就继承了父类的所有属性和方法。 
extends后面只可以跟一个父类。

super 关键字

super关键字可以用来调用其父类的构造器或方法。

class Cat { 
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

类的Getter和Setter方法

与ES5一样,在类内部可以使用getset关键字,对某个属性设置取值和赋值方法。

class Foo {
    constructor() {}

    get prop() {
        return 'getter';
    }

    set prop(val) {
        console.log('setter: ' + val);
    }
}

let foo = new Foo();

foo.prop = 1;
// setter: 1

foo.prop;
// "getter"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上面代码中,prop属性有对应 的赋值和取值方法,因此赋值和读取行为都被自定义了。 
存值和取值方法是设置在属性的descriptor对象上的。

var descriptor = Object.getOwnPropertyDescriptor(Foo.prototype, 'prop');

"get" in descriptor // true
"set" in descriptor // true
  • 1
  • 2
  • 3
  • 4

上面代码中,存值和取值方法是定义在prop属性的描述对象上的,这与ES5一致。

类的Generator方法

如果类的某个方法名前加上星号(*),就表示这个方法是一个Generator函数。

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值