掘金 后端 ( ) • 2024-06-05 16:48

theme: yu

前言

在JavaScript的世界里,原型(Prototype)机制是一种核心特性,它支撑着面向对象编程的实现,特别是对象的继承机制。本文旨在深入探讨原型的内涵、原型链的工作原理,以及如何利用这一机制来增强代码的复用性和灵活性。我们将结合提供的代码示例,逐一剖析每个概念,确保读者能透彻理解这一复杂而又迷人的特性。

首先放上这张图,你能用文字表达每一条路径的吗?

1632878-20190507094639999-1584111224.png

原型

原型,简单来说,是JavaScript中每个函数对象自动附带的一个属性,称为prototype。这个属性是一个对象,用于存储所有通过该函数作为构造函数创建的新实例所共享的属性和方法。例如,Car.prototype就定义了所有由Car构造函数产生的对象共有的属性,如颜色和主人信息。

function Car(color, owner) {
    this.color = color;
    this.owner = owner;
}

在上面的代码中,尽管没有直接给Car.prototype赋值,但默认情况下,每个函数的prototype属性都指向一个对象,这个对象有一个特殊的属性constructor,指向该函数自身。

原型链

原型链是JavaScript实现继承的关键所在。当尝试访问一个对象的属性或方法时,如果该对象本身没有定义,JavaScript引擎会继续在该对象的__proto__属性所指向的对象(即其原型)中查找,这个过程会一直向上追溯,直至找到该属性或方法,或直到原型链的顶端(null)。这就是所谓的原型链查找过程。

car1为例,尽管它直接没有定义colorowner以外的属性,但理论上如果Car.prototype上有其他属性,car1也能访问到,因为它的__proto__(隐式原型)指向了Car.prototype

let car1 = new Car('green', 'tian');

隐式原型(proto)与显示原型(prototype)

  • 隐式原型(__proto__ :每个由构造函数生成的对象都拥有一个内部属性__proto__,它指向该对象的构造函数的prototype。这意味着,每个对象都可以通过其__proto__链向上访问到构造函数原型上的属性和方法。
  • 显示原型(prototype :正如前文所述,每个函数都有一个prototype属性,它是一个对象,用于存放所有实例共享的属性和方法。通过修改构造函数的prototype,可以影响所有由该构造函数创建的实例。

特例:Object.create()与无原型对象

Object.create()方法提供了一种不通过构造函数直接创建对象并设置其原型链的方式。例如,let b = Object.create(a);创建了一个新对象b,其原型指向了a。这说明b可以访问a上的属性,但如果想要一个没有原型的对象,可以使用Object.create(null)。这样创建的对象没有原型链,既不能访问任何非自有属性,也无法调用像toString这样的基本方法。

let a = { num1: 1 };
let b = Object.create(a); // b 的隐式原型指向 a
console.log(b.num1); // 输出 1

let c = Object.create(null); // c 没有原型链
console.log(c.toString); // undefined

image.png

原型与面向对象编程

在面向对象编程中,原型模式为实现继承提供了一种灵活且高效的方式。通过原型链,JavaScript模拟了类继承的特性,允许子对象继承父对象的属性和方法,同时也支持动态修改原型链,实现更复杂的继承结构。然而,由于原型链查找机制的特性,过度或不当使用可能导致性能问题,特别是在深层原型链中。

最后我们来揭秘第一张图

1632878-20190507094639999-1584111224.png
  • f1 和 Foo.prototypef1 是一个函数对象,它的 __proto__ 属性指向 Foo.prototype。这是因为 f1 是由 Function 构造函数创建的,而 Function 函数的 prototype 属性指向 Function.prototype

  • f2 和 Foo.prototype: 同样,f2 也是一个函数对象,它的 __proto__ 属性也指向 Foo.prototype

  • o1 和 Object.prototypeo1 是一个普通对象,它的 __proto__ 属性指向 Object.prototype。这是因为 o1 是由 Object 构造函数创建的,而 Object 函数的 prototype 属性指向 Object.prototype

  • o2 和 Object.prototype: 类似地,o2 的 __proto__ 属性也指向 Object.prototype

  • Foo.prototype 和 Function.prototypeFoo.prototype 是一个对象,它的 __proto__ 属性指向 Function.prototype。这是因为 Foo.prototype 是由 Function 构造函数创建的,而 Function 函数的 prototype 属性指向 Function.prototype

  • Object.prototype 和 nullObject.prototype 的 __proto__ 属性指向 null,这是原型链的尽头。

  • Function.prototype 和 Object.prototypeFunction.prototype 是一个对象,它的 __proto__ 属性指向 Object.prototype。这是因为 Function.prototype 是由 Object 构造函数创建的,而 Object 函数的 prototype 属性指向 Object.prototype

结语

总之,原型和原型链是JavaScript面向对象编程的基石,通过不断实践与探索,我们不仅能提升自己的编码技巧,还能更深刻地领悟这门语言的精髓。在不断变化的Web开发领域,这样的深入理解将是我们最宝贵的财富之一。