devStandard/docs/learning/3-js/3-面向对象.md
2025-03-29 14:35:49 +08:00

4.5 KiB
Executable File
Raw Permalink Blame History

面向对象

封装, 继承, 多态

封装: 属性私有化 继承: extends 多态: js不会检查参数的类型,任何数据都可以作为参数传递

es6 class类

替代了es5的构造函数和原型,使之代码结构上更加简洁 简单理解, 类是一个模板, 实例化就是进行个性化填充参数

// 关键字class
class 类名{//类名大驼峰
	属性名 = 属性值  //属性直接写, 不能用let
	属性名       //也可以不用赋值
    函数名() {}  //函数写法, 直接写函数名, 不用写function
				//ES6的语法, 最终系统会转成es5, 函数是挂载在原型上的, 只占用一份空间
}
class Student {
	name = '张三'
	age = 33
	sayhello(){
		console.log('hello')
	}
}
let s = new Student()
console.log(s)
console.log(s.name)
s.sayhello()

new的原理

  1. 开辟内存空间(堆)
  2. 设置原型链
  3. 产生this对象
  4. 初始化: 执行构造函数(这里是执行构造器)
  5. 返回对象的内存地址

属性

  • 非静态属性
  • 静态属性: static, 只能通过类访问, 不能通过实例访问
  • 私有属性: #, 只能在类内部访问, 数据更安全
    • 通过getter方法读取, 通过setter方法修改
class Student {
	static name = '张三'
	age = 33
	#address = '长安'

	getAddress(){
		return this.#address
	}
	setAddress(address){
		this.#address = address
	}
}
let s = new Student()
console.log(s.name, s.age) // undefined 33
console.log(Student.name, Student.age) // 张三 undefined

console.log(s.#address) // must be declared in an enclosing class
console.log(s.getAddress())
console.log(s.setAddress('123'))

如果想读还是正常读, 写需要调用方法

get address(){
	return thisl#address
}

setAddress(address){
	this.#address = address
}

同理也可以改成直接写

set address(address){
	this.#address = address
}

方法

  • 非静态方法
  • 静态方法: static, 只能通过类访问, 不能通过实例访问
class Student {
	static sayhello(){
		console.log('hello')
	}
	saybye (){
		console.log('bye')
	}
}
let s = new Student()
s.sayhello() // not a function
s.saybye()
Student.sayhello()
Student.saybye() // not a function

constructor 构造器

构造器存在的价值: 初始化属性 只要实例化, 对象就会在产生后的第一件事, 调用constructor

class Student {
	constructor(name, age){
		console.log('立刻被执行')
		console.log(this) // this指向新实例的那个对象
		this.name = name
		this.age = age
	}
	sayhello(){
		console.log(`hello, my name is ${this.name}`)
	}
}
let s = new Student('张三', 18)
s.sayhello()

extends 继承

子类没有的, 直接继承父类, 继承之后, 可以直接使用父类的属性和方法

语法:
1. 定义父类
class 父类{属性和方法}
2. 定义子类要实现继承
class 子类  extends 父类{}
// 如果我还有一个猫的类, 一个鸟的类, 每个类都要创建一遍, 太麻烦, 把公共的抽出来,变成一个公共动物类

class Animal {
	name:string
	age:number
	
	constructor(name:string, age:number){
	  this.name = name
	  this.age = age
	  
	}
	
	say(){
	  console.log('动物在叫')
	}
}

class Dog extends Animal {
	food: string = "骨头"
	say(){
	  console.log('汪汪汪')
	}
}

class Cat  extends Animal {
	say(){
	  console.log("喵喵喵")
	}
}

const dog = new Dog("小白",1)
const cat = new Cat("小黑",1)

重写

当子类继承了父类后,子类拥有与父类同名的属性或者方法,同名的子类方法或者属性就叫做重写 属性被重写: 被覆盖掉 方法被重写: 不会被覆盖, 在不同的原型中

访问父类被重写的方法/构造函数

super.父类被重写的方法名()
class Father {
	name = 'father'
	say(){
		console.log("I'm father")
	}
}

class Son extends Father {
	name = 'son'
	say(){
		super.say() // 调用父类的方法
		console.log("I'm son")
	}
}

let s = new Son()
s.say()
  • 也可以在构造函数中调用父类的构造函数
class Bird extends Animal {
	color:string
	constructor(name:string, age:number, color:string){
		super(name, age)
		this.color = color
	}
}

重写构造函器

一般写在第一行 构造器可以重写, 必须保证父类的构造器先运行 父类构造器有参数, 就必须给super传入 子类有构造方法且使用this前必须使用super()

class Bird extends Animal {
	color:string
	constructor(name:string, age:number, color:string){
		super(name, age)
		this.color = color
	}
}