掌握 JS 中的对象🍨(第 2 部分)
对象和原型
与许多面向对象语言一样,JavaScript 支持实现继承:通过动态委托机制实现代码或数据的重用。但与许多传统语言不同,JavaScript的继承机制基于原型而非类。对于许多程序员来说,JavaScript是他们遇到的第一门没有类的面向对象语言。
在许多语言中,每个对象都是一个关联类的实例,关联类提供其所有实例之间共享的代码。相比之下, JavaScript没有内置的类概念。相反,对象继承自其他对象。每个对象都与某个其他对象关联,这些对象被称为原型。使用原型可能与使用类不同,尽管许多来自传统面向对象语言的概念仍然沿用。
prototype
了解和getPrototypeOf
之间的区别__proto__
原型包含三个独立但又相互关联的访问器,它们都以“原型”一词的变体命名。这种令人遗憾的重叠自然会造成不少困惑。让我们直奔主题。
C.prototype
用于建立new C()创建的对象的原型。Object.getPrototypeOf(obj)
是检索原型对象的标准ES5机制。obj
obj.__proto__
是一种检索原型obj
对象的非标准机制
为了理解这些,请考虑 JavaScript 数据类型的典型定义。User 构造函数需要使用 new 运算符调用,并接受名称和密码字符串的哈希值,并将
它们存储在其创建的对象中。
function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
User.prototype.toString = function () {
return "[User " + this.name + "]";
};
User.prototype.checkPassword = function (password) {
return hash(password) === this.passwordHash;
};
let u = new User("sfalken", "0ef33ae791068ec64b502d6cb0191387");
User函数带有一个默认的原型属性,其中包含一个初始状态几乎为空的对象User.prototype
。在本例中,我们向该对象添加了两个方法:toString
和。checkPassword.
当我们使用 new 运算符创建 User 实例时,生成的对象 u 会自动将存储在 处的对象User.prototype
赋值为其原型对象。下图展示了这些对象的示意图。
注意连接实例对象 u 和原型对象 的箭头User.prototype
。此链接描述了继承关系。
属性查找从搜索 的 开始object
;own properties
例如,u.name,并u.passwordHash
返回 u 的直接属性的当前值。在 u 上无法直接找到的属性将在 u 的原型中查找。u.checkPassword
例如,访问 会检索存储在 中的方法User.prototype
。
这引出了我们列表中的下一个项目。构造函数的prototype属性用于设置新实例的原型关系,而ES5函数Object.getPrototypeOf()
可
用于检索现有对象的原型。例如,在上面的示例中创建对象u之后,我们可以测试:
Object.getPrototypeOf(u) === User.prototype; // true
这说明了User构造函数和实例的原型关系。
某些环境会生成一种非标准机制,通过特殊属性来获取对象的原型
。这 对于不支持ES5的环境来说,可以作为一种权宜之计。在这样的环境中,我们可以类似地测试:__proto__
Object.getPrototypeOf
u.__proto__ === User.prototype; // true
关于原型关系的最后一点说明:JavaScript程序员经常将 User 描述为一个类,即使它只包含一个函数。JavaScript 中的类本质上是一个构造函数(User)和一个原型对象的组合,原型 对象用于在类的实例之间共享方法()。User.prototype
这是用户“类”的概念视图。
上图提供了一种从概念上理解 User类的好方法。User函数为该类提供了一个公共的构造函数User.prototype
,并且是实例之间共享方法的内部实现。User 和 u 的常规使用无需直接访问原型 对象。
要记住的事情🧠
C.prototype
确定由 new 创建的对象的原型C()
。Object.getPrototypeOf(obj)
是用于检索对象原型的标准 ES5 函数。obj.__proto__
是一种检索对象原型的非标准机制。- 类是一种由构造函数和相关原型组成的设计模式。
更Object.getPrototypeOf
喜欢__proto__
🦄
ES5引入了Object.getPrototypeOf
用于检索对象原型的标准API,但在此之前,许多JavaScript引擎早已提供了__proto__
用于相同目的的特殊属性。然而,并非所有JavaScript环境都支持此扩展,即使支持,也并非完全兼容。例如,不同环境对原型为空的对象的处理方式有所不同。在某些环境中,proto继承自Object.prototype
,因此原型为空的对象没有特殊的proto属性:
var empty = Object.create(null); // object with no prototype
"__proto__" in empty; // false (in some environments)
// In others, __proto__ is always handled specially, regardless of an object’s state:
var empty = Object.create(null); // object with no prototype
"__proto__" in empty; // true (in some environments
只要Object.getPrototypeOf
可用,它就是提取原型更标准、更便携的方法。此外,__proto__
由于该属性会污染所有对象,因此会导致许多错误
。目前支持该扩展的 JavaScript 引擎将来可能会选择允许程序禁用它,以避免这些错误。优先使用Object.getPrototypeOf
可以确保即使__proto__
禁用了它,代码也能继续工作。
对于没有提供ES5 API 的JavaScript环境,可以通过以下方式轻松实现:__proto__
if (typeof Object.getPrototypeOf === "undefined") {
Object.getPrototypeOf = function (obj) {
var t = typeof obj;
if (!obj || (t !== "object" && t !== "function")) {
throw new TypeError("not an object");
}
return obj.__proto__;
};
}
此实现可以安全地包含在 ES5 环境中,因为它避免在
Object.getPrototypeOf
已存在的情况下安装该函数。
要记住的事情🧠
-
优先选择符合标准的属性,
Object.getPrototypeOf
而不是非标准
的__proto__
属性。 -
在支持的
Object.getPrototypeOf
非 ES5 环境中实现。__proto__
切勿修改__proto__
🍕
特殊的proto属性提供了一项额外的功能Object.getPrototypeOf
:修改对象的原型链接。虽然这种功能看似无害(毕竟,它只是另一个属性,对吧?),但它实际上会带来严重的影响,应该避免。避免修改的最明显原因是可移植性:由于并非所有平台都支持 更改对象原型的功能,__proto__
因此您根本无法编写可移植的代码来实现这一点。
避免修改的另一个原因__proto__
是性能。所有现代JavaScript引擎都对获取和设置对象 属性的操作进行了深度优化,因为这些是 JavaScript 程序执行的最常见操作之一。这些优化建立在引擎对对象结构的认知之上。当你更改对象的内部结构时,例如,通过向对象或其原型链中的对象添加或删除属性,其中一些优化将失效。修改__proto__
实际上会改变继承结构本身,这是最具破坏性的更改。与修改普通属性相比,这会使更多的优化失效。
但避免修改的最大原因__proto__
是为了保持可预测的行为。对象的原型链通过确定其属性集和属性值来定义其行为。修改对象的原型链接就像给它做一次大脑移植:它会交换对象的整个继承层次结构。或许可以想象,在特殊情况下,这样的操作可能会有所帮助,但出于基本的理性考虑,继承层次结构应该保持稳定。
要使用自定义原型链接创建新对象,可以使用ES5的Object.create
。对于未实现 ES5 的环境,Item 33 提供了一个Object.create
不依赖于 的可移植实现__proto__
。
要记住的事情🧠
- 永远不要修改对象的
__proto__
属性。 - 用于为新对象
Object.create
提供自定义原型。
🎉🎉🎉 感谢您阅读本文的第二部分!🎉🎉🎉
别忘了看看这个系列的第三部分!🥳让你的构造函数与 new-Agnostic 兼容。
如果您想更深入地了解您最喜欢的编程语言,请查看我的个人博客,成为一名按需开发人员😉,您也可以在Twitter上找到我😃。
文章来源:https://dev.to/jrmatanda/master-objects-in-js-part-2-2fnn