我想我终于“明白”了 JS 对象
在学习 JavaScript 时,我曾查找过“面向对象”的含义。
那是一个错误。
第一个原因是我接触了太多没用的 OOP 图。你知道这种图:
这张来自 Wikimedia Commons 的图表很典型:它模拟了一些你在真实程序中永远不会编写的代码。(为什么这种情况如此常见?)
另一个错误是,它让我了解了 Java 和 C++ 程序员对对象的看法……但这些看法与我正在学习的语言几乎无关。我读过“信息隐藏”、“封装”和“多态”等概念,但我不明白它们之间有什么关系。
事实上,它们大多不相关!
-
有些术语指的是其他语言中很难的技术,但在 JavaScript 中却很容易,你甚至不需要考虑它们。
-
一些术语是关于低级语言如何优化性能,而 JavaScript 则太高级而不需要关心。
-
一些术语描述了JavaScript 对象甚至无法做到的技术。
-
最后,有些行话指的是不好的想法——即使在其起源语言中也是如此。
多年来,我隐约意识到似乎没有必要摆弄原型、类、构造函数以及this
98% 的代码——那么为什么我需要在所有事情上都使用它们,或者我没有“正确地”进行面向对象?
事实证明,面向对象的代码在某些方面很出色,但并非所有方面
后来我意识到,JavaScript 会将其强大的对象重用于许多非面向对象的目的,其中大多数都简单得多。我使用 JS 对象大致有以下 4 种用途:
- 分组变量
- 命名空间
- 类型化数据
- 实际满功率物体
前 3 个不是“真正的”面向对象(或无论其含义如何),但它们重用了 JavaScript 已有的对象机制,因为它很方便。
1. 分组变量
有时,你会想要将相关的变量收集在一起。其他语言中,字典、哈希、映射或结构体等功能都支持这种功能。Perl本身就有 6 种方法来实现这一点,例如“BagHash”。
这就像为一起使用的变量名称添加前缀:
var player_x = 0;
var player_y = 0;
var player_health = 100;
// or:
var player = {
x: 0,
y: 0,
health: 100
};
通常,其他语言中对变量进行分组的具体细节是出于性能方面的考虑。例如,C 语言的结构体会将值分组,以提高字节效率,但如果您希望在运行时按名称查找属性,则需要使用哈希函数/哈希表。
这些分组变量作为对象也用于:1.) 返回多个值;2.) 以友好的方式实现可选参数;以及 3.) 语言不支持一次传递多个变量的其他情况。作为反例,Python 具有可选的关键字函数参数 和 多个返回值,因此它不使用对象来实现这两种情况。但在 JavaScript 中,我们使用对象和对象附件来实现这两种情况:
function getCoordinates (
target,
{ relativeTo } // Using object destructuring for optional arguments
) {
let ret = { x: 0, y: 0, relativeTo };
if (relativeTo) {
ret.x = getHorizontalOffset(target, relativeTo[0]);
ret.y = getVerticalOffset(target, relativeTo[1]);
} else {
ret.x = getHorizontalOffset(target);
}
return ret; // Returning 3 related variables in the same object
}
2. 命名空间
这个物体的存在没有任何技术原因。但它有人类的原因:很容易记住这个物体里有与数学相关的东西。Math
它们可以是带前缀的独立标识符math_
——老语言一直都是这么做的。但这似乎更简洁一些;对比一下PHP 全局作用域中的无数函数。
现代 JavaScript 现在使用模块import
s 和export
s来处理命名空间,但您仍然可以将它们包装到对象中,语法类似,并且许多旧的浏览器 API 和库仍然使用对象进行命名空间。
(这也称为static
属性/方法,这个名字有点糟糕,并且感染了其他不那么有趣的编程语言的 JS。请参阅@TC39。)
3. 类型化的数据对象
类型化的数据对象(我刚刚发明的一个非常严格的术语)将数据与读取和操作所述数据的方式捆绑在一起,例如Date
,DOMRect
或Array
。
- 类型化的数据对象
this
和方法变得有用——不使用的方法this
只是命名空间函数。 - 我认为这是有用的水平
instanceof
,但 JS/DOM 设计错误无论如何在 JavaScript 世界中流行了鸭子类型。
这些对象允许您以不同的方式更改其数据,并且读取该数据的不同方式将更新以匹配您的更改:
var d = new Date();
d.toDateString(); // "Sat Apr 11 2020"
d.setMonth(7);
d.toDateString(); // "Tue Aug 11 2020"
这类对象可以使用(并且经常使用)信息隐藏/内部表示,但这不是必需的。例如, aDate
仅将一个 UNIX 时间戳存储为整数,并且它甚至允许您根据需要直接查看和更改该整数。至于TypedArray,嗯,它的名字就说明了一切。
例如,一个Color
对象的构造函数接受任何 CSS 可以解析为颜色的字符串,然后将其扩展为具有R
、G
、B
和A
属性的对象。(这部分留给读者练习,因为它出奇地难。)
首先,一个形状如下的物体:
{
R: 255,
G: 40,
B: 177,
A: 255
}
…似乎并不比直接在 JS 中使用十六进制代码更有用0xff28b1ff
。但是通过创建实际的颜色,Object
我们new
可以添加一些有用的功能,例如:
- 我们可以使用setter强制执行
R
,G
并且B
永远不会超过 255 或低于 0 。 - 它可能具有比手动 RGB 修补更容易理解的方法
tint()
。shade()
- 该
Color
对象可以返回其十六进制代码的等效数字作为其.valueOf(),
输出的人性化hsla()
语法.toString()
,或输出到数组以进行数据传输.toJSON()
。
4. 全力大写O内部状态对象,也称为艺术
最后,你拥有了完全独立的对象,它们就像离散的……嗯,对象一样。比如 DOM 元素、Document
等等。它们会用到this
,而且需要用到 。(虽然可以在函数式编程中对这类东西进行建模,但这需要费尽心思去避免,this
最终你不得不发明that
。1 )
HTMLInputElement.validity.validate()
不过, 非常好用。它在一个特定的用户可见对象上使用一组属性,然后更改元素的操作以反映其计算结果。如果你.focus()
在一个 上调用该方法<input>
,它会更改其他 的属性以及拥有这些属性activeElement
的 的属性document
。
对于更复杂的例子,请考虑 jQuery 对象及其看似简单的 API:
$('.widget').addClass('loaded').fadeIn().on('click', activateWidget);
这里面有很多东西需要处理。但是,由于 jQuery 在底层实现了这些对象操作,它成为了一个经受住了时间考验的优秀 API。
无论如何,我还不是一个足够优秀的程序员,无法真正理解或从事第 4 级工作。
所以呢?
我的初稿原本想把这些内容与读者的学习联系起来,但我怀疑这篇文章对初学者来说太令人困惑,对高级开发人员来说又太过平凡,所以我把它发表出来,作为给自己的享受。
-
不,我不会解释我的意思 。↩