彻底理解原型和原型链


JavaScript中是支持面向对象编程的,那么我们知道 面向对象有三大特性

  • 封装:将属性和方法封装到一个类中,称为封装的过程
  • 继承:它是非常重要的,不仅可以减少重复代码,也是多态前提(纯面向对象中),它帮助我们将重复的代码和逻辑抽取到父类中,子类只需要直接继承过来使用即可
  • 多态:不同的对象在执行时表现出不同的形态

原型和原型链构成了 JavaScript 的继承机制,使得对象能够共享属性和方法,我们必须理解原型和原型链才能明白继承的实现,继承实现具体学习这篇文章:https://juejin.cn/post/7399986979735781391

原型

1. 对象原型

JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]]这个特殊的对象可以指向另外一个对象,这就是对象的原型,也称隐式原型

  • JavaScript 中,任何对象的原型最终都会指向 Object.prototype,除非你明确地指定了另一个原型

作用

不管如何创建只要是对象都会有这样的一个内置属性,那么这个对象有什么用呢?

  • 当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作
  • 首先检查该对象是否有对应的属性,如果有的话就使用它
  • 如果对象中没有该属性,那么会访问对象[[prototype]]内置属性指向的对象里的属性

在这里插入图片描述

特殊

函数也是对象,它们也有 __proto__ 属性,在 JavaScript 中,所有的函数都是 Function 构造函数的实例,这意味着每个函数的 __proto__ 属性都指向 Function.prototype

function foo() {}
console.log(foo.__proto__);
console.log(foo.__proto__ === Function.prototype); // true

获取

  • 方式一:通过对象的 __proto__ 属性可以获取到(但是这个是早期浏览器自己添加的,虽然大多数现代浏览器都支持 __proto__,但它不是标准属性,应该尽量避免直接使用)
  • 方式二通过 Object.getPrototypeOf(obj) 方法获取
var obj = {
  name: "obj",
  age: 18,
};
console.log(obj, obj.__proto__);
console.log(Object.getPrototypeOf(obj));

2. 函数原型

请先理解构造函数相关知识点,具体学习这篇文章https://juejin.cn/post/7399179026801868863
所有的函数都有一个prototype的属性(注意:不是__proto__),也称显式原型

定义

