# 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
Copied!
constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
class Foo { constructor() { return Object.create(null); } } console.log(new Foo() instanceof Foo) // false
Copied!类的构造函数,不使用new是没法调用的,会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
class Foo { constructor() { return Object.create(null); } } Foo() //报错: Class constructor Foo cannot be invoked without 'new'
Copied!实例的属性除非显式定义在其本身(即定义在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
Copied!不存在变量提升:ES6不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。
new Foo(); // ReferenceError class Foo {}
Copied!Class表达式
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me { getClassName() { return Me.name; } }; //这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类
Copied!采用Class表达式,可以写出立即执行的Class。
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }('张三'); console.log(person.sayName()); // "张三" undefined
Copied!
静态方法
- 静态方法只有类本身和他的子类可以调用,实例化的对象不可以调用
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
Copied!
静态方法可以直接通过类调用,不要要生成实例,它的主要作用方便我们使用(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; } // ... };
Copied!
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() } }
Copied!类的 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
Copied!
- Extends 的继承目标
extends关键字后面可以跟多种类型的值。
class B extends A { }
Copied!
上面代码的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); } } ```
Copied!