1. 类定义

  • 类的定义分为类声明和类选表达式
  • 类表达式在它们被求值前不能引用
  • 类定义与函数定义的区别: 函数表达式可以提升, 类定义不能
  • 另一个不同点, 函数受函数作用与限制; 而类受块作用域限制 ```javascript // 类声明 class Animal {}

// 类表达式 const animal = class {}

// 变量的提升 console.log(fn) // undefined var fn = function () {}

console.log(c) // 报错 var c = class {}

// 另一个不同点, 函数受函数作用与限制; 而类受块作用域限制 { function fn () {} class ClassName {} } console.log(fn) // 能够打印出fn console.log(ClassName) // 出错

  1. **类构成**
  2. ```javascript
  3. // 空类定义,有效
  4. class Foo {}
  5. // 有构造函数的类 有效
  6. class Bar {
  7. constructor() {}
  8. }
  9. // 有获取函数的类 有效
  10. class Bar {
  11. get myBzr () {}
  12. }
  13. // 有静态方法的类 有效
  14. class Qux {
  15. static myQux () {}
  16. }

2. 类的构造函数

类的构造函数: constructor() 用在类内部创建类的构造函数。 constructor() 在使用 new操作符创建类的实例时调用。

实例化:

(new 的实例化过程很重要)
讲了使用 new 操作符实例化后的操作; 意味着会跟着调用 constructor()构造函数;

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

  1. 在内存种创建一个新对象
  2. 这个对象内部的 [[Prototype]] 指针被赋值为构造函数(constructor)的 prototype(原型) 属性
  3. 构造函数 constructor内部的this被赋值给这个新对象 (即 this 指向新对象)
  4. 执行构造函数constructor 内部的代码 (给新对象添加属性)
  5. 如果构造函数返回非空对象, 则返回该对象; 否则,返回刚创建的对象
  • 在类实例化时,可以传入参数作为构造函数的参数。
  • 可以使用 instanceof 检测实例 与类是否具有关联
  • 类的构造函数 class Animal {} 与 构造函数 function Person () {} 的区别
    • 调用类构造函数必须使用 new操作符, 不使用 new 操作符会报错
    • 而普通构造函数不适用 new调用, 则它们就以全局的 this (通常时window) 作为内部对象
  1. class Person {
  2. constructor(name) {
  3. this.foo = 'foo';
  4. if (name) {
  5. return {
  6. bar: 'bar'
  7. }
  8. }
  9. }
  10. }
  11. let p1 = new Person(),
  12. p2 = new Person('张三');
  13. console.log(p1) // Person { foo: 'foo' }
  14. console.log(p1 instanceof Person) // true
  15. console.log(p2) // { bar: 'bar' } 返回的只是一个普通的对象
  16. console.log(p2 instanceof Person) // false
  17. //
  18. function Person () {}
  19. class Animal {}
  20. let p = Person(); // 把window 作为 this 来构建实例
  21. let a = Animal(); // 报错

把类当成一个特殊的函数

  • 类的本质就是一个 函数 ;使用 typeof 检测返回 function
  • 类表示符有 prototype 属性,而这个原型也有一个constructor属性指向类自身
  • 可以使用 instanceof 检查构造函数原型是否存在于实例的原型链中
  • 类于立即调用函数表达式相似,类也可以立即实例化 ```javascript }class Person {}; console.log(typeof Person); // function

// 第二点 let person = new Person();

console.log(Person.prototype); // 指向 constructor 类的构造函数 console.log(Person === Person.prototype.constructor); //true

// 类也可以立即实例化 let p = new class Foo { constructor(x) { console.log(x) } }(“这是传入的参数”)

  1. <a name="PwbIn"></a>
  2. ## 3. 实例、原型、类成员
  3. <a name="sDvZm"></a>
  4. #### 实例成员
  5. - 创建出来的实例_相互独立,实例不会再原型上共享属性_
  6. ```javascript
  7. // ● 创建出来的实例相互独立,实例不会再原型上共享属性
  8. class Person {
  9. constructor() {
  10. this.name = new String('Jack');
  11. this.sayName = () => console.log(this.name)
  12. }
  13. }
  14. let p1 = new Person(),
  15. p2 = new Person();
  16. console.log(p1.name === p2.name) // false
  17. console.log(p1.sayName === p2.sayName) // false
  18. // 说明了不同实例 相互独立

