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

news/2024/10/19 10:12:45/

大多数浏览器的 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属性。

class A {
}class B extends A {
}B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

上面代码中,子类B__proto__属性指向父类A,子类Bprototype属性的__proto__属性指向父类Aprototype属性。

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

class A {
}class B {
}// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);const b = new B();

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

Object.setPrototypeOf = function (obj, proto) {obj.__proto__ = proto;return obj;
}

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

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

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

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

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

B.prototype = Object.create(A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

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

class B extends A {
}

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

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

class A extends Object {
}A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

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

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

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__属性。也就是说,子类的原型原型,是父类的原型

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__属性,可以修改父类实例的行为。

p2.__proto__.__proto__.printName = function () {console.log('Ha');
};p1.printName() // "Ha"

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


参考资料:

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


http://www.ppmy.cn/news/1540230.html

相关文章

反欺诈与数字信任:保障数字经济安全的关键

随着互联网和数字化技术的快速发展,全球数字经济规模迅速扩张,但同时也面临着前所未有的安全威胁。其中,欺诈行为在数字环境中的泛滥成为了主要的安全挑战之一,反欺诈与数字信任技术因此成为了保障在线交易、数字身份和数据安全的…

记录一个vue比较奇怪的bug,在父组件使用定时器会影响子组件的深度监听属性

需求:在大屏项目的页面有个时间需要用到定时器,结果影响子组件的参数 实现:将定时器抽成一个单独的组件 描述:定时器没有单独抽出来的时候,professionalECharts组件接受的参数schoolId一直在变(通过watch发…

YOLO改进快速有效方案进行缝合

1. 明确模块 1.1 模块的作用与结构 1.2 制作一个即插即用模块 2. 缝入YOLO的对应位置 2.1 确定YOLO中模块的缝合位置 2.2 改写YOLO模型的 YAML 文件 2.3 测试与验证 2.4 模块微调与创新 结论 1. 明确模块 1.1 模块的作用与结构 在整合任何模块之前,首先要…

Shell脚本备份文件

需求:原文件备份,新文件覆盖掉源文件 vue项目打包自动化部署使用 假设已经将打包后得文件复制到了需要覆盖得目录得同级下 #!/bin/bashdst_folder"/home/compose/nginx/html"# 创建备份文件夹 backup_folder"$dst_folder/backup"# …

JNI(Java Native Interface)和NIO(New Input/Output)是什么?

1. JNI(Java Native Interface) JNI是一种接口,允许Java代码与其他编程语言(例如C或C)编写的本地代码进行交互。通过JNI,Java程序可以调用本地代码中的函数或库,反过来,本地代码也可…

Android Settings 设置项修改

Settings 设置项 在 Android 系统上,WRITE_SETTINGS 这个权限从 API 1 就已经开始有了。 通过在 app 中设置权限 android.permission.WRITE_SETTINGS 允许 app 读/写 系统设置。 在官方文档的描述中,还有一段注意事项: Note: If the app targets API level 23 or higher,…

妇女、商业与法律(WBL)(1971-2023年)

WBL项目由世界银行开发,旨在通过分析时间序列数据,研究女性机会不平等与劳动市场动态之间的关系。该项目提供了1971年至2023年的190个经济体的面板数据,包括8个评分指标和35个数据点,涵盖了流动性、工作场所、薪酬、婚姻、父母身份…

18-基于双TMS320C6678 DSP的3U VPX的信号处理平台

一、板卡概述 该板卡是由我公司自主研发的基于3U VPX架构的信号处理板,该处理板包含2片TI的TMS320C6678 DSP芯片,1片Xilinx公司的Spartan-3系列XC3S200AN配置芯片,两片DSP分别有1路RapidIO x4连接至VPX背板,两片DSP之间通过Hyperl…