JS类-对象

192 阅读8分钟

前言

该文章记录一些JS对象/类的一些基本知识,后续将会逐步增加,所有内容均从网上整理而来,加上自己得理解做一个整合,方便工作中使用。

对象的分类

  • 内建对象
    • 由ES标准所定义的对象
    • 比如 Object Function String ···
  • 宿主对象
    • 由浏览器提供的对象
    • BOM、 DOM
  • 自定义对象

1.创建对象

  • 使用Object字面量创建对象
    • 无法区分不同类型的对象
    • 不方便批量创建对象
  • 通过类(class)来解决Object创建对象的问题
    • 类是对象的模板,可以将对象中的属性和方法直接定义在类中,然后通过类直接创建对象,该对象则是该类的实例对象
    • instanceof 可以检测对象是否是某个类创建(对象 instanceof 类 返回true/false)
    • 语法:
      • class 类名{} //类名必须使用大驼峰,使用频率高
      • const 类名 = class {}
  • 案例
//创建一个Person类
class Person{
//类的代码块中,默认就是严格模式
   // 类的实例属性(该类所有的实例对象的公共属性)
   name="胡歌"
   age=18
   // 类的静态属性(只属于类的属性,只能通过类来调用)
   static arm = 2       //需要使用static声明,且只能通过类调用 Person.arm
   static lifetime = 80
   
   //实例对象的方法
   sayHello(){
       console.log('我是实例对象的方法',this)
   }

   //类的方法(静态方法) 需要static声明,只能通过类调用
   static sayHello(){
       console.log('我是类的方法',this)
   }
}
const man = new Person()   // Person类的实例对象
const women = new Person() // Person类的实例对象
console.log(man)        //输出 Person {name: '胡歌', age: 18}
console.log(Person.arm) //输出 2

//作为对象的方法被调用时,方法中的this指向调用该方法的对象
man.sayHello()     //实例对象的方法中this指向实例对象
Person.sayHello()  //类的方法中this指向类本身

2.构造函数

  • 构造函数---函数名固定---constructor
  • 在调用类创建实例对象时,构造函数被自动执行
  • 构造函数是实例对象的方法---this指向实例对象
//创建一个Person类,每个实例对象拥有相同属性,但属性值不同
class Person{
    //构造函数可以给实例对象添加属性,并赋值传来的值
    constructor(name,age){
        console.log('我是构造函数',this) //构造函数this执行正在创建的实例对象,所以可以传参赋值
        this.name=name //给当前实例对象添加一个name属性,并且赋值
        this.age=age   //给当前实例对象添加一个age属性,并且赋值
    }  
}
const man = new Person('李白',18)   //此时构造函数自动执行,给当前实例对象的属性赋值
const women = new Person('杜甫',48) //此时构造函数自动执行,给当前实例对象的属性赋值
console.log(man)    //输出Person {name: '李白', age: 18}
console.log(women)  //输出Person {name: '杜甫', age: 48}

