同class类已经写在JS高级第一天:https://www.jianshu.com/writer#/notebooks/49678790/notes/86773079/preview
一、创建类与实例化对象
1.创建类并new一个实例对象
(1)class 类名 { };const 变量名 = new 类名()
(2)类名 首字母大写。类是 构造函数 的语法糖。方法和方法之间 不能使用逗号分隔。
(3)类中包括 、
、
、
、
。
(4)构造函数接收实例对象传递过来的形参,默认返回 实例对象(即this)。
(5)构造函数中的this指向 。实例方法中的this指向 方法的调用者。
(6)构造函数可以不写,new 实例对象时会自动生成并 调用。
(7)实例属性和实例方法通过 实例对象 调用。静态属性和静态方法通过 类本身 调用。
(8)实例属性挂载到 上;实例方法挂载到
。
class Person {
// 1.构造函数
constructor(name, age) {
// 2.实例属性。可以通过实例对象访问。
this.name = name;
this.age = age;
}
// 3.实例方法。挂载到原型对象上。可以通过实例对象访问。
say() {
console.log("我是实例方法,我能说");
}
// 4.静态属性或类属性。可以通过类访问。
static info = "我是静态属性";
// 5.静态方法。可以通过类访问。
static jump() {
console.log("我是静态方法,我能跳");
}
}
// 创建对象
const p = new Person("张三", 18);
// 调用访问实例属性并调用实例方法
console.log(p.name);
p.say();
// 访问静态属性,调用静态方法
console.log(Person.info);
Person.jump();
2.extends与super关键字
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello(): void {
console.log("动物叫!");
}
}
class Dog extends Animal {
hobby: string; // 子类特有的实例属性
constructor(name: string, age: number, hobby: string) {
super(name, age);
this.hobby = hobby;
}
// 子类重写父类方法
sayHello(): void {
console.log("汪汪汪!");
}
}
const dog = new Dog("旺财", 5, "看门");
dog.sayHello() // 汪汪汪!
(9)子类可以通过extends关键字继承父类的属性和方法。继承中,类调用方法时,先从子类中查找并调用。如果子类没有该方法,就去父类中查找并调用。(就近原则)
(10)super关键字在 子类构造函数 中当 函数 使用,相当于父类的,其内部的this指向
。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
// 1.这里的super相当于A的构造函数,内部this指向B的实例对象
// 2.因此super()在这里相当于A.prototype.constructor.call(this)。
super();
}
}
new A() // A
new B() // B
(11)super关键字在 子类的构造函数 或者 实例方法 中当 对象 使用,它指向父类的,其内部的this指向
。它可以访问父类的实例方法,但不能访问父类的实例属性。(原因见第8条)
class A {
constructor(x) {
this.x = 1;
}
y() {
return 2;
}
}
A.prototype.z = 3;
class B extends A {
constructor() {
super();
console.log(super.x); // undefined, B不能访问A的实例属性,因为A的实例属性挂载到A的实例对象上,而不是A的原型对象上
console.log(super.y()); // 2, B可以访问A的实例方法,因为A的实例方法挂载到A的原型对象上
console.log(super.z); // 3, B可以访问A原型对象上的属性
}
}
let bb = new B();
- 下面代码中,super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的 是super.print.call(this)。
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
特殊情况:如果通过super对某个属性赋值,这时
- 下面代码中,通过super对x属性赋值,这里的 super相当于子类B中的this。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3; // 这里的super相当于this
console.log(super.x); // undefined 这里的super相当于父类的原型对象,但是父类的x属性挂载到了父类的实例对象上,而非原型对象上
console.log(this.x); // 3
}
}
let b = new B();
(12)super关键字在 子类的静态方法 中当 对象 使用,它指向 ,其内部的this指向
而非子类实例。
(13)子类必须先在构造函数中 调用super()方法之后才能使用this。
(14)类中的构造器函数constructor 不是必须 写的,当需要对实例对象进行初始化操作时才写,如添加属性。
(15)如果 B类继承了A类,且B类中写了构造器函数,则B类构造器函数内,必须写 super()。
类的继承可参考:https://juejin.cn/post/7000891889465425957?searchId=202401091029105CB33A9EDB5C1F69E7B2#heading-18
3.其他
(1)类中的 自定义属性,都放在了类的 实例对象 上。
class MyForm extends React.Component {
// React的state数据简写方法就是用的这个。
state = {input: '我是普通input表单',}
}
const form = new Form();
form.hasOwnProperty("state") // true
(2)类中的 自定义方法,都放在了类的 原型对象 上,实例对象的 __proto__ 属性也指向类的原型对象。
class MyForm extends React.Component {
//这个方法被放到了类的原型对象上
handler () { }
}
const form = new Form();
form.hasOwnProperty("handler") // false
form.__proto__.hasOwnProperty("handler") // true
(3)构造函数中的this指向实例化对象;普通方法中的this指向方法的调用者。
var that;//1.声明全局变量,用来接收类的实例化对象。
class Star {
constructor(x, y) {
that = this;//2.将实例化对象赋值给全局变量that
this.x = x;
this.y = y;
this.sing(); //类中调用其他方法必须加this
this.btn = document.querySelector('button');//类中获取DOM对象要加this
this.btn.addEventListener('click',this.sing);//类中使用DOM对象要加this
}
sing() {
console.log(this);//普通方法中的this指向调用者--按钮btn
console.log(that.x + '会唱' + that.y);//3.类中使用公共属性必须加this(全局变量that)
}
}
var WuLei = new Star('吴磊', '九妹');
(4)向 类自身 添加属性和方法
① 用 static 关键字修饰属性名或方法名。
② 用 函数表达式 向类添加方法。
class MyForm extends React.Component {
static a = 1; // 这个属性被放到类自身上
static fun() {}; // 这个方法被放到类自身上
handler = function(){}; // 这个方法被放到类自身上
}
(5)类中使用公共属性或者调用类中的其他方法都必须加this。
(6)Object.getPrototypeOf方法可以用来从子类上获取父类。Object.getPrototypeOf(子类),返回值是父类。
二、抽象类
1. 抽象类与抽象方法
(1)以 abstract 开头的类叫做 抽象类;在抽象类中,以 abstract 开头的方法是 抽象方法。
(2)抽象类不能被 实例化,只能被当做父类继承。
(3)抽象方法不包含具体实现,子类 必须 对抽象方法进行 重写。
// 以abstract开头的类是抽象类。抽象类不能被实例化。
abstract class Job {
name: string;
constructor(name: string) {
this.name = name;
}
// 以abstract开头的方法是抽象方法
// 抽象类中的抽象方法不包含具体实现,子类必须对抽象方法进行重写
abstract work(): void;
}
class Teacher extends Job {
constructor(name: string) {
super(name)
}
// 子类必须对抽象方法进行重写
work(): void {
console.log("老师正在教课!");
}
}
const teacher = new Teacher("小王");
teacher.work()
(4)TypeScript中,用interface关键字定义的接口中,属性和方法都是抽象的。
三、属性的封装(TypeScript)
1. public、private、protected
(1)类中,被 public 修饰的 属性 可以在 任意位置 被访问(或修改),且类中的属性默认被public修饰。
(2)类中,被 private 修饰的 属性 只能在 当前类 的内部被访问(或修改)。
(3)类中,被 protected 修饰的 属性 只能在 当前类和子类 的内部被访问(或修改)。
class A {
public name: string; // 公有属性,默认就是public
private age: number; // 私有属性,只有类的内部可以访问
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const a1 = new A("张三", 20);
a1.name = "李四"; // 可以访问公有属性name
// a1.age = 30; // 报错,不能在类的外部访问私有属性age
2. 如何访问private私有属性
(1)可以在类的内部 自定义 getter方法和setter方法,来访问和修改private私有属性。
class A {
private age: number; // 私有属性
constructor(age: number) {
this.age = age;
}
// 获取私有属性age的值
getAge() {
return this.age;
}
// 设置私有属性age的值
setAge(age: number) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new Error("年龄不合法");
}
}
}
const a1 = new A(20);
a1.setAge(30); // 通过公有方法设置私有属性age的值
(2)TypeScript中用 get、set关键字来优化getter方法和setter方法。
(3)get、set关键字修饰的方法名不能与属性名重复。
(4)格式上:直接访问私有属性,而不是调用方法。
class A { private age: number; // 私有属性
constructor(age: number) {
this.age = age;
}
// 获取私有属性age的值,名称不能与age重复
get _age() {
return this.age;
}
// 设置私有属性age的值,名称不能与age重复
set _age(age: number) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new Error("年龄不合法");
}
}
}
const a1 = new A(20);
a1._age = 30; // 可以通过getter和setter访问私有属性age
3. 属性声明的简写
// 简写
class B {
constructor(public name: string, private age: number) { }
}
// 等价于这种写法
class B {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}