1、虽然ES6类表面上看起来是可以支持正式的面向对象编程,但是实际上它背后使用的仍然是原型和构造函数的概念

2、类与函数之间的区别

与函数的定义不同的是,虽然函数声明可以提升,但类的定义不可以提升

函数受函数作用域限制,而类受块作用域限制

类表达式的名称是可选的,把类表达式赋值给变量后,可以通过属性取得类表达式的名称字符串,但不能在表达式作用域外部访问这个标识符

let Person = class PersonName {
     identify() {
         console.log(Person.name, PersonName.name)
     }
}

let p = new Person()

p.identify()

console.log(Person.name)
console.log(PersonName)

3、constructor关键字

constructor关键字用于在类定义块内部创建类的构造函数,方法名constructor会告诉解释器在使用new操作符创建类的新实例时,应该调用这个函数。构造函数的定义不是必须的,不定义构造函数相当于将构造函数定义为空函数。

4、实例化

使用new操作符实例化Person的操作等于使用new调用其构造函数。唯一可感知的不同之处就是,Javascript解释器知道使用new和类意味着应该使用constructor函数进行实例化

使用new调用类的构造函数会执行如下操作

(1)在内存中创建一个新对象

(2)这个新对象内部的[[ Prototype ]]指针被赋值为构造函数的prototype属性

  (3) 构造函数内部的this被赋值为这个新对象(即this指向新对象)

(4)执行构造函数内部的代码(给新对象添加属性)

(5)如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象

 看看高程上关于对象和原型的部分

class Animal {}

class Person {
  constructor() {
    console.log('person ctor')
  }
}

class Vegetable {
  constructor() {
    this.color = 'orange'
  }
}

let a = new Animal()

let p = new Person()

let v = new Vegetable()

console.log(v.color)

类实例化时传入的参数会作用构造函数的参数,如果不需要参数,则类名后面的括号也是可选的

class Person {
  constructor(name) {
    console.log(arguments.length)
    this.name = name || null
  }
}

let p1 = new Person
console.log(p1.name)

let p2 = new Person()
console.log(p2.name)

let p3 = new Person('Jake')
console.log(p3.name)

默认情况下,类构造函数会在执行之后返回this对象,构造函数返回的对象会被用作实例化的对象,如果没有什么引用新创建的this对象,那么这个对象会被销毁,不过,如果返回的不是this对象,而是其他对象,那么这个对象不会通过instanceof操作符检测出跟类有关联,因为这个对象的原型指针并没有修改

class Person {
  constructor(override) {
    this.foo = 'foo'
    if(override) {
      return {
        bar: 'bar'
      }
    }
  }
}

let p1 = new Person(),
    p2 = new Person(true);

console.log(p1)
console.log(p1 instanceof Person)

console.log(p2)
console.log(p2 instanceof Person)

类构造函数与构造函数的主要区别是:调用类构造函数必须使用new操作符,而普通构造函数如果不使用new调用,那么就会以全局的this(通常是window)作为内部对象

类通过typeof操作符检测类标识符,表明它是一个函数

class Person {}

console.log(Person)
console.log(typeof Person)


console.log(Person.prototype)
console.log(Person === Person.prototype.constructor)

在类的上下文中,类本身在使用new调用时就会被当成构造函数

类中定义的constructor方法不会被当作构造函数,在对它使用instanceof操作符时会返回false

如果在创建实例时直接将类构造函数当成普通构造函数来使用,那么instanceof操作符的返回值会反转

class Person {}

let p1 = new Person()

console.log(p1.constructor === Person)
console.log(p1 instanceof Person)
console.log(p1 instanceof Person.constructor)

let p2 = new Person.constructor()

console.log(p2.constructor === Person)
console.log(p2 instanceof Person)
console.log(p2 instanceof Person.constructor)

5、实例成员

每个实例都对应一个唯一的成员对象,所有成员都不会在原型上共享

class Person {
  constructor() {

    this.name = new String('Jack')

    this.nicknames = ['Jake', 'J-Dog']
  }
}

let p1 = new Person(), 
    p2 = new Person();
p1.sayName()
p2.sayName()

console.log(p1.name === p2.name)
console.log(p1.sayName === p2.sayName)
console.log(p1.nicknames === p2.nicknames)

6、为了在实例间共享方法,类定义语法把在类块中定义的方法作为原型方法

class Person {
  constructor() {
    // 添加到this的所有内容都会存在于不同的实例上
    this.locate = () => console.log('instance')

    // 在类块中定义的所有内容都会定义在类的原型上
    locate() {
      console.log('prototype')
    }
  }
}

类定义也支持获取和设置访问器,语法和普通对象一样:

class Person {
  set name(newName) {
    this.name_ = newName
  }

  get name() {
    return this.name_
  }
}

let p = new Person()
p.name = 'Jake'
console.log(p.name)

7、静态类方法

静态类成员在类定义中使用static关键字作为前缀

在静态成员中,this引用类自有约定跟原型成员一样

静态类方法非常适合作为实例工厂

8、类的继承

ES6中原生支持了类继承,虽然使用了新的语法,但是背后使用的还是原型链

类可以继承类,也可以继承普通的构造函数(保持向后兼容)

派生类都会通过原型链访问到类和原型上定义的方法

9、super

派生类的方法可以通过super关键字引用他们的原型

关键字只能在派生类中使用,而且仅限于类构造函数、实例方法和静态方法内部

在类构造函数中使用super可以调用父类构造函数

10、抽象基类

11、类混入