jinmokai's blog logo

原型继承那些事

2025-01-12

原型继承那些事,零散的总结

一个对象 内部存在一个内部链接连接到原型对象 该原型对应因为也是对象也存在自己的原型对象

原型链

我的理解:

首先声明:统一将proto或[[prototype]]称为隐式原型,将 prototype 称为显式原型

一个普通对象的创建 const obj = {},其本质是通过 Object 构造函数创建的,即 const obj = new Object(),所以 obj 的隐式原型指向 Object 的显示原型,因为 obj 的隐式原型指向的 Object 的显示原型本质上就是 Object.prototype,这里特殊所以 Object.prototype 的隐式原型指向的是一个 null 话说回来,Object 的构造函数其实就是 new Function()创建的,所以 Object 的隐式原型指向的是 Function 的显示原型,因为 Function 的显示原型其实是一个对象也就是 new Object 创建的,所以 Function 的隐式原型指向 Object 的显示原型 特殊最后 Function 的构造函数的隐式原型指向的是 Function 原型

创建a对象原型链图全貌

创建a对象以及添加user实例 原型链图全貌

解释了控制台中proto和[[prototype]]的区别

[[prototype]]这只是 Chrome 控制台的开发者决定采取的行为。事实上,有一段时间,Chrome 控制台甚至没有 [[Prototype]] ,而是用 proto 来表示现在所用的 [[Prototype]] 。这很令人困惑,因为它们并不相同。每个对象都有一个 [[Prototype]] (即使为 null),但并非所有对象都有 protoproto 是从 Object.prototype 继承的 getter,如果你没有从那个继承,你就没有它。 基本上,这个想法是,至少就获取器而言,在控制台上下文中, [[Prototype]] 的层次结构更多的是一种分组,而不是一个层次路径。您在 [[Prototype]] 下看到的值仍然属于根对象;它们只是被隐藏在一个默认情况下隐藏它们的 [[Prototype]] 分组中。这种分组恰好也反映了实际的原型层次结构。所以看到

newPerson
    name
    [[Prototype]]
        constructor
        [[Prototype]]
            constructor
            __proto__
newPerson
  name
  constructor
  __proto__

与组。所以访问 proto (通过所有 [[Prototype]] )就像访问 newPerson.proto

https://www.reddit.com/r/learnjavascript/comments/z6z7mi/why_does_the_console_show_prototype_and_proto_in/

出现[[prototype]]在 chrome 控制台是 chrome92 版本,2021 年 4 月份左右,以便更好的跟随 firefox 开发者工具

原型语法反映了它是一个内部槽位(也在这里),仅由调试器暴露,而不是某个正常属性。使用 proto 作为别名会引起一些混淆,因为对象实际上可以有那个名称的正常属性,而且还有一个旧的已弃用的 Object.prototype.proto getter/setter,它会访问[[原型]]。

https://stackoverflow.com/questions/75718019/display-of-proto-vs-prototype-in-chrome-developer-tools

https://issues.chromium.org/issues/40565034 https://issues.chromium.org/issues/40759936 https://docs.google.com/document/d/1Xetnc9s6r0yy4LnPbqeCwsnsOtBlvJsV4OCdXMZ1wCM/edit?tab=t.0

javascript 一切皆为对象?

这句话其实不够准确,更准确的说法是: JavaScript 中的值可以分为原始类型和对象类型 原始类型包括:boolean、number、string、null、undefined、symbol、bigint 原始类型有对应的包装对象(除了 null 和 undefined)

当你调用原始类型的方法时,JavaScript 会临时创建一个包装对象

// 临时包装对象的过程
const str = "hello";
str.toUpperCase(); // JavaScript临时创建String对象,调用方法后销毁

// 等同于
new String("hello").toUpperCase();

这就是为什么原始类型看起来”像对象”,但实际上它们不是对象。

给数组添加属性可取吗?

数组本质其实就是一个特殊的对象,所以自然也能给数组添加属性,但是这种方式好不好? 不推荐在生产环境大量使用

  • 可能导致代码难以理解
  • 影响代码可维护性
  • 可能造成性能问题

new Boolean

new Boolean(true) === true 打印结果是 false 常用的解释式一个是包装类型一个是基本类型,所以两者不相等,我们可以通过 typeof 来查看

typeof new Boolean(true); // "object"
typeof Boolean(true); // "boolean"
typeof true; // "boolean"

但是值得注意的是

const b = new Boolean(true);

if (b) {
  console.log("true");
} else {
  console.log("false");
}

// 这里会打印true,也就是说在执行的时候Boolean对象会被自动转换为原始布尔值

在打印 new Boolean(true)时在控制台中,你可以看到[[prototype]]展开出现[[prototype]]和一个[[PrimitiveValue]]

new Boolean()打印结果图

本次使用 chrome 浏览器

那[[PrimitiveValue]]这个是什么呢

ecma 官方解释:

Internal state information associated with this object. Of the standard built-in ECMAScript objects, only Boolean, Date, Number, and String objects implement [[PrimitiveValue]].

译文:与该对象关联的内部状态信息。在标准内置 ECMAScript 对象中,只有 Boolean、Date、Number 和 String 对象实现了[[PrimitiveValue]]。

参考 https://262.ecma-international.org/5.1/