原型方法于访问器

  • 定义属性或方法时,在类中 定义在 constructor()构造函数中,和 类本身 是不一样的
    • 定义在类本身, 都会出现在 类的原型上
    • 定义在构造函数中, 都会出现在不同的实例上
  • 不能直接在类中添加原始值, 不然会报错
  • 类方法等同于对象属性, 可以使用 字符串、符号、计算的值 作为键
    • 例如类的方法名 [symbolKey](){} ['computed' + 'Key']() {}
  • 类定义同样支持获取设置访问器, 设置访问器 getter | setter ```javascript class Person { constructor() { // 定义在实例上 this.locate = () => console.log(‘constructor’); }

// 定义在类的原型上 locate() { console.log(‘prototype’); } }

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

console.log(p1) console.log(p1.locate()) // constructor

console.log(Person.prototype.locate()) // prototype

// ● 不能直接在类中添加原始值

class Person { name: ‘Yellowsea’ } // 这样会报错

// ● 类定义同样支持获取设置访问器, 设置访问器 getter | setter class Person { … get name () { return this.name } set name (Val) { this.name = Val } }

  1. <a name="kkAUx"></a>
  2. #### 静态类方法
  3. 静态方法, 在类中使用 `static` 关键字定义静态方法或静态属性
  4. - 静态成员每一个类上只能有一个
  5. - 静态成员必须要使用`static`关键字作为前缀
  6. - 静态方法只能通过类本身来访问
  7. - 子类通过 `super().xxx()`调用静态方法
  8. - 在类外部添加数据成员 或者 在原型上定义数据成员
  9. ```javascript
  10. class Person {
  11. static sayHello() {
  12. console.log('Hello');
  13. }
  14. }
  15. // 通过 Person访问
  16. Person.sayHello(); // Hello
  17. // ● 在类外部添加数据成员 或者 在原型上定义数据成员
  18. class Person {
  19. sayName() {
  20. console.log(`${Person.name} is ${Person.greeting}`);
  21. }
  22. }
  23. Person.greeting = 'My name is';
  24. Person.name = 'Red';
  25. const p = new Person();
  26. p.sayName() // Person is My name is

4. 继承

ES6新增的原型继承机制,原理同样使用的是 原型链

继承基础

  • 继承使用的是 extends 关键字,可以继承任何拥有[[Construct]]和原型链的对象
  • 继承不仅可以继承类, 同样可以继承普通的构造函数
  1. class Vehicle {}
  2. class Bus extends Vehicle {}
  3. let b = new Bus();
  4. console.log(b instanceof Bus); // true
  5. console.log(b instanceof Vehicle); // true
  6. // 继承普通构造函数
  7. function Person() {}
  8. class Engineer extends Person {}
  9. console.log(new Engineer() instanceof Person); // true

构造函数和、super()

  • 派生类通过使用super关键字引入它们的原型。super关键字只能在派生类中使用
  • 在类构造函数中使用 super 可以调用父类构造函数
  • super的作用,简单来说在继承的类构造函数类中调用父类的构造函数
  • 在父类定义的静态方法,同样可以使用 super().xxx 进行调用 ```javascript

class Vehicle { constructor() { this.hasEngine = true; } }

class Bus extends Vehicle { constructor() { // 不要在调用 super() 之前引入 this,否则会报错。 super(); // 执行 super() 后, 相当于调用了 Vehicle 的构造函数。给属性赋值 console.log(this instanceof Vehicle); // true console.log(this); // Bus { hasEngine: true } // 此时的this指向的是Bus } }

new Bus();

```

super的几个注意事项

  • super 只能在派生类的构造函数和静态方法中使用
  • 不能单独使用super ,要么用它调用构造函数,要么用它引用静态方法
  • 调用super() 会调用父类的构造函数, 并将返回的实例赋值给 this
  • super 如果调用构造函数,如果需要给父类构造函数传参,需要手动设置传入参数
  • 如果没有定义类的构造函数, 在实例化派生类时会调用 super(), 而且传入的参数会传给父类的构造函数
  • 在类构造函数中, 不能在调用super() 之前引用 this
  • 如果派生类中显示定义了构造函数,则要么必须在其中调用 super(), 要么必须在其中返回一个对象