前言
该文章记录一些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.封装--主要用来保障对象中数据的安全 (很少使用--了解)
- 对象就是一个用来存储不同属性的容器
- 对象不仅存储数据,还要负责数据的安全
- 直接添加到对象中的属性,并不安全,可以被随意修改,类型、值都不会审核
- 如何确保数据的安全:
- 数据私有化
- 将需要保护的数据设置为私有,只能在类的内部使用
- 提供setter和getter方法来对属性操作
- 可以控制属性的读写权限
- 修改属性值时可以对新数据进行条件验证
- 数据私有化
- 封装的方法
- 需要私有化的属性加上#,例如age --- #age
- 通过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. 类的实例对象的结构
- 实例对象中存储属性的区域有两个
-
对象自身
- 直接通过对象所添加的属性,位于对象自身中,对象添加属性/方法通过
=赋值 - 在类中,方法使用简写方式 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: ƒ} - 直接通过对象所添加的属性,位于对象自身中,对象添加属性/方法通过
-
对象中的原型对象(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个步骤
- 新创建一个普通的空对象
- 将构造函数的prototype属性设置成为新对象的原型对象(
obj.__proto__ = 类.prototype) - 构造函数中的this指向新对象,则this.属性全部都存在新对象中了
- 判断返回值,如果return有返回值,且是一个非原始值,则new运算符返回的就是该值;如果没有返回值或者返回值是原始值,则new运算符返回的就是新对象