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之间的原型关系-腾讯云开发者社区-腾讯云

相关推荐
前端郭德纲1 天前
JavaScript 原型相关属性详解
开发语言·javascript·原型模式
yaaakaaang1 天前
五、原型模式
java·原型模式
时寒的笔记1 天前
js基础05_js类、原型对象、原型链&案例(解决无限debugger)
开发语言·javascript·原型模式
weixin_307779134 天前
使用COPY INTO从S3导入CSV文件到Snowflake表的问题分析与自动化验证方案
运维·自动化·原型模式
UXbot5 天前
UXbot 是什么?一句指令生成完整应用的 AI 工具
前端·ai·交互·个人开发·ai编程·原型模式·ux
WZTTMoon6 天前
Spring Prototype Bean的四种正确使用方式
spring·原型模式
梓贤Vigo6 天前
【Axure教程】字母定位选择器
交互·产品经理·axure·原型·中继器
Irene19918 天前
2、智能冰箱管家项目原型(墨刀AI)
原型·智能冰箱管家
梓贤Vigo8 天前
【Axure原型分享】能上下拖动和滚动查看内容的中继器表格
交互·产品经理·axure·原型·中继器
xy34538 天前
Axure 9.0 原生组件:让折线图实现动态交互(文本标签)
ui·交互·axure·原型·折线图