JS:原型与原型链(附带图解与代码)

一、原型

写在前面:

任何对象都有原型。

函数也是对象,所以函数也有原型。

1.什么是原型

在 JavaScript 中,对象有一个特殊的隐藏属性 [[Prototype]],它要么为 null,要么就是对另一个对象的引用,该对象被称为"原型"。所以,[[Prototype]] 链接所引用的对象就是该对象的"原型"。

我们可以通过Object.getPrototypeOf/Object.setPrototypeOf(推荐写法)或者是__proto__去设置原型,proto 是 [[Prototype]] 的 getter/setter,我们可以用Object.getPrototypeOf/Object.setPrototypeOf 来取代 proto 去 get/set 原型,也可以继续使用__proto__去访问原型。

例如,在下面的示例中,通过Object.create()将对象b的原型设置为a,实现原型式继承,此时对象a就是对象b的原型。

javascript 复制代码
    const a = {
      name: "小王",
    };
    // Object.create()是一个静态方法,它创建一个新对象,使用现有的对象作为新创建的对象的proto。
    // 在这个例子中,我们使用对象a作为原型来创建新对象b。这意味着b的原型是a`。
    const b = Object.create(a);
    // Object.getPrototypeOf()方法返回指定对象的原型(即内部[[Prototype]]属性的值)。
    console.log(Object.getPrototypeOf(b));//{name: '小王'}
    // proto是一个非标准的属性,但在许多环境中都可用,它提供了对对象原型的直接访问。这里,b.__proto__也返回a,即{name: '小王'}`。
    console.log(b.__proto__);//{name: '小王'}
    console.log(a); //{name: '小王'}
    console.log(b.name); //小王

图像示例:

2.原型的作用

  1. 实现继承 :原型链是JavaScript中实现对象继承的主要机制。当一个对象试图访问一个属性时,如果它自身没有这个属性,JavaScript会在它的原型链上查找这个属性,直到找到这个属性或者到达链的尽头(null)。通过这种方式,原型允许对象继承其他对象的属性和方法。
  2. 共享属性和方法:通过原型,我们可以定义对象的共享属性和方法。这意味着所有对象实例都可以访问和修改这些属性和方法。这在创建大量具有相同属性和方法的对象时非常有用,因为它可以避免在每个对象实例中重复定义这些属性和方法。
  3. 动态修改和扩展:由于原型是一个对象,我们可以在运行时动态地修改和扩展它。这允许我们在不修改原始构造函数的情况下,为所有对象实例添加新的属性和方法。这种灵活性使得原型成为JavaScript中一个非常强大的工具。
  4. 代码重用和模块化:通过创建具有特定原型的对象,我们可以实现代码的重用和模块化。这有助于降低代码的复杂性,提高代码的可读性和可维护性。

二、原型链

直接上图:

原型链图

讲解:

1.构造函数

构造函数是一种特殊的函数,通常用于初始化新创建的对象实例。每个构造函数都有一个prototype属性,这个属性是一个指针,指向一个对象(原型对象),该对象的用途是包含可以由特定类型的所有实例共享的属性和方法。当访问一个对象的属性时,如果这个对象自身没有这个属性,JavaScript会查找这个对象的原型(即[[Prototype]]指向的对象),以此类推,直到找到这个属性或者到达原型链的尽头(null)。我们可以使用new关键字来调用一个构造函数,它会创建一个新的空对象,然后将这个新对象的内部链接([[Prototype]])指向构造函数的prototype属性所引用的对象。构造函数的实例和构造函数的原型对象有一个constructor属性指向构造函数。在上面原型链图中,因为对象b继承了实例a,所以b.constructor也指向构造函数A。当然,构造函数也是一个对象,所以它也有原型,通常是Function.prototype。

2.Object.prototype和Function.prototype

Object.prototype是所有JavaScript对象(除了null)的原型。换句话说,几乎所有的对象都会从Object.prototype继承属性和方法,比如.toString(), .hasOwnProperty(), 和 .constructor等。

Function.prototype是所有函数的原型,包括那些被用作构造函数的函数。这意味着所有的函数都会从Function.prototype继承属性和方法,如.apply(), .call(), 和 .bind()等。

所以在查找原型时,在null之前函数会找到Function.prototype,所有对象都会找到Object.prototype。

3.理解原型链

原型链是一种实现继承的机制。 在上面的原型链图可以看出,通过把一个对象的原型指向另一个对象,可以让这个对象访问另一个对象的属性,最终形成了一个链条一样的结构。在原型链中查找属性或方法时,JavaScript 会从当前对象开始,沿着原型链(即 __proto__ 链)向上查找,直到找到相应的属性或方法或直到到达 Object.prototype 的原型(即 null)。如果找不到,则返回 undefined

4.代码展示

希望下面的代码可以帮你理解上面的原型链图。

javascript 复制代码
<script>
    function A() {
      this.name = "a";
    }
    A.prototype.say = function () {
      return "hello";
    };
    const a = new A();
    const b = Object.create(a);
    const c = Object.create(b);

    console.log(c.say()); //hello,因为c.__proto__指向b,b.__proto__指向a,所以c可以访问到a的原型链上的方法
    console.log(c.name); //a,因为c.__proto__指向b,b.__proto__指向a,所以c可以访问到a的原型链上的属性
    console.log(c.age); //undefined,因为在原型链上没找到age属性
    console.log(c.__proto__.__proto__.__proto__.__proto__.__proto__); //null
    console.log(A.prototype === a.__proto__); //true,它们指向同一个原型对象
    console.log(c.__proto__); //A {}
    console.log(b.__proto__); //A {}
    console.log(c === b); //false,c和b的原型链长度是不一样的,也就是说{}展开的内容是不一样的
  </script>

三、总结

1.是个对象就有原型

2.对象的原型是个对象或者null

3.被 [[Prototype]] 链接所引用的对象就是该对象的"原型"

4.构造函数.prototype指向一个原型对象,这个对象是构造函数的实例的原型

5.通过Object.getPrototypeOf或者__proto__逐级访问形成的链条叫做原型链

相关推荐
庸俗今天不摸鱼29 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX1873030 分钟前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下36 分钟前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758101 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周1 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei2 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