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

244 lines
4.5 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 面向对象
> 封装, 继承, 多态
>
> 封装: 属性私有化
> 继承: extends
> 多态: js不会检查参数的类型,任何数据都可以作为参数传递
## es6 class类
> 替代了es5的构造函数和原型,使之代码结构上更加简洁
> 简单理解, 类是一个模板, 实例化就是进行个性化填充参数
```js
// 关键字class
class 类名{//类名大驼峰
属性名 = 属性值 //属性直接写, 不能用let
属性名 //也可以不用赋值
函数名() {} //函数写法, 直接写函数名, 不用写function
//ES6的语法, 最终系统会转成es5, 函数是挂载在原型上的, 只占用一份空间
}
```
```js
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方法修改
```js
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'))
```
如果想读还是正常读, 写需要调用方法
```js
get address(){
return thisl#address
}
setAddress(address){
this.#address = address
}
```
同理也可以改成直接写
```js
set address(address){
this.#address = address
}
```
### 方法
* 非静态方法
* 静态方法: static, 只能通过类访问, 不能通过实例访问
```js
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
```js
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 继承
> 子类没有的, 直接继承父类, 继承之后, 可以直接使用父类的属性和方法
```js
语法:
1. 定义父类
class 父类{属性和方法}
2. 定义子类要实现继承
class 子类 extends 父类{}
```
```js
// 如果我还有一个猫的类, 一个鸟的类, 每个类都要创建一遍, 太麻烦, 把公共的抽出来,变成一个公共动物类
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)
```
### 重写
> 当子类继承了父类后,子类拥有与父类同名的属性或者方法,同名的子类方法或者属性就叫做重写
> 属性被重写: 被覆盖掉
> 方法被重写: 不会被覆盖, 在不同的原型中
### 访问父类被重写的方法/构造函数
```js
super.父类被重写的方法名()
```
```js
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()
```
* 也可以在构造函数中调用父类的构造函数
```js
class Bird extends Animal {
color:string
constructor(name:string, age:number, color:string){
super(name, age)
this.color = color
}
}
```
### 重写构造函器
> 一般写在第一行
> 构造器可以重写, 必须保证父类的构造器先运行
> 父类构造器有参数, 就必须给super传入
> 子类有构造方法且使用this前必须使用super()
```js
class Bird extends Animal {
color:string
constructor(name:string, age:number, color:string){
super(name, age)
this.color = color
}
}
```