2

【笔记】JS面向对象

 1 year ago
source link: https://blog.loli.fj.cn/2023/05/12/JS%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【笔记】JS面向对象

2023-05-122023-05-13

JS面向对象学习笔记

  • 类名首字母大写
class 类名 {
...
}

定义构造方法

  • constructor()方法为构造方法,在创建对象时会自动执行
    • 构造方法不需要function关键字修饰
    • 构造方法通过形参为对象初始化属性值
    • 如果不写构造方法,类中会有默认的构造方法
  • 必须通过this关键字调用当前类实例的属性或方法
class 类名 {
constructor(形参) {
this.属性名 = 形参
}
}
  • 创建对象必须在定义类之后
class 类名 {
constructor(形参) {
this.属性名 = 形参
}
}

let 对象名 = new 类名();
  • 类中定义方法时不需要使用function关键字修饰
  • 类中定义的多个方法不需要使用,分隔
class 类名 {
方法名(形参) {
...
}
}
  • 通过extends关键字继承父类
  • 继承父类后可以直接通过子类调用父类的属性和方法
class 父类名 {
...
}

class 子类名 extends 父类名 {
...
}

子类调用父类方法

调用父类构造方法
  • 子类通过super()调用父类构造方法
class 父类名 {
constructor(形参) {
this.属性名 = 形参
}
}

class 子类名 extends 父类名 {
constructor(形参) {
super(形参)
}
}
同时将形参传递给子类构造和父类构造
  • 同时将构造方法的形参传递给子类构造方法和父类构造方法时,必须先调用父类构造方法再调用子类构造方法,super关键字需要在this关键字之前
class 父类名 {
constructor(形参) {
this.属性名 = 形参
}
}

class 子类名 extends 父类名 {
constructor(形参) {
super(形参)
this.属性名 = 形参
}
}
调用父类普通方法
class 父类名 {
父类方法名() {
...
}
}

class 子类名 extends 父类名 {
子类方法名() {
super.父类方法名()
}
}

方法的重写

  • 子类重写父类同名方法
class 父类名 {
父类方法名() {
...
}
}

class 子类名 extends 父类名 {
父类方法名() {
...
}
}

ES6之前

通过 new Object() 创建对象

let 对象名 = new Object();

通过对象字面量创建对象

let 对象名 = {
属性名: 属性值
};

通过构造函数创建对象

  • 构造函数的函数名首字母大写
  • 构造函数需要与new配合使用才有意义
  • new在执行时会做的事
    • 在内存中创建一个新的空对象
    • 让this指向这个对象
    • 执行构造函数的代码,给这个对象添加属性和方法
    • 返回这个新对象,无需return关键字
function 构造函数名(形参) {
this.属性名 = 形参;
}

let 对象名 = new 构造函数名();
  • 通过this添加的成员属性或成员方法,实例成员只能通过实例化的对象来访问,不可以通过构造函数名直接访问实例成员
function 构造函数名(形参) {
// 定义实例成员
this.属性名 = 形参;
}

let 对象名 = new 构造函数名();
// 调用实例成员
对象名.属性名;
  • 在静态函数中直接定义的属性或成员方法,只能通过构造函数名定义和访问,不能通过对象名定义和访问
// 定义静态成员
构造函数名.属性名 = 属性值;

// 调用静态成员
构造函数名.属性名;

通过原型对象实现方法的共享

  • 由于每次创建对象时,其内部的方法在内存存储时都需要重新开辟空间
  • 通过原型对象可以实现方法的共享,防止反复创建相同的方法
  • 通过构造函数的prototype属性可以定义公共方法,在对象中的__proto__属性指向了构造函数的prototype属性,所以可以直接调用构造函数中prototype属性定义的公共方法
function 构造函数名() {
...
}
// 添加一个方法
构造函数名.prototype.方法名 = function() {
...
}
// 添加多个方法
构造函数名.prototype = {
constructor: 构造函数名,
方法名: function() {
...
},
方法名: function() {
...
}
}

let 对象名1 = new 构造函数名();
对象名1.方法名();
let 对象名2 = new 构造函数名();
对象名2.方法名();
  • 原型对象(prototype__proto__)中定义了一个constructor属性,指向了构造函数本身
  • 添加多个方法时,由于会覆盖原型对象中的所有属性,所以会丢失constructor属性,需要重新指向构造函数
    • 内置构造函数不能通过对象覆盖的方式为原型对象添加多个方法
构造函数名.prototype.constructor
对象名.__proto__.constructor
  • 原型链
    • 通过构造函数创建的对象,包含一个__proto__属性,这个__proto__属性指向了构造函数的原型对象prototype
    • 构造函数的原型对象prototype也是一个对象,所以也有__proto__属性,这个__proto__属性指向了Object.prototype
    • Object的原型对象prototype也是一个对象,所以也有__proto__属性,这个__proto__属性指向了null
  • 原型链中属性查找机制
    • 首先查找这个对象自身是否有指定的属性
    • 然后查找__proto__属性指向的原型对象中是否有指定的属性
    • 然后查找Object的原型对象中是否有指定的属性
    • 直到找到null为止,还没找到,返回undefined
function 构造函数名() {
...
}

let 对象名 = new 构造函数名();
对象名.方法名();

对象名.属性名 = 属性值;
构造函数名.prototype.属性名 = 属性值;
Object.prototype.属性名 = 属性值;
  • this指向
    • 在构造函数中,this指向的是对象实例
    • 原型对象中的this指向的是对象实例

原型对象的应用

扩展内置类方法
内置类.prototype.方法名 = function {
...
}
  • 组合继承:通过构造函数+原型对象模拟继承的实现
  • 通过call()函数,将子函数中的this交给父函数,作为父函数this的指向,从而实现继承属性
  • 通过将子构造函数的prototype指向父构造函数的实例,从而实现继承父构造函数中的函数,仍然要重新修改constructor的指向
// 继承属性
function 父函数名(形参列表) {
...
}

function 子函数名() {
父函数名.call(this, 形参列表)
}

// 继承函数

父构造函数名.prototype.函数名 = function() {
...
};

子构造函数名.prototype = new 父构造函数名();
子构造函数名.prototype.constructor = 子构造函数名;

哔哩哔哩——黑马前端


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK