类是用于创建对象的模板,简单的理解,类就是通用事务的抽象
类的所有实例对象都是从同一个原型对象继承属性,因此,原型对象是类的核心。只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性就会指向函数的原型对象,同时prototype会有一个constructor属性,会指向prototype属性所在函数的指针
构造函数是用来初始化新创建的对象的,使用关键字new来调用构造函数,就能自动创建一个新对象,也就是能够创建一个实例,构造函数是类的外在体现
实例就是类的具体化,类的每一个实例都是独立的对象。
构造函数,原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针
js主要通过原型链实现继承,原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型来实现的。
在es6之前,js创建类会比较繁琐,可以通过工厂模式,构造函数模式以及原型模式进行创建,同时,在实现继承的方式上,可以通过原型链继承,寄生式继承,寄生组合式继承等形式(详情请看红宝书,理解设计思路为主)。
class
在es6,出现了一个class语法糖,使得类的定义变得简单很多
1.class只是一个语法糖,两端代码是相等的
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return '(' + this.x + ',' + this.y + ')';
}
class Point {
constructor(x, y) { // 将实例传入的数据进行this绑定
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ',' + this.y + ')';
}
}
2.constructor
方法是类的构造函数,是一个默认方法,通过 new
命令创建对象实例时,自动调用该方法。一个类必须有 constructor
方法,如果没有显式定义,一个默认的 consructor
方法会被默认添加,一个类只能拥有一个constructor方法
3.在子类的 constructor
中必须调用 super
方法,因为子类没有自己的 this
对象,而是继承父类的 this
对象,然后对其进行加工,而 super
就代表了父类的构造函数。super
虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super
内部的 this
指的是 B,因此 super()
在这里相当于 ```A.prototype.constructor.call(this, props)``。
class A {}
class B extends A { // extends在类声明或者类表达式中用于创建一个类作为另一个类的子类
constructor() {
super(); // ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错,它用来调用对象的父对象上的函数。
}
}
4.通过 super
调用父类的方法时,super
会绑定子类的 this
,相当于方法是用的父类的方法,但是在遇到this的时候还是就近选择了子类的this,所以此时父类的this在调用方法的时候是没有用的
一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在ECMAScript中实现混合
static
用来定义一个类的一个静态方法,调用静态方法不需要实例化该类,但同时不能通过类实例调用静态方法。所以一般静态方法更多的是用在为应用程序创建工具函数上。
Species
species 方式允许在派生数组类 MyArray
中返回Array对象,覆盖默认的构造函数
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray);
// false
console.log(mapped instanceof Array);
// true
类私有域
类属性在默认情况下是公共的,可以被外部检测或者修改,ES2020实验草案中,增加了定义私有类字段的能力,写法是使用#作为前缀,静态变量只能被静态方法调用的限制仍然成立。
class ClassWithPrivateField {
#privateField
constructor() {
this.#privateField = 42
this.#randomField = 666 // Syntax error
}
}
const instance = new ClassWithPrivateField()
instance.#privateField === 42 // Syntax error
类公有域
静态公有字段和实例公有字段都是可编辑的,可遍历的,可配置的。它们本身不同于私有对应值(private counterparts)的是,它们参与原型的继承
class ClassWithStaticField {
static baseStaticField = 'base static field';
static anotherBaseStaticField = this.baseStaticField;
static baseStaticMethod() { return 'base static method output'; }
}
class SubClassWithStaticField extends ClassWithStaticField {
static subStaticField = super.baseStaticMethod();
}
console.log(ClassWithStaticField.anotherBaseStaticField);
// 预期输出值: "base static field"
console.log(SubClassWithStaticField.subStaticField);
// 预期输出值: "base static method output"