【JS】JavaScript 中的原型与原型链

本文详细解析了JavaScript中原型与原型链的概念,包括原型对象的指向、constructor属性、__proto__与[[Prototype]]的关系,以及原型链在查找属性、继承和多态性方面的应用。
摘要由CSDN通过智能技术生成

原型

1 函数中 prototype 指向原型对象

当我们创建一个函数时,函数都会有一个默认属性 prototype

该属性指向一个对象, 该对象就被称之为 原型对象

function fun(){

}
fun.prototype // 原型对象

在这里插入图片描述

2 对象中 proto 指向原型对象

当函数作为 普通函数 进行调用时,该属性不会有任何作用

当函数被作为 构造函数 进行调用 (使用 new 运算符调用) 时,构建出来的 实例对象 会有一个属性 __proto__ 指向 原型对象

function fun(name){
  this.name = name
}

fun.prototype // 原型对象

// 函数被作为构造函数进行调用
const obj = new fun('swim') 

// 实例对象.__proto__ 指向 构造函数.prototype
obj.__proto__ === fun.prototype // true

》》》2

3 原型对象中 constructor 指向构造函数

原型对象 默认会有一个特殊的属性 constructor, 该属性又指向了函数本身

function fun(name){
  this.name = name
}

fun.prototype // 原型对象

// 原型对象中 constructor 指向构造函数
fun.prototype.constructor === fun // true

在这里插入图片描述

4 proto 与 [[Prototype]] 的关系

如果将 实例对象 打印出来, 会发现对象中并不具有 __proto__ 属性,恰恰相反有个特殊的 [[Prototype]] 属性。

《《《《4

__proto__ 并不是 ECMAScript 语法规范的标准, 它只是大部分浏览器厂商实现或说是支持的一个属性, 通过该属性方便我们访问、修改原型对象

遵循 ECMAScript 标准, [[Prototype]] 才是正统, [[Prototype]] 无法被直接修改、引用

ECMAScript 6 开始, 可通过 Object.getPrototypeOf()Object.setPrototypeOf() 来访问、修改 原型对象

简单理解: __proto__[[Prototype]] 是同一个东西, __proto__ 是非标准的, [[Prototype]] 才是标准的, 但是它们都是指向 原型对象

那么问题来了, 我们访问的 __proto__ 在哪里呢? 实际上它是被添加在 Object.prototype 上, 然后通过 原型链(后面会详细展开说明) 我们就能够访问到该属性

5 所有非空类型数据,都具有原型对象

任何非空数据,本质上都是通过对应的构造函数构建出来的,所以他们都具有 __proto__ 属性,指向构造函数的原型对象。

如果需要判断某个值的原型对象,只需要确认该值是通过哪个构造函数构建的即可,只要确认了构造函数,那么该值的 __proto__ 必然指向该构造函数的原型对象 prototype

// 数字
const num = 1
// 数字是通过 Number 构建的, 那么其原型对象等于 Number.prototype
num.__proto__ === Number.prototype // true

// 字符串
const str = 'str'
// 字符串是通过 String 构建的, 那么其原型对象等于 String.prototype
str.__proto__ === String.prototype // true

// 布尔类型
const bool = false
// 布尔值是通过 Boolean 构建的, 那么其原型对象等于 Boolean.prototype
bool.__proto__ === Boolean.prototype // true

// Symbol
const sym = Symbol('symbol')
// sym 是通过 Symbol 构建的, 那么其原型对象等于 Symbol.prototype
sym.__proto__ === Symbol.prototype // true

// BigInt
const big = BigInt(1)
// big 是通过 BigInt 构建的, 那么其原型对象等于 BigInt.prototype
big.__proto__ === BigInt.prototype // true

// 对象
const obj = { age: 18 }
// 对象是通过 Object 构建的, 那么其原型对象等于 Object.prototype
obj.__proto__ === Object.prototype // true

// 函数
const fun = () => {}
// 函数是通过 Function 构建的, 那么其原型对象等于 Function.prototype
fun.__proto__ === Function.prototype // true

// 数组
const arr = [1, 2, 3]
// 数组是通过 Array 构建的, 那么其原型对象等于 Array.prototype
arr.__proto__ === Array.prototype // true

6 new运算符做了哪些事情
  1. 创建一个新的空对象 A
  2. 往空对象挂载 构造函数 Com原型对象: 对象 A 创建 __proto__ 属性, 并将 构造函数prototype 属性赋值给 __proto__
  3. 执行 构造函数 Com: 改变 构造函数 this 指向, 指向空对象 A, 并执行 构造函数, 往空对象注入属性
  4. 判断 构造函数 是否返回一个对象?
  • 是: 如果 构造函数 也返回了一个对象 B, 则最终 new 出来的对象则为返回的对象 B
  • 否: 最终 new 出来的对象为最初创建的对象 A