3.封装--主要用来保障对象中数据的安全 (很少使用--了解)

  • 对象就是一个用来存储不同属性的容器
  • 对象不仅存储数据,还要负责数据的安全
  • 直接添加到对象中的属性,并不安全,可以被随意修改,类型、值都不会审核
  • 如何确保数据的安全:
    1. 数据私有化
      • 将需要保护的数据设置为私有,只能在类的内部使用
    2. 提供setter和getter方法来对属性操作
      • 可以控制属性的读写权限
      • 修改属性值时可以对新数据进行条件验证
  • 封装的方法
    1. 需要私有化的属性加上#,例如age --- #age
    2. 通过getter和setter方法操作属性
    get 属性名(){ return this.#属性名 } set 属性名(参数){ this.#属性名 = 参数 }
//创建一个Person类
class Person{
 #age   //属性私有化
 constructor(name,age){
   this.name=name
   this.#age=age
 }
 // 外部获取私有化属性方法
 get age(){
   return this.#age
 }
 // 外部设置私有化属性方法
 set age(age){
   // 可以先判断传进来的参数age是否符合要求,然后再赋值
   this.#age=age
 }
}
const man = new Person("胡歌",18)
man.age=22           //修改私有属性,如果没有set设置,这样直接修改是会报错的
console.log(man.age) //输出22

4. 多态 --- 灵活性

  • JS不会检查函数的参数类型,任何数据都能作为参数传递,具有灵活性

5. 继承 --- 扩展性

  • 类的继承通过关键字extends完成继承
  • 继承发生时,被继承的类称为父类,继承的类称为子类
//动物类
class Animal{
    constructor(name){
        this.name=name
    }
    scream(){
        console.log('动物会叫')
    }
}
//创建狗类
class Dog extends Animal{
}
const dog = new Dog('旺财')
//Dog类继承了Animal父类的name属性和scream方法
dog.scream()      //输出 '动物会叫'
console.log(dog)  //输出 Dog {name: '旺财'}
  • 子类可以通过创建同名方法重写父类方法
//父类
class Animal{
    constructor(name){
        this.name=name
    }
    scream(){
        console.log('动物会叫')
    }
}
//子类
class Dog extends Animal{
    scream(){
        console.log('狗是汪汪叫')
    }
}
const dog = new Dog('旺财')
dog.scream() //输出 '狗是汪汪叫'
  • 子类设置单独的属性--重写构造函数
//父类
class Animal{
    constructor(name){
        this.name=name
    }
    shot(){
        console.log('我是父类方法')
    }
}
//子类
class Dog extends Animal{
    //重写构造函数,带上父类的所有属性,加上自己的属性
    constructor(name,type){
        //子类构造函数第一行必须调用父类构造函数super()
        super(name)
        //子类属性
        this.type=type
    }
    
    //子类方法调用父类的方法 super.方法()
    shot(){
        super.shot() //调用父类的方法
        console.log('我是子类方法')
    }
}
const dog = new Dog('旺财','土狗')
console.log(dog) //输出 Dog {name: '旺财', type: '土狗'}
dog.shot() // 先输出-'我是父类方法',在输出-'我是子类方法' 

6. 类的实例对象的结构

  • 实例对象中存储属性的区域有两个
    1. 对象自身

      • 直接通过对象所添加的属性,位于对象自身中,对象添加属性/方法通过=赋值
      • 在类中,方法使用简写方式 say(){···},该方法存储在原型对象上
      • 在类中,方法使用=赋值,shot=()=>{···},该方法则存储在实例对象自身中
      class Person{
        name='胡歌'
        say(){
          console.log(123)
        }
        shot = ()=>{
          console.log(123)
        }
      }
      const obj = new Person()
      console.log(obj) //输出 Person {name: '胡歌', shot: ƒ}  
      //say存储在原型对象,shot在实例对象自身
      
      //直接通过对象所添加的属性,位于对象自身中,因为对象添加属性/方法必须用=赋值
      obj.age=18
      obj.call = ()=>console.log(666)
      console.log(obj) //输出 Person {name: '胡歌', age: 18, shot: ƒ, call: ƒ}
      
    2. 对象中的原型对象(prototype)

      • 对象中有一个属性__proto__,用来存储原型对象
      • 原型对象也为对象存储一些内容,当调用对象属性时,优先在对象上查找,对象没有则会在原型对象上查找
      • 访问一个对象的原型对象:
        • 对象.__proto__
        • Object.getPrototypeOf(对象)
      • 原型对象中数据:
        • 对象存放在原型对象中的属性/方法
        • 对象的构造函数(constructor)/obj.__proto__.constructor===类
        • 原型对象自身的原型对象,因此构成原型链,根据对象的复杂程度不同,原型链长度也不同
      • 原型链:读取对象属性/方法时,优先从自身查找,若没有,然后根据原型链查找,直到找到Object对象的原型(null),还没有则返回undefined

      • 同类的所有实例对象,包括子类的所有实例对象共用一个原型对象
      //创建一个父类
      class Person{
        name="胡歌"
      }
      const huge = new Person()
      const huojianhua = new Person()
      console.log(huge.__proto__ === huojianhua.__proto__) // 返回true
      //创建一个子类
      class Women extends Person{
      
      }
      const chenyao = new Person()
      console.log(chenyao.__proto__ === huge.__proto__) //返回true
      
      • 原型对象的作用
        • 原型对象就相当于一个公共区域,可以被该类的所有实例对象以及其所有子类的实例对象访问,这样该类共用的属性/方法就存在原型对象中,也只需存储一次,不需要每个实例对象单独存储
        • JS中的继承就是通过原型来实现,当继承时,子类的原型就是父类的实例

  • 修改原型对象(尽量不要修改)

    • 千万不要通过类的实例对象去修改原型
    • 通过类的prototype属性
      • 类的prototype属性-就是-实例对象的原型对象
      class Person{
       name="胡歌"
      }
      const huge = new Person()
      
      //类的prototype属性-就是-实例对象的原型对象
      console.log(huge.__proto__ === Person.prototype) //返回true
      
      //修改Person类的原型,添加sayhello方法,则所有实例对象都可使用该方法
      Person.prototype.sayhello=()=>{
       console.log(123)
      }
      huge.sayhello() //输出123   
      
      
  • 检测对象属性

    • Object.hasOwn(对象,属性名)

    检测一个对象自身是否存在某个属性(原型对象上的不算),返回true/false

    • in

    属性名 in 对象 检测属性是否在对象身上或者对象的原型对象上,返回true/false

    • instanceof 检测一个对象是否是一个类的实例

    对象 instanceof 类 检测该对象是否是该类的实例对象,子类实例对象也属于子类和父类

    //创建一个Person类
    class Person{
      name="胡歌"
    }
    //子类
    class Women extends Person{
    
    }
    const chenyao = new Women()
    console.log(chenyao instanceof Women)  //true
    console.log(chenyao instanceof Person) //true
    console.log(chenyao instanceof Object) //true
    
    //instanceof检查的是对象的原型链上是否有该类实例
    

7.new关键字

new运算符是创建对象时需要的运算符,当new去调用一个函数时,有4个步骤

  1. 新创建一个普通的空对象
  2. 将构造函数的prototype属性设置成为新对象的原型对象(obj.__proto__ = 类.prototype)
  3. 构造函数中的this指向新对象,则this.属性全部都存在新对象中了
  4. 判断返回值,如果return有返回值,且是一个非原始值,则new运算符返回的就是该值;如果没有返回值或者返回值是原始值,则new运算符返回的就是新对象