JavaScript 对象系列:
第五篇
ES6 中 提供的类 (class)其实是 ES5 之前构造函数、原型、原型链的语法糖。
定义方式
class Person {} // 类的申明
let Person = class {} // 类的表达式(很少使用)
保留 ES5 的特性
class Person {}
console.log(Person.prototype) // {} 函数原型
console.log(Person.prototype.__proto__) // [Object: null prototype] {} 顶层原型
console.log(Person.prototype.constructor) // [class Person]
console.log(typeof Person) // function 本质上还是函数
let p = new Person()
console.log(p.__proto__ === Person.prototype) // true
类的传参
// 类的申明
class Person {
// 类的构造方法(接受参数)
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let p = new Person("copyer", 18);
注意:一个类里面只有一个构造方法(constructor),多了就会报错。
当使用 new 关键词 的时候,就会调用类的构造方法。
类的方法(普通实例方法)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 类的方法
play() {
console.log(this.name + " playing~");
}
}
let p = new Person("copyer", 18);
类的方法是放在类的原型上的,所以能实现方法共享。
如何证明呢?
// 使用Object提供的方法,来获取对象上的属性
console.log(Object.getOwnPropertyDescriptors(Person.prototype))
/**
{
constructor: {
value: [class Person],
writable: true,
enumerable: false,
configurable: true
},
play: { // 说明存在Person的函数原型上
value: [Function: play],
writable: true,
enumerable: false,
configurable: true
}
}
*/
类的访问器
class Person {
constructor() {
this._name = "copyer";
}
// 类的访问器
get name() {
console.log("获取值之前的拦截");
return this._name;
}
set name(newValue) {
console.log("设置属性值之前的拦截");
this._name = newValue;
}
}
可以实例化,验证一下
let p = new Person()
console.log(p.name) // 获取值之前的拦截 copyer
p.name = 'james' // 设置属性值之前的拦截
console.log(p.name) // 获取值之前的拦截 james
作用:进行获取和设置的拦截。
类的静态方法
class Person {
constructor() {
this._name = "copyer";
}
// 类的静态方法
static createPerson() {
return new Person('james', 18)
}
}
通过关键词 static 来定义
Person.createPerson() // 只能构造函数.的形式调用
类的继承
class 中 类的继承很简单, 使用 extends 即可。
// 父类
class Person {
}
// 子类继承父类: extends
class Student extends Person {
}
继承属性应该怎么写呢
// 父类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 子类继承父类: extends
class Student extends Person {
constructor(name, age, address) {
this.name = name;
this.age = age;
this.address = address;
}
}
var s = new Student("copyer", 12, "cq");
可以这样写吗?答案是不可以的
报错信息:
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
简单理解就是:在使用 this 之前,应该在构造函数中先使用 super 关键词。
解释:
JavaScript引擎在解析子类的时候,是有要求的。如果存在继承关系的话,如果使用了this,就必须先使用 super,才能正常的解析。
总结:
在子类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数 。
super关键词
上面提及到了 super ,那么应该怎么使用呢?
使用方式一:调用父类的构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
constructor(name, age, address) {
super(name, age) // 类似于借用构造函数继承 Person.call(this, name, age)
this.address = address;
}
}
var s = new Student("copyer", 12, "cq");
子类中的构造函数调用 super 方法,就是借用了父类中的构造函数中逻辑
方式二:重写类方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 类的方法
play() {
console.log("父类的逻辑");
}
}
class Student extends Person {
constructor(name, age, address) {
super(name, age);
this.address = address;
}
// 重写父类的方法
play() {
super.play(); // 需求:先执行父类的逻辑,然后在执行子类的逻辑
console.log("子类的逻辑");
}
}
方式二:重写静态方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 类的静态方法
static play() {
console.log("父类的逻辑");
}
}
class Student extends Person {
constructor(name, age, address) {
super(name, age);
this.address = address;
}
// 重写类的静态方法
static play() {
super.play(); // 需求:先执行父类的逻辑
console.log("子类的逻辑");
}
}
Student.play()