跳到主要内容

原型

对象的结构

  • 对象中存储属性的区域实际上有两个:
    1. 对象自身
      • 直接通过对象所添加的属性,位于对象自身中 p1.address = "花果山"
      • 在类中通过 x = y 的形式添加的属性,位于对象自身中 name = "孙悟空"
    2. 神秘位置
      • 对象中还有一些内容,会存储到其他的对象里(原型对象)
      • 在对象中会有一个属性用来存储原型对象,这个属性叫做 __proto__
      • 原型对象也负责为对象存储属性
        • 当我们访问对象中的属性时,会优先访问对象自身的属性,对象自身不包含该属性时,才会去原型对象中寻找
      • 会添加到原型对象中的情况:
        1. 在类中通过xxx()方法添加的方法,位于原型中
        2. 主动向原型中添加的属性或方法
class Person {
name = "孙悟空";
age = 18;
sayHello = "hello";

sayHello() {
console.log("hello");
}
}

const p1 = new Person();

console.log(p1.sayHello); // "hello"

Docusaurus Plushie


原型对象

  • 访问一个对象的原型对象
    1. 对象.__proto__
    2. Object.getPrototypeOf(对象)
  • 原型对象中的数据:
    1. 对象中的数据(属性、方法等)
    2. constructor(对象的构造函数)
  • 注意:
    • 原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同
      • p对象的原型链:p对象 -> 原型 -> 原型 -> null
      • obj对象的原型链:obj对象 -> 原型 -> null
    • 原型链:
      • 读取对象属性时,会优先读取对象自身属性,如果对象中有,则使用,没有则去对象的原型中寻找,如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null)),如果依然没有找到,则返回undefined
      • 作用域链,是找变量的链,找不到会报错
      • 原型链,是找属性的链,找不到会返回undefined
class Person {
name = "孙悟空";
age = 18;

sayHello() {
console.log(this.name);
}
}

const p = new Person();

console.log(p); // Person {name: '孙悟空', age: 18}
console.log(p.__proto__); // {constructor: ƒ, sayHello: ƒ}
console.log(Object.getPrototypeOf(p) === p.__proto__); // true
console.log(p.__proto__.__proto__);
console.log(p.__proto__.__proto__.__proto__); // null
  • 所有的同类型对象它们的原型对象都是同一个,也就意味着,同类型对象的原型链是一样的
  • 原型的作用:
    • 原型就相当于是一个公共的区域,可以被所有该类实例访问,可以将该类实例中,所有的公共属性(方法)统一存储到原型中,这样我们只需要创建一个属性,即可被所有实例访问
    • JS中继承就是通过原型来实现的,当我们继承时,子类的原型就是父类的实例
    • 在对象中有些值是对象独有的,像属性(name,age,gender)每个对象都应该有自己的值,但是有些值对于每个对象来说都是一样的,像各种方法,对于一样的值没必要重复创建
class Person {
name = "孙悟空"

sayHello() {

}

sayHello2 = () => {

}
}

const p1 = new Person();
const p2 = new Person();

console.log(p1.__proto__ === p2.__proto__); // true
console.log(p1.sayHello === p2.sayHello); // true
console.log(p1.sayHello2 === p2.sayHello2); // false

class Animal { }
class Cat extends Animal { }
const cat = new Cat();
// cat --> Animal实例 --> object --> Object原型 --> null

Docusaurus Plushie


修改原型对象

  • 大部分情况下,我们是不需要修改原型
    • 注意:千万不要通过类的实例去修改原型
      1. 通过一个对象影响所有同类对象,这么做不合适
      2. 修改原型先创建实例,麻烦
      3. 危险
  • 处理通过__proto__能访问对象的原型外,还可以通过类的prototype属性,来访问实例的原型,修改原型时,最好通过类去修改
    • 好处:
      1. 一修改就是修改所有实例的原型
      2. 无需创建实例即可完成对类的修改
    • 原则:
      1. 原型尽量不要手动改
      2. 要改也不要通过实例对象去改
      3. 通过 类.prototype 属性去修改
      4. 最好不要直接给prototype去赋值
class Person {

}

const p = new Person();
console.log(Person.prototype === p.__proto__); // true

Person.prototype.fly = () => {
console.log("我在飞");
}
console.log(p.fly()); // 我在飞