JS | JS中类的 prototype 属性和__proto__属性

大多数浏览器的 ES5 实现之中,每一个对象都有**__proto__** 属性,指向对应的构造函数的**prototype属性。Class 作为构造函数的语法糖,同时有prototype** 属性和**__proto__**属性,因此同时存在两条继承链。

构造函数的子类 有prototype属性**。** ‌ 子类通过继承机制继承了父类的属性,包括prototype属性。子类的prototype属性不仅存在,而且其__proto__属性指向父类,而子类prototype属性的__proto__属性指向父类的prototype属性‌。

在JavaScript中,构造函数用于创建对象,其子类通过继承父类的属性和方法来实现功能的扩展。子类的prototype属性允许子类实例共享方法和属性,这与父类的prototype属性相关联,形成了原型链‌。

具体来说,当创建一个子类的实例时,该实例的原型(proto)指向父类,而子类prototype属性的__proto__则指向父类的prototype。这样,子类实例可以通过原型链访问父类的方法和属性,实现方法的共享和继承‌。

(1)子类的**__proto__**属性,表示构造函数的继承,总是指向父类。

(2)子类**prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype**属性。

javascript 复制代码
class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

上面代码中,子类**B** 的**__proto__属性指向父类A** ,子类B的**prototype属性的__proto__属性指向父类A** 的**prototype**属性。

这样的结果是因为,类的继承是按照下面的模式实现的。

javascript 复制代码
class A {
}

class B {
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

const b = new B();

《对象的扩展》一章给出过 **Object.setPrototypeOf()**方法的实现。

javascript 复制代码
Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

因此,就得到了上面的结果,即:

javascript 复制代码
Object.setPrototypeOf(B.prototype, A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

Object.setPrototypeOf(B, A);
// 等同于
B.__proto__ = A;

这两条继承链,可以这样理解:

作为一个对象 ,子类(B)的原型(__proto__属性)是父类(A);

作为一个构造函数 ,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。

javascript 复制代码
B.prototype = Object.create(A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

**extends**关键字后面可以跟多种类型的值。

javascript 复制代码
class B extends A {
}

上面代码的**A** ,只要是一个有**prototype属性的函数,就能被B** 继承。由于函数都有**prototype属性(除了Function.prototype函数),因此A**可以是任意函数。

下面,讨论两种情况。第一种,子类继承**Object**类。

javascript 复制代码
class A extends Object {
}

A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

这种情况下,A 其实就是构造函数**Object** 的复制,A 的实例就是**Object**的实例。

第二种情况,不存在任何继承。

javascript 复制代码
class A {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

这种情况下,A 作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承**Function.prototype。但是,A** 调用后返回一个空对象(即**Object** 实例),所以**A.prototype.__proto__指向构造函数(Object** )的**prototype**属性。

实例的 proto 属性

子类实例的**__proto__属性的__proto__** 属性,指向父类实例的**__proto__**属性。也就是说,子类的原型的原型,是父类的原型。

javascript 复制代码
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');

p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true

上面代码中,ColorPoint 继承了**Point** ,导致前者原型的原型是后者的原型。因此,通过子类实例的**__proto__.__proto__**属性,可以修改父类实例的行为。

javascript 复制代码
p2.__proto__.__proto__.printName = function () {
  console.log('Ha');
};

p1.printName() // "Ha"

上面代码在**ColorPoint** 的实例**p2** 上向**Point** 类添加方法,结果影响到了**Point** 的实例**p1**。


参考资料:

详解JS原型,构造函数以及class之间的原型关系-腾讯云开发者社区-腾讯云

相关推荐
方见华Richard3 小时前
世毫九《认知几何学修订版:从离散概念网络到认知拓扑动力学》
人工智能·经验分享·交互·原型模式·空间计算
方见华Richard6 小时前
自指系统的安全本体论:论内生安全性的哲学基础与形式化路径
人工智能·经验分享·交互·学习方法·原型模式
xianyinsuifeng7 小时前
RAG + Code Analysis 的标准路线
数据仓库·自动化·云计算·原型模式·aws
Beginner x_u2 天前
JavaScript 原型、原型链与原型继承的核心机制解析
开发语言·javascript·原型模式·原型原型链
方见华Richard3 天前
递归对抗引擎(RAE)核心极简实现框架
人工智能·交互·学习方法·原型模式·空间计算
方见华Richard3 天前
递归对抗引擎RAE V2.0(多智能体分布式对抗版)
人工智能·经验分享·交互·学习方法·原型模式
方见华Richard3 天前
递归对抗引擎RAE V3.0(碳硅共生版)
人工智能·经验分享·学习方法·原型模式·空间计算
懵萌长颈鹿4 天前
原型模式 (Prototype Pattern)
原型模式
2601_949480065 天前
Flutter for OpenHarmony音乐播放器App实战:定时关闭实现
javascript·flutter·原型模式
方见华Richard6 天前
解构对话本体论:实验设计与认知重构
人工智能·交互·学习方法·原型模式·空间计算