原型与原型链:这篇看完还懵?来找我“算账”!💸

前言

Hello~大家好。我是秋天的一阵风

今天咱们来聊聊JavaScript中一个超级重要的概念------原型和原型链。你是不是在学习JavaScript的时候,经常听到这两个词,但又觉得有点摸不着头脑呢?

今天我想把我的学习经验和大家分享一下,希望能帮你少走弯路。如果你看完这篇还没搞懂,可以随时评论或者私信我。只要跟着我一步步来,你肯定能掌握这个知识点。准备好了吗?让我们一起揭开原型和原型链的神秘面纱吧!

一、什么是原型、原型链?

在深入探讨原型和原型链之前,我想先聊聊大家在学习过程中可能遇到的困惑。相信你在阅读其他文章时,也见过各种各样的表述:一会儿是proto,一会儿是prototype,还有原型对象......这些不同的称呼,很容易让人摸不着头脑,把一个原本并不复杂的话题搞得复杂起来。

为了避免这种混乱和更好地帮你理清思路,在后续的内容中,我们会直接使用英文术语来表示,这样既能保持一致性,也能让你更准确地理解这些概念。

1. prototype : 原型对象, 函数的一个属性,它的本质是对象:{}

每一个 函数 都有一个prototype属性,这个属性的值是一个对象。

js 复制代码
      function Test() {
        this.a = 1;
      }

      console.log("-------Test.prototype-------------");
      console.log(Test.prototype); // {}
      console.log(Object.prototype.toString.call(Test.prototype)); // [object Object]

2. proto: 原型属性,对象的一个属性,它的本质也是一个对象:{}

每一个 对象 都有一个__proto__属性,这个属性的值也是一个对象。

js 复制代码
const a = {};
console.log(a) // {}
console.log(a.__proto__)
console.log(Object.prototype.toString.call(a.__proto__)); // [object Object]

3. 一个对象的__proto__ 保存着 该对象的构造函数的 prototype

是不是觉得有点绕?没关系,我用代码给你演示:

  • Test是一个构造函数,所以它身上有一个prototype属性
  • test对象是通过Test构造函数生成的一个实例对象,所以它身上有一个__proto__属性

简单来说就是: 一个对象的__proto__属性的值 等于 这个对象的构造函数的prototype属性的值

js 复制代码
function Test() {
        this.a = 1;
      }
const test = new Test();
console.log(test.__proto__ === Test.prototype); // true

4. 提出几个问题,深入理解

在了解完上面的相等关系以后,我们来思考一个问题,既然说每个对象都有一个__proto__属性,值是一个对象

那么Test.prototype不也是一个对象吗? Test.prototype__proto__属性吗,答案是肯定的。

js 复制代码
console.log(Test.prototype.__proto__); // {}

我们继续提出第二个问题,这个Test.prototype.__proto__ 指向的是不是应该是构造它的函数的prototyype?

也就是 Test.prototype.__proto__ === Test.prototype的构造函数.protoype

当然,我直接告诉你答案,

js 复制代码
Test.prototype.__proto__ === Object.prototype // true

看到这里,估计你应该会举一反三地提出第三个问题,Object.prototype 也是一个对象,那它的__proto__属性指向谁呢? 答案是null,因为这已经是最顶层了。

js 复制代码
console.log(Object.prototype.__proto__); //null,顶层

5. 总结

我们把上面的案例代码都完整列出来,并且同学们重新回顾一遍加深印象:

js 复制代码
      function Test() {
        this.a = 1;
      }

      console.log("-------Test.prototype-------------");
      console.log(Test.prototype);

      const test = new Test();
      console.log(test);

      console.log(test.__proto__ === Test.prototype); // true

      console.log(Test.prototype.__proto__ === Object.prototype); //true

      console.log(Object.prototype.__proto__); //null,顶层

二、 那什么是原型链呢?

我们先看一个代码:

js 复制代码
     function Test() {
        this.a = 1;
      }

      Test.prototype.b = 2; 
      console.log("Test.prototype: ", Test.prototype); 

      const test = new Test();

      console.log(test.b); // 2

      console.log("test: ", test);

      Object.prototype.c = 3;

      console.log(test.c) // 3
  1. 我们给Test构造函数的prototype对象赋值了一个b属性,值为2

  2. 再给Object构造函数的prototype对象赋值了一个c属性,值为3

  3. 然后在test对象上直接访问bc,发现是都可以访问到的,这是为什么呢?

  4. 我们看看控制台:

其实,查找b和c的过程如下:

