JavaScript 原型链完全解析,让你的代码更加高效

517 阅读6分钟

本文正在参加「金石计划」

JavaScript 的原型链是一种非常重要的概念,但对于很多初学者来说,它可能会感到有些抽象和难以理解。然而,理解原型链是非常必要的,因为它是 JavaScript 中实现面向对象编程的核心机制。本文旨在快速搞懂 JavaScript 的原型链,你将能够深入理解 JavaScript 的原型链,进而更加灵活地运用面向对象编程的思想,从而提高你的代码质量和开发效率。

创建对象

原型链我们先从创建对象的方法说起。问,创建对象有几种方式?

创建对象的方式有多种,总结来说就是这三类

1)对象字面量方式:使用 {} 创建对象,可以直接在大括号中定义属性和方法。【最简单、最常用】

let o1 = {name:'o1'}
let o2 = new Object({name:'o2'})

2)构造函数方式:使用 new 关键字和构造函数来创建对象,可以在构造函数中定义属性和方法。

let M = function(name){this.name=name}   //M是构造函数
let o3 = new M('o3')  //o3是实例

3)Object.create() 方法:用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)

let P = {name:'p'}
let o4 = Object.create(P)   //返回值o4,一个新对象,带着指定的原型对象及其属性。

可以看下,三种方式创建对象,分别打印的结果

image-20230320222514892

可以看到,

  • o1,o2的打印结果相同。
  • o3通过构造函数的方式,对象前面有一个M。
  • o4则是空的,但是是一个对象,通过打印可以看到,它有name属性。为什么会这样呢?说完原型链就明白为什么了。

构造函数和实例对象

接下来你还需要先知道什么是构造函数和实例对象。

什么是构造函数?

来看这一句,当前M是一个普通函数

let M = function(name){this.name=name} 

加上这句,现在M叫构造函数了

let o3 = new M('o3')  //o3是实例

所谓构造函数,指的就是专门生成对象的函数。用 new 关键字来调用的函数,称为构造函数。

简单的说,就是new后面的函数,叫构造函数。

JavaScript内置的构造函数,如Object,Array和Function。

  • 构造函数,创建具有相同属性和方法的对象
  • 构造函数,只是以相同方式定义的函数
  • 函数体内使用this关键字,代表所要生成的对象实例

什么是实例对象?

首先了解两个概念

:比如 人类 (对象的模板、实物的共同特征)

在JS中是没有“类”这个概念的,而是用构造函数来作为对象模版的

实例:比如 张三

实例化:张三的父母孕育他直到出生的过程,就叫:实例化

image-20230320225230428

原型关系说明

确认理解并记住上面的一些基础概念之后,接下来就正式来看什么是原型和原型链,先花三秒记住这张图。

image-20230326180241526

现在分别解释一下它们的对应关系

构造函数 - new - 实例

构造函数,使用new运算符,来生成一个实例。

实例是由构造函数创建的对象,每个实例都有自己的属性和方法。

构造函数 - prototype - 原型对象

每个JavaScript函数都有一个prototype属性,它是一个对象,也被称为该函数的【原型对象】,包含该函数的所有实例共享的属性和方法。

  • 构造函数也是函数,任何函数都可以被当做构造函数,只要使用了new运算符
  • prototype属性,这是在声明一个函数的时候,JS自动加上去的属性。prototype是函数才会有的属性

原型对象 - constructor - 构造函数

原型对象怎么区分自己是被哪个构造函数所引用呢?

原型对象中会有一个constructor(构造器),这个构造器会默认你声明的那个函数。来看一个示例

let M = function(name){this.name=name}   //M是构造函数
let o3 = new M('o3')  //o3是实例

M是构造函数,o3是实例

image-20230320232211989

M的原型对象M.prototype是一个空对象,空对象里面有一个constructor属性,这个属性的值是一个函数

image-20230320232342965

通过判断可以看到,【M构造函数】的【原型对象】里面的【constructor】的属性值函数,就等于M构造函数

image-20230320232431809

实例 - __proto__ -原型对象

在创建一个新对象实例时,该实例会自动继承其构造函数的prototype对象中的所有属性和方法。

每个JavaScript对象实例都有一个特殊的属性叫做__proto__,它指向创建该实例构造函数prototype对象

还是这个示例,已经知道o3是M的实例对象

let M = function(name){this.name=name}   //M是构造函数
let o3 = new M('o3')  //o3是实例

通过浏览器打印可以看到,o3里面有个属性,__proto__。并且它等于M的prototype。

所以实例o3这个对象的__proto__它引用的就是构造函数M的原型对象。

image-20230322204307172

注:函数才有属性prototype,对象才有属性__proto__。在JS中函数既是函数,同时它也是对象。

M.__proto__ ===Function.prototype

原型链

在JavaScript中, 每个对象都会有个内部属性[[Prototype]], 可以通过对象的_ proto_属性来访问它的原型。

当我们访问一个对象的属性或方法时,JavaScript会先在该对象本身上查找,如果找不到,就会沿着原型链向上查找,这个过程可以一直延续下去,直到Object.prototype对象。Object.prototype是所有对象的原型,它的原型为null,即原型链的最终点。


例如,我们创建了一个person对象,它有firstName、lastName、age和fullName四个属性和方法。然后,我们通过Object.create方法创建了一个新对象student,它继承了person的所有属性和方法。

var person = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
};

var student = Object.create(person);
student.firstName = "Bob";
student.grade = "A";

console.log(person.firstName); // 输出 "John"
console.log(student.firstName); // 输出 "Bob",在student本身找到,直接输出结果

console.log(person.lastName); // 输出 "Doe"
console.log(student.lastName); // 输出 "Doe",在student本身没找到,则会沿着原型链往上找,找到了person的lastName,输出结果

来看控制台的打印

image.png

1)原型链是由一系列对象和它们的原型构成的,每个对象都有一个原型,继承它的属性和方法;

2)通过(函数)的prototype和(对象)的__proto__来完成原型链的查找;

3)通过原型链的方式找到原型上的方法,这个方法是被构造函数的实例共有的;


🎨【点赞】【关注】不迷路,更多前端干货等你解锁

往期推荐

👉 一起来看看JS的原型继承

👉 15 个JavaScript数组实用技巧

👉 JavaScript数组方法看这一篇就够了

👉 JS内置日期对象Date的使用指南