prototype 是一个对象,该对象在使用构造函数创建新的实例对象时会被赋值给实例对象的 __proto__ 属性(具体学习new操作符这篇文章:https://juejin.cn/post/7397399723601215488

  • 当定义一个函数时,JavaScript 引擎会自动为该函数创建一个 prototype 属性
  • 会将其初始化为一个包含 constructor 属性的对象constructor 属性指向函数本身
function foo() {}
console.log(foo.prototype);

在这里插入图片描述

作用

使用 prototype 实现方法共享:当使用构造函数创建实例时,可以通过 prototype 属性为所有实例共享方法,这避免了在每个实例上都创建相同的方法,从而节省内存,参考学习总结练习代码

获取

function foo() {}
console.log(
  foo.prototype,
  foo["prototype"],
  foo["prototype"] === foo.prototype
);

constructor属性

  • 每个函数的 prototype 对象默认都有一个 constructor 属性,指向这个函数自身,这意味着你可以通过实例对象的原型找到它们的构造函数
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    
    // 默认情况下,Person.prototype.constructor 指向 Person
    console.log(Person.prototype.constructor === Person); // 输出: true
    
    const john = new Person('John', 30);
    // john.__proto__ 是 Person.prototype
    console.log(john.__proto__ === Person.prototype); // 输出: true
    
    // john.constructor 指向 Person
    console.log(john.constructor === Person); // 输出: true
    
  • 如果你重写prototype或者修改了构造函数的 prototype 对象时,你需要手动修正 constructor 属性,否则它会指向错误的构造函数
  • 作用
    • 使用 constructor 确定对象的类型
      function Car(make, model) {
        this.make = make;
        this.model = model;
      }
      
      const myCar = new Car('Toyota', 'Corolla');
      
      // 使用 constructor 属性确定对象的类型
      console.log(Car.prototype.constructor === Car); // 指向本身
      console.log(myCar.constructor === Car); // 输出: true
      
    • constructor 用在运行时动态创建对象
      function Person(name) {
        this.name = name;
      }
      
      const person1 = new Person('Alice');
      const person2 = new person1.constructor('Bob');
      
      console.log(person2.name); // 输出: Bob
      console.log(person2 instanceof Person); // 输出: true
      

重写prototype

function Person() {}
Person.prototype = {
  // constructor: Person, // 手动修正 `constructor` 属性, 但这样指向constructor会被枚举到
  message: "hello",
  info: {},
  running: function () {
    this.name + '在running'
  },
  eating: function () {
    this.name + '在eating'
  },
};
Object.defineProperty(Person.prototype, "constructor", {
  enumerable: false,
  configurable: true,
  writable: true,
  value,
});

Object

Object 是一个函数,是所有对象的构造函数

  • Object.prototype
    • 当你创建对象时,例如通过 {}new Object(),你实际上是在使用 Object 这个构造函数
    • Object 作为一个函数,有自己的 prototype 属性(这是一个对象),所有通过 Object 构造的实例都共享这个原型对象,任何对象的原型最终都会指向 Object.prototype
    • 可以修改 Object.prototype 来为所有对象添加新的方法或属性,但这种做法不推荐,因为它会影响所有对象,可能导致意外的行为和兼容性问题
  • Object.__proto__
    • Object 是一个函数,函数也是对象,因此它有 __proto__ 属性
    • Object.__proto__ 实际上指向 Function.prototype,因为在 JavaScript 中,所有函数都是由 Function 构造的
  • 原型链的末端是Object构造函数的原型对象的原型(Object.prototype.__proto__)即是null
// Object和Foo是一个函数也是对象,所以它有 __proto__ 属性,函数的__proto__都指向Function.prototype
function Foo() {}
console.log(Foo.__proto__ === Function.prototype)
console.log(Object.__proto__ === Function.prototype); // 输出: true
console.log(Function.prototype.__proto__ === Object.prototype); // 输出: true

// 字面量创建一个普通对象相当于const obj = new Object()
const obj = {};
console.log(obj.__proto__ === Object.prototype); // 输出: true

// 最顶层的 Object.prototype.__proto__ 为 null
console.log(Object.prototype.__proto__ === null); // 输出: true

// 可以修改,但不建议修改
Object.prototype.newMethod = function() { console.log('This is a new method'); };

原型链

原型链是指对象通过其原型属性([[Prototype]]__proto__)形成的链条

  • 当访问对象的属性时,如果该属性在对象自身不存在,JavaScript 引擎会沿着原型链向上查找
  • 原型链的末端是Object构造函数的原型对象的原型(Object.prototype.__proto__)即为null

总结练习

function Person(name, age, height) {
  this.name = name;
  this.age = age;
  this.height = height;
}
Person.prototype.running = function () {
  console.log(this.name + "在running");
};
Person.prototype.jumping = function () {
  console.log(this.name + "在jumping");
};
var p1 = new Person("ablice", 20, 188);
var p2 = new Person("bob", 18, 180);
p1.running();
p2.jumping();

// 练习 打印什么 记得__proto__不是标准,不是练习用时要判断
Person.prototype.address = "中国";
p1.__proto__.info = "中国很大";
p1.height = 190;
p1.address = "深圳";
p2.isAdmin = true;

console.log(p1.address);
console.log(p2.address);
console.log(p1.isAdmin);
console.log(p2.isAdmin);
console.log(p2.info);

这个原型链图将整篇文章的知识点串联了起来,看图答案就呼之欲出了,图示如下:
在这里插入图片描述

  • 14行打印:ablicerunning
  • 15行打印:bobjumping
  • 24行打印:深圳
  • 25行打印:中国
    • 寻找过程:p2 --> p2.__proto__(Person.prototype) --> 中国
  • 26行打印:undefined
    • 寻找过程:p1 --> p1.__proto__(Person.prototype) --> p1.__proto.__proto__(Object.prototype) --> p1.__proto.__proto__.__proto__ --> null(没找到)
  • 27行打印:true
  • 28行打印:中国很大
    • 寻找过程:p2 --> p2.__proto__(Person.prototype) --> 中国很大

原型关系图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值