原型链的查找过程

  1. 自身属性查找:首先在对象自身查找属性或方法。
  2. 原型链查找 :如果自身没有找到,沿着 __proto__ 属性向上查找,直到找到该属性或方法,或者到达原型链的末端(null)。

所以我们可以给原型链下一个定义:

  • 当你访问一个对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript 会沿着 __proto__ 属性向上查找,直到找到该属性或方法,或者到达原型链的末端(即 null)。
  • 这个查找过程形成了一个"链",称为原型链。

三、Function 和 Object的特殊性

1. Function

我们还是以上面的代码来当测试案例:

js 复制代码
      function Test() {
        this.a = 1;
      }
      const test = new Test();
      console.log("test: ", test);

首先Test是一个函数,这一点是毋庸置疑的。但是在Javascript的世界里呢,函数是对象的一种特殊类型。函数(Function)不仅仅是代码块,它们也是对象(Object)。这意味着函数可以像普通对象一样具有属性和方法,并且可以被动态地操作。

既然是这样,我们像之前一样"套公式":只要是对象,就有一个__proto__属性,等于它的构造函数的prototype对象

Test被当成对象时,其实它的构造函数就是Function,你可以这么理解:

const Test => new Function()

所以我们可以得出一个结论:

js 复制代码
    console.log(Test.__proto__); // ƒ () { [native code] }
    console.log(Test.__proto__ === Function.prototype); //true

Function 自己本身也是一个函数,所以我们可以继续套公式得出结论:

js 复制代码
     //Function 本身也是一个函数
      console.log(Function); //ƒ Function() { [native code] }
      console.log(Function.__proto__); // ƒ () { [native code] }
      console.log(Function.prototype); // ƒ () { [native code] }
      console.log(Function.__proto__ === Function.prototype); //true

2. Object

我们进行这么一个推理,平时我们生成对象都是采用字面量的形式,也就是:const obj = {};

那么其实 const obj = {} 等于 const obj = new Object();

并且Object也是一个Function:

javascript 复制代码
console.log(typeof Object); //function

那么Object是不是跟Test一样,具备以下的关系:

js 复制代码
      //推理
      // const obj = {} 等于 const obj = new Object()
      console.log(typeof Object); //function
      console.log(Object.__proto__ === Function.prototype); //true

      console.log(Object.__proto__ === Function.__proto__); //true

四、 constructor

constructor 就比较简单了,它是实例对象上的一个属性,指向它的构造函数:

js 复制代码
  console.log("---------constructor----------");
      console.log(test.constructor);
      /**
       * ƒ Test() {
       *   this.a = 1;
       * }
       * */
      console.log(test.constructor === Test); //true
      //构造函数本身是函数,相当于函数的prototype
      console.log(test.constructor.prototype === Test.prototype); //true

五、测试题目

最后,我们来看一个简答题:

js 复制代码
  console.log("简答题:");
      function Foo() {}
      const foo = new Foo();
      // Foo.__proto__ = ?
      // foo.__proto__ = ?
      // Foo.prototype.__proto__ = ?
  1. Foo是一个函数,也是一个对象,它的构造函数是Function
  2. fooFoo的实例对象,它的构造函数是Foo
  3. Foo.protootype也是一个普通对象,它的构造函数是Object

所以答案如下:

js 复制代码
  console.log("简答题:");
      function Foo() {}
      const foo = new Foo();
      // Foo.__proto__ = ?
      // foo.__proto__ = ?
      // Foo.prototype.__proto__ = ?
      console.log(Foo.__proto__ === Function.prototype); // true
      console.log(foo.__proto__ === Foo.prototype); // true
      console.log(Foo.prototype.__proto__ === Object.prototype); // true
相关推荐
小满zs3 小时前
Zustand 第五章(订阅)
前端·react.js
涵信4 小时前
第一节 基础核心概念-TypeScript与JavaScript的核心区别
前端·javascript·typescript
谢尔登4 小时前
【React】常用的状态管理库比对
前端·spring·react.js
编程乐学(Arfan开发工程师)4 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
小公主5 小时前
JavaScript 柯里化完全指南:闭包 + 手写 curry,一步步拆解原理
前端·javascript
姑苏洛言6 小时前
如何解决答题小程序大小超过2M的问题
前端
TGB-Earnest7 小时前
【leetcode-合并两个有序链表】
javascript·leetcode·链表
GISer_Jing7 小时前
JWT授权token前端存储策略
前端·javascript·面试
开开心心就好7 小时前
电脑扩展屏幕工具
java·开发语言·前端·电脑·php·excel·batch
拉不动的猪7 小时前
es6常见数组、对象中的整合与拆解
前端·javascript·面试