JavaScript 原型、原型链与原型继承的核心机制解析

文章目录

  • [原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance)](#原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance))

原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance)

1.原型是什么?

在 JavaScript 中,每个对象都包含了一个 [[Prototype]] 内部隐藏属性,这个属性对应到的就是该对象的原型 (prototype) ,值有可能是 null 或是指向另一个对象。但因为 [[Prototype]] 是内部属性并无法直接被访问到,所以浏览器提供了__proto__ 的访问方法,可参考以下代码。

但要注意, __proto__ 方法并不在 ECMAScript 规范中,实际上开发中要取得对象的原型会使用 Object.getPrototypeOf

__proto__ 方法这是一个历史遗留的坑

参考代码如下:

javascript 复制代码
// Person 是一个构造函数
function Person(){}
//通过 Person 构造函数南湖,创建一个 personA对象
const personA=new Person();

//通过__proto__方法,查看 personA 的原型
console.log(personA.__proto__);

//给 Person 的原型增加一个属性sayHi
Person.prototype.sayHi =function(){
    console.log('hi')
}
console.log(personA.__proto__);

//personA通过原型链"链接"到了 Person.prototype
console.log(personA.__proto__===Person.prototype) 

// 使用 Object.getPrototypeOf API 查看原型,和__proto__方法所等价的
console.log(Object.getPrototypeOf(personA))

nodejs 环境输出如下:

javascript 复制代码
{}
{ sayHi: [Function (anonymous)] }
true
{ sayHi: [Function (anonymous)] }

因为 nodejs 环境下只会输出可枚举属性,所以第一个只输出{},而不是浏览器中看到的{constructor: ƒ}

2.\[Prototype] 又是什么? 和__proto__ 的差別是什么?

上面第一個問題已有提到,[[Prototype]] 是在 JavaScript 中物件的特殊隱藏屬性,但因為無法直接被訪問到,因此可以透過 __proto__ 的訪問方法。

3.proto 属性和 prototype属性的差別是什麼?

__proto__和prototype 是不同的属性。

  • __proto__是"每一个对象上的属性",它指向的是这个对象的"原型对象",也就是规范里的 \[Prototype]。

  • prototype 是"构造函数"上的属性,用来指定,将来 new 出来的示例,他们的原型是谁

  • 它们两个会指向同一个地方,这个地方就叫做原型对象

javascript 复制代码
// Person 是一個構造函式
function Person() {}
// 透過 Person 構造函式,創建了一個 personA 對象
const personA = new Person();

personA.__proto__ === Person.prototype; // true

4.原型链是什么?

原型对象(\[Prototype])本身是一个对象,因此它也拥有自己的原型。当我们试图访问某个对象的属性时,如果该对象没有所需的属性,它会在其原型 (prototype) 中寻找。如果原型 (prototype) 中仍然没有找到,它将会继续往上一层查找,直到找到,或者到 null 为止。这条连续的路径被称为原型链 (prototype chain) ,;链的终点值为 null

javascript 复制代码
personA.__proto__.__proto__.__proto__ === null;

例如,我們经常使用数组的 filter 方法。假设现在有一个数组 「list」,我们在这个数组上使用 filter 方法。但事实上,filter 方法并不存在于这个 list,它存在于 Array 这个构造函数上。我们今天能使用 filter 方法,也是通过原型链 (prototype chain) 实现的。

5.什么是原型链继承?

假设有一个对象 「animal」,这个对象拥有自己的属性和方法。同时我们又想建立两个基于 「animal」 的对象,分別为「cat」和「dog」,这两个对象会有一些独特的方法和属性,但同时又需要用到「animal」对象的方法和属性。在 JavaScript 里,不需要通过复制或重新实现,就可以通过原型继承(Prototypal inheritance) 达成这个目的。

简而言之,「cat」和「dog」对象本身虽然没有「animal」对象的方法,但可以从他们的原型中继承方法来使用。实际操作上,我们会将属性或方法加到原型 (prototype) 上,那么所有从这个对象中实例出来的对象,都有办法使用这个方法或属性。

javascript 复制代码
// 构造函数 Animal
function Animal() {}

// 实例
const cat = new Animal();

// 往原型对象加上方法
Animal.prototype.sleep = function () {
  console.log("sleep");
};

// 使用构造函数的 prototype 的方法
cat.sleep(); // sleep
相关推荐
旋律翼217 小时前
Qt Bridges for C# 深度技术解析
开发语言·qt·c#
弹简特17 小时前
【Java项目-企悦抽】07-用户与认证模块-实现用户注册01
java·开发语言
研☆香17 小时前
闭包实战避坑指南
javascript
你怎么知道我是队长17 小时前
JavaScript 的控制语句介绍
开发语言·javascript·ecmascript
RSTJ_162518 小时前
PYTHON+AI LLM DAY ONE HUNDRED AND THREE
开发语言·人工智能·python
用户440145493141918 小时前
Spring Boot 3.x 异步编程实战:从 @Async 到虚拟线程
javascript
charlie11451419119 小时前
RK3506B: buildroot:出一份正规的最小 rootfs
开发语言·嵌入式·开源项目·rk3506b
Byron Loong19 小时前
【c#】Bitmap释放之 大象与蚊子
开发语言·c#
音符犹如代码19 小时前
Java动态线程池:避坑原生线程池,吃透Dynamic-TP,手写一个Demo
java·开发语言
喜欢就别19 小时前
平均风险:Rényi DP 和零集中 DP
开发语言·r语言