# class(类)

ES6引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。类在大部分功能其实可以通过ES6之前的语法实现。

# 类的定义

ES6 的类,完全可以看作构造函数的另一种写法。

class People {
  constructor(name,age) {
    this.name = name;
    this.age = age;
  }
  //属性放在constructor方法中
  toString() {
    return this.name+'今年'+this.age;
  }
}
typeof People   //function
  • constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

    class Foo {
      constructor() {
        return Object.create(null);
      }
    }
    
    console.log(new Foo() instanceof Foo)  // false
    
  • 类的构造函数,不使用new是没法调用的,会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

    class Foo {
      constructor() {
        return Object.create(null);
      }
    }
    
    Foo()  //报错: Class constructor Foo cannot be invoked without 'new'
    
  • 实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    }
    
    var point = new Point(2, 3);
    point.toString()                 // (2, 3)
    point.hasOwnProperty('x')        // true
    point.hasOwnProperty('y')        // true
    point.hasOwnProperty('toString') // false
    point.__proto__.hasOwnProperty('toString') // true
    
  • 不存在变量提升:ES6不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。

    new Foo(); // ReferenceError
    class Foo {}
    
  • Class表达式

    • 与函数一样,类也可以使用表达式的形式定义。

      const MyClass = class Me {
        getClassName() {
          return Me.name;
        }
      };
      //这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类
      
    • 采用Class表达式,可以写出立即执行的Class。

      let person = new class {
        constructor(name) {
          this.name = name;
        }
        sayName() {
          console.log(this.name);
        }
      }('张三');
      
      console.log(person.sayName()); // "张三"  undefined
      
  • 静态方法

    • 静态方法只有类本身和他的子类可以调用,实例化的对象不可以调用
class P{
	static pF(){
		console.log('父类静态方法');
	}
}
class C extends P{
}
var p1=new P();
P.pF();              //父类静态方法
C.pF();               //父类静态方法
p1.pF();             //p1.pF is not a function

静态方法可以直接通过类调用,不要要生成实例,它的主要作用方便我们使用(Math对象上的方法)

  • 私有方法
    * 私有方法是常见需求,但ES6不提供,只能通过变通方法模拟实现。
    使用symbol设置
const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass{
  // 公有方法
  foo(baz) {
    this[bar](baz);
  }
  // 私有方法
  [bar](baz) {
    return this[snaf] = baz;
  }
 // ...
};

bar和snaf都是Symbol值,导致第三方无法获取到它们,因此达到了私有方法和私有属性的效果。

# Class的继承

# 基本用法

  • Class之间可以通过extends关键字实现继承

    class ColorPoint extends Point {
      constructor(x, y, color) {
        super(x, y); // 调用父类的constructor(x, y)
        this.color = color;
      }
      toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString()
      }
    }
    
  • 类的 prototype 属性和 _proto_ 属性

每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

  • 子类的__proto__属性,表示构造函数的继承,总是指向父类。
  • 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
  • Extends 的继承目标

extends关键字后面可以跟多种类型的值。

class B extends A {

}

上面代码的A,只要是一个有prototype属性的函数,就能被B继承。由于函数都有prototype属性(除了Function.prototype函数),因此A可以是任意函数。

下面我们叙述三种特殊情况:

  • 第一种特殊情况,子类继承Object类。
 class A extends Object {

 }
 A.__proto__ === Object // true
 A.prototype.__proto__ === Object.prototype // true
 ```
 这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。

* 第二种特殊情况,不存在任何继承。

 ```javascript
 class A {

 }
 A.__proto__ === Function.prototype // true
 A.prototype.__proto__ === Object.prototype // true
 ```
 这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回一个空对象(即Object实例),所以A.prototype.\__proto__指向构造函数(Object)的prototype属性。

* 第三种特殊情况,子类继承null。

 ```javascript
 class A extends null {

 }
 A.\__proto__ === Function.prototype // true
 A.prototype.\__proto__ === undefined // true
 ```
 这种情况与第二种情况非常像。A也是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回的对象不继承任何方法,所以它的__proto__指向Function.prototype,即实质上执行了下面的代码。

 ```javascript
 class C extends null {
     constructor() { return Object.create(null); }
 }
 ```