因此当我们执行
var o = new Foo();

实际上执行的是:
// 1. 创建一个新的空对象 A
let A = {};

// 2. 往空对象挂载, 挂载构造函数 Com 的原型对象: obj.__proto__ === Com.prototype;
Object.setPrototypeOf(A, Com.prototype);

// 3. 执行构造函数: 改变构造函数 this 指向, 指向对象 A, 往 A 注入属性
let B = Com.apply(A, args);

// 4. 判断构造函数是否返回对象: 是则取返回值、否则取最初创建的对象 A
const newObj = B instanceof Object ? res : A;

原型链

原型链是一种对象之间的链接机制,它用于实现对象之间的继承和属性访问。

每个JavaScript对象都有一个原型(prototype),原型是一个对象或null。当你访问一个对象的属性时,如果该对象自身没有该属性,JavaScript 引擎会沿着原型链向上查找,直到找到具有该属性的对象或到达原型链的末尾(即null)。这样,对象可以继承其原型的属性和方法。

根据上文,所有非空数据,都可以通过 __proto__ 指向原型对象,如果原型对象非空,那么必然会有 __proto__ 指向自己的原型对象,如此一层一层往上追溯,依此类推,就形成了一整条链路,一直到某个原型对象为null,才能达到最后一个链路的最后环节,而原型对象之间这种链路关系被称之为原型链 (prototype chain)

1 举个栗子
1.1 直接创建一个对象
const obj = { age: 18 };

从对象 obj 视角来看:
1. obj 本质上是通过 Object 构建出来的,那么 obj.__proto__ === Object.prototype
2. Object.prototype 的原型对象为 null,因此原型链到此结束

》》》图5

1.2 数字、字符串、数组等类型数据, 下面以数字为例, 其他类型大同小异
let num = 1;

1. num 本质上是通过 Number 构建出来的,那么 num.__proto__ === Number.prototype
2. Number.prototype 本质上是一个对象,是通过 Object 构建出来的,
	那么 Number.prototype.__proto__ === Object.prototype
3. Object.prototype 的原型对象为 null,因此原型链到此结束

num.__proto__ === Number.prototype // true
Number.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__  // null 原型链结束
1.3 一个复杂的例子
function Person(age) {
  this.age = age       
}
var person = new Person(100)

从对象  person 视角来看:
	1. person 是通过 Person 构建出来的, 那么 person.__proto__ 等于 Person.prototype
	2. Person.prototype 是个对象, 是通过 Object 构建出来了, 那么Person.prototype.__proto__ 等于 Object.prototype
	3. Object.prototype 的 原型对象 为 null, 原型链 到此结束

2 原型链的作用
  • 查找属性: 当我们试图访问 对象属性 时, 它会先在 当前对象 上进行搜寻, 搜寻没有结果时会继续搜寻该对象的 原型对象, 以及该对象的 原型对象原型对象, 依次层层向上搜索, 直到找到一个名字匹配的属性或到达原型链的末尾
function Person(age) {
  this.age = age
}

Person.prototype.name = 'klx'
Person.prototype.age = 18

const person = new Person(28)

person // 当前对象: { age: 28 }

person.name // klx, 取自原型对象 Person.prototype
person.age // 28, 取自当前对象

person.toString() // [object Object], 取自原型对象 Object.prototype

person.address // undefined, 沿着原型链找不到 address 

  • 继承属性和方法:原型链允许对象继承其原型的属性和方法。当你访问一个对象的属性时,如果对象本身没有该属性,它会通过原型链访问原型对象的属性。这样可以实现属性的共享和代码的重用。

  • 创建对象关联:通过原型链,你可以将一个对象与另一个对象关联起来,形成一个对象链。这样,一个对象可以通过原型链访问到另一个对象的属性和方法。

  • 实现对象的多态性:在原型链中,可以通过在原型对象上定义相同名称的属性或方法,从而实现对象的多态性。当对象在原型链中找到具有相同名称的属性或方法时,会使用最近的那个

JavaScript中的原型链是基于原型继承的概念,它允许对象通过原型链接到其他对象,并继承其属性和方法。这种机制在JavaScript中是一种重要的特性,使得对象之间可以实现继承和共享,提供了灵活性和代码复用的机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值