# 面向对象 > 封装, 继承, 多态 > > 封装: 属性私有化 > 继承: 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 } } ```