一、类:构造函数的语法糖
1. 传统的构造函数的问题
- 属性和原型方法定义分离,降低了可读性
- 原型成员可以被枚举
- 默认情况下,构造函数仍然可以被当作普通函数使用
2. 类的特点
- 类声明不会被提升,与 let 和 const 一样,存在暂时性死区
- 类中的所有代码均在严格模式下执行
- 类的所有方法都是不可枚举的
- 类的所有方法都无法被当作构造函数使用
- 类的构造器必须使用 new 来调用
//面向对象中,将 下面对一个对象的所有成员的定义,统称为类
//构造函数 构造器
function Animal(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
//定义实例方法(原型方法)
Animal.prototype.print = function () {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
const a = new Animal("狗", "旺财", 3, "男");
a.print();
for (const prop in a) {
console.log(prop)
}
//将上面构造函数用class改写
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
print() {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
}
const a = new Animal("狗", "旺财", 3, "男");
a.print();
for (const prop in a) {
console.log(prop)
}
二、类的其他书写方式
- 可计算的成员名
- getter和setter
- Object.defineProperty 可定义某个对象成员属性的读取和设置
- 使用getter和setter控制的属性,不在原型上
- 静态成员
- 构造函数本身的成员
- 使用static关键字定义的成员即静态成员
- 字段初始化器(ES7)
- 注意:
- 使用static的字段初始化器,添加的是静态成员
- 没有使用static的字段初始化器,添加的成员位于对象上
- 箭头函数在字段初始化器位置上,指向当前对象
- 注意:
- 类表达式
- [扩展]装饰器(ES7)(Decorator)
- 横切关注点
- 装饰器的本质是一个函数
const printName = "print";
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
[printName]() {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
}
const a = new Animal("狗", "旺财", 3, "男");
a[printName]();
const printName = "print";
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
//创建一个age属性,并给它加上getter,读取该属性时,会运行该函数
get age() {
return this._age + "岁";
}
//创建一个age属性,并给它加上setter,给该属性赋值时,会运行该函数
set age(age) {
if (typeof age !== "number") {
throw new TypeError("age property must be a number");
}
if (age < 0) {
age = 0;
}
else if (age > 1000) {
age = 1000;
}
this._age = age;
}
[printName]() {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
}
var a = new Animal("狗", "旺财", 3, "男");
class Chess {
constructor(name) {
this.name = name;
}
static width = 50;
static height = 50;
static method() {}
}
console.log(Chess.width)
console.log(Chess.height)
Chess.method();
//字段初始化器(ES7)
class Test {
static a = 1;
b = 2; //相当于在constructor()中写this.b=2
c = 3;
constructor() {
this.d = this.b + this.c;
}
}
const t = new Test();
console.log(t)
//字段初始化器:箭头函数在字段初始化器位置上,指向当前对象
//可以绑定this指向,但是会造成每一个通过构造函数创建的函数都包含各自的箭头函数,浪费内存空间
class Test {
constructor() {
this.a = 123;
}
print = () => {
console.log(this.a)
}
}
const t1 = new Test();
const t2 = new Test();
console.log(t1.print === t2.print) //false
//类表达式
const A = class { //匿名类,类表达式
a = 1;
b = 2;
}
const a = new A();
console.log(a)
三、类的继承
如果两个类A和B,如果可以描述为:B 是 A,则,A和B形成继承关系
如果B是A,则:
- B继承自A
- A派生B
- B是A的子类
- A是B的父类
如果A是B的父类,则B会自动拥有A中的所有实例成员。
新的关键字:
- extends:继承,用于类的定义
- super
- 直接当作函数调用,表示父类构造函数
- 如果当作对象使用,则表示父类的原型
注意:ES6要求,如果定义了constructor,并且该类是子类,则必须在constructor的第一行手动调用父类的构造函数
如果子类不写constructor,则会有默认的构造器,该构造器需要的参数和父类一致,并且自动调用父类构造器
//原本继承的方法
function Animal(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
Animal.prototype.print = function () {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
function Dog(name, age, sex) {
//借用父类的构造函数
Animal.call(this, "犬类", name, age, sex);
}
Object.setPrototypeOf(Dog.prototype, Animal.prototype);
const d = new Dog("旺财", 3, "公");
d.print();
console.log(d);
//类的继承
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
print() {
console.log(`【种类】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
console.log(`【性别】:${this.sex}`);
}
jiao(){
throw new Error("动物怎么叫的?");
}
}
class Dog extends Animal {
constructor(name, age, sex) {
super("犬类", name, age, sex);
// 子类特有的属性
this.loves = "吃骨头";
}
print(){
//调用父类的print
super.print();
//自己特有的代码
console.log(`【爱好】:${this.loves}`);
}
//同名方法,会覆盖父类
jiao(){
console.log("旺旺!");
}
}
const d = new Dog("旺财", 3, "公");
d.print();
console.log(d)
d.jiao();