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

相关推荐
想吃火锅100514 天前
【前端手撕】instanceof
前端·javascript·原型模式
UXbot14 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
UXbot15 天前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot15 天前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
sunny.day19 天前
js原型与原型链
开发语言·javascript·原型模式·js原型链
UXbot20 天前
AI网页开发工具能替代工具吗?5大平台对比
前端·人工智能·低代码·ui·原型模式·web app
weixin_3077791320 天前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
swordbob20 天前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
isNotNullX21 天前
企业数据中台建设,ETL工具选错了会踩哪些坑?
数据仓库·etl·原型模式
半个烧饼不加肉21 天前
JS 底层探究-- 普通函数和构造函数
开发语言·javascript·原型模式