js 对象解构_JS对象:解构

js 对象解构

JS对象:TL; DR (JS Objects: TL;DR)

JavaScript has been plagued since the beginning with misunderstanding and awkwardness around its "prototypal inheritance" system, mostly due to the fact that "inheritance" isn't how JS works at all, and trying to do that only leads to gotchas and confusions that we have to pave over with user-land helper libs. Instead, embracing that JS has "behavior delegation" (merely delegation links between objects) fits naturally with how JS syntax works, which creates more sensible code without the need of helpers.

从一开始,JavaScript就一直困扰着其“原型继承”系统,人们对其产生了误解和尴尬,这主要是由于“继承”根本不是JS的工作原理,而试图做到这一点只会导致我们陷入困境和困惑。必须铺平用户土地的帮助库。 相反,认为JS具有“行为委托”(仅是对象之间的委托链接)自然符合JS语法的工作原理,从而无需帮助者即可创建更明智的代码。

When you set aside distractions like mixins, polymorphism, composition, classes, constructors, and instances, and only focus on the objects that link to each other, you gain a powerful tool in behavior delegation that is easier to write, reason about, explain, and code-maintain. Simpler is better. JS is "objects-only" (OO). Leave the classes to those other languages!

当您搁置诸如混合包,多态性,组成,类,构造函数和实例之类的干扰,仅关注彼此链接的对象时,您将获得行为委托中的强大工具,该工具易于编写,推理,解释,和代码维护。 越简单越好。 JS是“仅对象”(OO)。 将课程留给其他语言!

由于感谢 (Due Thanks)

I'd like to thank the following amazing devs for their generous time in feedback/tech review of this article series: David Bruant, Hugh Wood, Mark Trostler, and Mark McDonnell. I am also honored that David Walsh wanted to publish these articles on his fantastic blog.

我要感谢以下出色的开发者在本文系列的反馈/技术评论中所花的大量时间:David Bruant,Hugh Wood,Mark Trostler和Mark McDonnell。 我也很荣幸David Walsh希望将这些文章发布在他的博客上。

完整系列 (Complete Series)

In part 1 of this article series (which you should totally go read if you haven't yet!), I revisited an idea not original to me: JS doesn't have "inheritance" in the traditional sense, and what it does have is more appropriately labeled "behavior delegation" -- the ability of one object to delegate a method or property access which it cannot handle over to another object which can handle it.

在本系列文章的第1部分中(如果您还没有阅读,应该完全阅读!),我重新审视了一个并非我独有的想法: JS没有传统意义上的“继承”,它确实具有被更恰当地标记为“行为委托”-一个对象将无法处理的方法或属性访问委托给可以处理该对象的另一对象的能力。

Then, in part 2, I addressed several distractions which I think obfuscate JS's true object-oriented identity, including "custom types", "mixins", "polymorphism" (which we'll come back to again later), and even the new "class syntax" coming in ES6. I suggested that to understand (and leverage) better the [[Prototype]], we needed to strip away the cruft. Here, I will attempt to do that.

然后,在第2部分中,我讨论了一些让 困惑的JS真正的面向对象身份的干扰 ,包括“自定义类型”,“ mixins”,“多态性”(我们稍后将再次介绍)。 ES6中引入了“类语法”。 我建议为了更好地理解(并利用) [[Prototype]] ,我们需要剥离所有内容。 在这里,我将尝试这样做。

海龟 一路物体 向上 (Turtles Objects all the way down up)

The key realization, the punchline to this entire article series, is that [[Prototype]] is really only about linking one object to another object, for the purposes of delegating, if the first object cannot handle a property or method access but the second can. In other words, it's only objects, linked to other objects. That's really all JS has.

整个实现系列文章的重点是, [[Prototype]]实际上仅是为了将一个对象链接到另一个对象,如果第一个对象不能处理属性或方法访问权限,而第二个对象不能处理属性或方法访问权限能够。 换句话说,它只是链接到其他对象的对象。 这就是JS拥有的全部。

In a sense, JS is the purest essence of a "object-oriented (OO)" language, in that it really is all about objects. In contrast to most other languages, JS is somewhat unique that you can actually create objects directly without the notion of classes or other abstractions. That's a powerful and brilliant feature!

从某种意义上说,JS是“面向对象(OO)”语言的最纯粹的本质,因为它实际上完全关于对象的。 与大多数其他语言相反,JS在某种程度上是独特的,您实际上可以直接创建对象而无需类或其他抽象概念。 这是一个强大而辉煌的功能!

People often bash JavaScript, but it's one of the few prog languages that let you directly create objects. Others: Lua, Dylan, Cecil.

人们经常猛击JavaScript,但这是使您直接创建对象的少数编语言之一。 其他:Lua,Dylan,Cecil。

— Axel Rauschmayer (@rauschma) April 9, 2013

— Axel Rauschmayer(@rauschma) 2013年4月9日

JavaScript legitimately is "object-oriented", and perhaps we shouldn't have used that term for the other languages which imply a lot more than just "objects". Maybe "class-oriented" would have been more accurate, which would have freed us up to use "object-oriented" for JS. Of course, as I argued in part 1, what everybody means when they use some term, matters, so it's far too late to redefine or bend the commonly accepted "object-oriented" to my own purposes, much as I'd like to.

JavaScript合法地 “面向对象的”,也许我们不应该在其他语言中使用该术语,这意味着不仅仅是 “对象”。 也许“面向类”会更加准确,这将使我们腾出更多空间来使用JS的“面向对象”。 当然,正如我在第1部分中所论证的那样, 每个人在使用某个术语时都意味着什么很重要 ,因此重新定义或弯曲通常被接受的“面向对象”以实现自己的目的为时已晚,就像我想要的那样。 。

I'm mildly tempted, however, to just hijack the abbreviation of "OO" to mean "objects-only" instead of "object-oriented", but I bet that probably wouldn't get anywhere, either. So, for our purposes here, let's just sayJavaScript is "object-based (OB)" to clarify against "object-oriented (OO)".

但是,我有些诱惑,只是劫持了“ OO”的缩写,以表示“仅对象”而不是“面向对象”,但是我敢打赌,也可能不会有任何结果。 因此,就我们的目的而言,我们只说JavaScript是“基于对象(OB)”的,以明确“面向对象(OO)”的含义。

Whatever we call it, we normally tap into this object mechanism by following the "OO way": we create a function which we use as a "constructor", and we call that function with new so that we can "instantiate" our "class", which we specify with the constructor function together with its subsequent .prototype additions... but all that is like a magician's sleight of hand that dazzles you over here to distract you from what's really going on over there.

不管我们叫什么,我们通常都按照“ OO方式”利用此对象机制:我们创建一个用作“构造函数”的函数,并使用new调用该函数,以便我们可以“实例化”我们的“类” ”我们与构造函数一起指定其后续.prototype增加......但这一切就像是手的魔术师的戏法是炫你在这里从到底发生了什么那边让你分心。

What really matters, at the end of the trick, is that two objects end up linked to each other via the[[Prototype]] chain.

最后,真正重要的是两个对象最终通过[[Prototype]]链彼此链接

Codez Plz (Codez Plz)

Before we can derive and understand that simpler view of "objects-only" or "object-based", we need to understand what actually gets created and linked when we build up some "inherited" objects in JavaScript. Not only are we going to see what happens by default, but what doesn't happen.

在获得和理解“仅对象”或“基于对象”的更简单视图之前,我们需要了解在JavaScript中构建一些“继承”对象时实际创建和链接的内容。 我们不仅会看到默认情况下还会发生什么,而且不会发生什么。

Take this code for our main example:

将此代码用作我们的主要示例:

function Foo(who) {
    this.me = who;
}

Foo.prototype.identify = function() {
    return "I am " + this.me;
};

function Bar(who) {
    Foo.call(this,who);
}

Bar.prototype = Object.create(Foo.prototype);
// NOTE: .constructor is borked here, need to fix

Bar.prototype.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = new Bar("b1");
var b2 = new Bar("b2");

b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."


Note: Some people write Bar.prototype = Object.create(Foo.prototype); as Bar.prototype = new Foo();. Both approaches end up with the same linked objects, where Bar.prototype is an object linked via its[[Prototype]] to Foo.prototype. The only real difference is whether or not the Foo function is called during the creation of Bar.prototype. Depending on your circumstances and intent, you may or may not want that to happen, so let's consider them roughly interchangable but with different purposes.

注意:有人写Bar.prototype = Object.create(Foo.prototype);Bar.prototype = new Foo(); 。 两种方法都以相同的链接对象结束,其中Bar.prototype是通过[[Prototype]]Foo.prototype 。 唯一真正的区别是在创建Bar.prototype期间是否调用Foo函数。 根据您的情况和意图,您可能会或可能不希望发生这种情况,因此,让我们考虑一下它们可以互换,但目的不同。

What we have is an object labeled Foo.prototype with an identify() method, and another object calledBar.prototype with a speak() method. Bar.prototype is a new empty object that is [[Prototype]]-linked to Foo.prototype. Then we have two objects b1 and b2, who each are each respectively linked via their own [[Prototype]] to Bar.prototypeb1 and b2 also have an "owned property" directly on each of them called me, which respectively holds the values "b1" and "b2".

我们拥有的是一个带有identify()方法的标为Foo.prototype的对象,另一个是带有speak()方法的名为Bar.prototype对象。 Bar.prototype[[Prototype]]Foo.prototype新的空对象 。 然后我们有两个对象b1b2 ,它们分别通过各自的[[Prototype]]分别链接到Bar.prototypeb1b2在它们各自称为me每个对象上也直接具有“私有财产”,分别拥有值“ b1”和“ b2”。

Let's take a visual look at the relationships implied by the above code snippet:

让我们直观地看一下上面的代码片段所隐含的关系:

Note: All the [[Prototype]] links in the diagram also mention a ".__proto__" property__proto__ is a formerly non-standard property (which exists in most but not all JS environments) to expose the internal [[Prototype]]chain. As of ES6, however, it will be standardized.

注意:图中的所有[[Prototype]]链接也都提到了“ .__ proto__”属性__proto__是以前的非标准属性(在大多数但并非所有JS环境中都存在),用于公开内部[[Prototype]]链。 从ES6开始,它将被标准化。

I left a whole bunch of detail out of that diagram, intentionally, so it was even remotely digestable. But of course, since JS is all objects, all the linkages and ancestry of each item can be fully traced. We'll come back to all the omitted parts of this diagram in a little bit.

我故意从该图中遗漏了很多细节,因此甚至可以远程消化。 但是,当然,由于JS是所有对象,因此可以完全跟踪每个项目的所有链接和血统。 我们将稍微回到该图的所有省略部分。

Note in this diagram that the function constructors all have a .prototype property pointing at an object. As we've been suggesting, the object is what we really care about, and in this way of viewing the JS object mechanism, the way we get that object is to look at a constructor function's .prototype. The function doesn't really serve any particularly important role.

请注意,在此图中,函数构造函数均具有指向对象的.prototype属性。 正如我们一直建议的那样,对象是我们真正关心的对象,通过这种方式查看JS对象机制,我们获得该对象的方式就是查看构造函数的.prototype 。 该功能实际上并没有发挥任何特别重要的作用。

I know a whole bunch of you just screamed out, "sure it does! it runs constructor code to initialize the new object!" OK, you're technically right. Foo() has some code in it which is ultimately run against b1 and b2.

我知道你们中的所有人都大声喊叫:“确定可以!它运行构造函数代码来初始化新对象!” 好的,您在技术上是正确的。 Foo()包含一些代码,最终会针对b1b2运行。

But the devil's always in the details. First, we don't need a constructor function to run such code. That's just one way of getting that outcome. And I'm going to suggest it's a more distracting approach.

但是魔鬼总是在细节中。 首先,我们不需要构造函数来运行此类代码。 那只是获得结果的一种方法。 我建议这是一种更分散注意力的方法。

Secondly, unlike C++, the base-class/superclass Foo() "constructor" doesn't automatically get called when you run the child-class Bar() "constructor" to make b1 and b2. So, like Java, we have to manually call theFoo() function from Bar(), but unlike Java, we have to do so with a variation of the explicit "mixin" pattern (I'd probably call it "implicit mixin" here) to make it work like we expect. That's an ugly detail that is very easy to forget or get wrong.

其次, 与C ++不同 ,运行子级Bar() “构造函数”以生成b1b2时,基类/超类Foo() “构造函数”不会自动被调用。 因此,就像Java一样,我们必须从Bar()手动调用Foo()函数,但是与Java不同,我们必须使用显式“ mixin”模式的变体(我可能将其称为“隐式mixin”在这里),使其按照我们的预期工作。 这是一个丑陋的细节,很容易忘记或出错。

So, where you'd probably argue with me that "constructor" functions are useful being automatically called at the construction of an object, I'd point out that this is true for only the immediate level, not for the entire "chain of inheritance", which means that automatic behavior is pretty limited/shallow in utility.

因此,在您可能与我争论“构造函数”对在构造对象时自动调用很有用的情况下,我指出,这仅适用于直接级别,而不适用于整个“继承链” ”,这意味着实用程序的自动行为非常有限/浅。

多态性redux (Polymorphism redux)

Moreover, we see here the first hint of the problems with relative polymorphism in JS: you can't do it! I can't tellBar() to automatically and relatively call his ancestor constructor(s), via a relative reference. I have to manually call (aka, "borrow") the Foo() function (it's not a constructor here, just a normal function call!) from inside ofBar(), and to make sure the this is bound correctly, I have to do the slightly more awkward .call(this)style of code. Ugh.

此外,我们在这里看到了JS中相对多态性问题的第一个提示: 您做不到! 我不能告诉Bar()通过相对引用自动相对地调用他的祖先构造函数。 我必须从Bar()内部手动调用(又称“借用”) Foo()函数(这里不是构造函数,只是普通的函数调用! Bar() ,并确保this绑定正确,做稍微尴尬的.call(this)代码风格。 啊。

What may not be obvious until you go back and look closer at the diagram above is that the Foo() function isnot related in any useful/practical way to the Bar() function. The Foo() function does not even appear in the "inheritance" (aka "delegation") chain of Bar.prototype object. The fact that there are some lines you can follow on the graph for indirect relationships doesn't mean that those relationships are what you'd want to rely on in your code.

直到您回过头来仔细查看上图时,可能看不出来的是Foo()函数与Bar()函数没有任何有用/实用的关系。 Foo()函数甚至没有出现在Bar.prototype对象的“继承”(也称为“委托”)链中。 您可以在图形上为间接关系遵循一些线这一事实,并不意味着这些关系就是您想要在代码中依赖的关系。

The problem with polymorphism we're seeing here is not only for "constructor" functions. Any function at one level of the [[Prototype]] chain that wants to call up to an ancestor with the same name must do so via this manual implicit mixin approach just like we did inside of Bar() above. We have no effective way of making relative references up the chain.

我们在这里看到的多态问题不仅是针对“构造函数”的。 想要调用具有相同名称的祖先的[[Prototype]]链一级的任何函数都必须通过此手动隐式mixin方法进行操作,就像我们在上面的Bar()所做的那样。 我们没有有效的方法来建立相对引用链。

Importantly, this means that not only do we establish the link between Bar and Foo once at "class" definition, but every single polymorphic reference also has to be hardcoded with the direct relationship. This significantly decreases the flexibility and maintainability of your code. As soon as you make a function hard-coded with implicit mixin to an "ancestor", now your function can't be "borrowed" as easily by other objects without those possible unintended side effects.

重要的是,这意味着我们不仅要在“类”定义中一次建立BarFoo之间的链接,而且每个单个多态引用也必须使用直接关系进行硬编码。 这大大降低了代码的灵活性和可维护性。 一旦使用隐式mixin将函数硬编码到“祖先”,现在就不能轻易地将其借用其他对象“借用”,而不会产生那些可能的意外副作用。

OK, so let's say you agree with me at this point that polymoprhism in JS is more trouble than it's worth. Using constructor-based coding to wire JS objects to each other forces you into the problems of polymorphism.

好的,让我们在这一点上说您同意我的观点,即JS中的多态性比它的价值更大。 使用基于构造函数的编码将JS对象彼此关联会迫使您陷入多态问题

.constructor (.constructor)

Another detail that's easy to miss is that an object's .constructor property really doesn't behave like we'd probably expect. It's correct at the Foo() level of the graph, but below that, at Bar() and b1 / b2, notice that the implied linkage there shows .constructor references, strangely, still pointing at Foo.

容易遗漏的另一个细节是,对象的.constructor属性的行为确实不像我们期望的那样。 在图的Foo()级别上是正确的,但在下面的水平上,在Bar()b1 / b2 ,请注意那里的隐式链接显示.constructor引用,奇怪的是,它仍然指向Foo

Actually, what this means is that the only time a .constructor property is added to an object is when that object is the default .prototype attached to a declared function, as is the case of Foo(). When objects are created via new Fn() or Object.create(..) calls, those objects don't get a .constructor added to them.

实际上,这意味着将.constructor属性添加到对象的唯一时间是该对象是附加到已声明函数的默认 .prototype ,就像Foo() 。 通过new Fn()Object.create(..)调用创建对象时,这些对象不会添加.constructor

Let me say that again: an object created by a constructor doesn't actually get a .constructor property to point to which constructor it was created by. This is an extremely common misconception.

我再说一遍:由构造函数创建的对象实际上并没有获得.constructor属性来指向其创建者。 这是一个非常普遍的误解。

So if you reference b1.constructor for instance, then you're actually going to delegate up the chain a few links, to Foo.prototype. Of course, Foo.prototype has a .constructor property and it's pointing at Foo like you'd expect.

因此,例如,如果您引用b1.constructor ,那么您实际上将向该链委托一些链接,即Foo.prototype 。 当然, Foo.prototype具有一个.constructor属性,它像您期望的那样指向Foo

What's that mean? In the above snippet, right after you perform Bar.prototype = Object.create(Foo) (or even if you'd done Bar.prototype = new Foo()), if you plan to rely on the .constructor property (which many do), you need to perform an extra step, right where I put the JS "Note:" comment:

那是什么意思? 在上面的代码段中,如果您打算依赖.constructor属性(其中有很多),则在执行Bar.prototype = Object.create(Foo) (或者即使您已经完成Bar.prototype = new Foo() )之后),您需要执行一个额外的步骤,就在我在JS上添加“注意:”的注释:

//...
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar; // <-- add this line!
//...

Then b1.constructor references will delegate to that Bar.prototype level, and will "correctly" point at Bar()as you'd probably have expected. Ugh...**more syntax gotchas** that user-land libs always have to "fix" for us.

然后, b1.constructor引用将委派到该Bar.prototype级别,并将“正确”指向您可能期望的Bar() 。 ...... **更多语法陷阱**用户级库始终必须为我们“修复”。

Furthermore, the fact that Foo.prototype has a .constructor property pointing at Foo is strange, when you think about "constructor" the way most people do. It's nice that it gives objects created by new Foo() a way to delegate to a .constructor property access and find Foo(), but it's bizarre where .constructor actually lives.

此外,当您以大多数人的方式考虑“构造函数”时, Foo.prototype具有指向Foo.constructor属性的事实很奇怪。 很好的是,它使new Foo()创建的对象可以委托给.constructor属性访问并查找Foo() ,但是.constructor实际存在的地方很奇怪。

It implies that Foo() constructed Foo.prototype, but that's nonsense. Foo() had nothing to do with creating the default Foo.prototypeFoo.prototype defaults to an empty object that was actually constructed by the built-in Object() constructor.

这意味着Foo()构造了Foo.prototype ,但这是胡说八道。 Foo()与创建默认的Foo.prototypeFoo.prototype默认为一个由内置Object()构造函数实际构造的空对象。

So we have to change how we think of what the .constructor property means. It does not mean "the constructor this object was created by". It actually means "the constructor which creates any objects that end up getting [[Prototype]] linked to this object." Subtle but super important difference to get straight.

因此,我们必须更改对.constructor属性含义的.constructor 。 这并不意味着“这个对象被创建的构造函数”。 实际上,它的意思是“创建最终以[[Prototype]]链接到该对象的对象的构造函数”。 微妙而又超重要的区别才能直达。

Point? These confusions only happen/matter if you're using constructor-style code, so it's the choice of this style of code that opts you into the problems. You don't have to live with that pain. There's a better, simpler way!

点? 仅当您使用构造函数样式的代码时,才会发生/混淆这些事情,因此, 选择这种样式的代码会使您陷入困境。 你不必忍受这种痛苦。 有更好,更简单的方法!

整个馅饼 (The Whole Pie)

Now let's look at everything that's actually implied by the above snippet of code. Ready for the whole messy thing?

现在,让我们看一下以上代码片段所隐含的所有内容。 准备好处理整个混乱的事情了吗?

Take a few minutes to just take all that in. Why show you such a complex diagram?

花几分钟时间将所有内容全部收录。为什么要显示这么复杂的图?

This diagram actually shows you where some of JavaScript's functionality comes from, where before you might have just never considered how it all worked. For instance, have you wondered how all functions are able to use behavior such as call()apply()bind(), etc? You may have assumed each function has that behavior built-in, but as you can see from this diagram, functions delegate up their [[Prototype]] chain to handle those behaviors.

该图实际上向您显示了一些JavaScript功能的来源,以及您可能从未考虑过所有功能的来源。 例如,您是否想知道所有函数如何能够使用诸如call()apply()bind()等行为? 您可能已经假定每个函数都内置了该行为,但是从该图可以看出,函数委托了它们的[[Prototype]]链来处理这些行为。

While the behavior delegation part is sensible and useful, consider all the implied complexity of constructor-style coding as visualized here. It's pretty tough to trace all the different entities and diagrams and make much sense of it all. A lot of that complexity comes from the function constructors. (here's the same complete graph but with the implied relationship lines omitted, if that helps to digest)

虽然行为委派部分是明智且有用的,但请考虑此处显示的所有构造函数式编码隐含的复杂性 。 跟踪所有不同的实体和图并使其具有很多意义是非常困难的。 这种复杂性很大一部分来自函数构造函数。 (这是相同的完整图形,但是省略了隐含的关系线 ,如果这样有助于消化的话)

If you take that diagram, and remove all the functions and any associated arrows (which we'll see in just a moment), you're left with "objects-only", and you'll have a much more simplified view of the JS objects world.

如果拿这份图表,并删除所有的功能和任何相关的箭头(我们会看到在短短的时刻),你留下了“对象-只有”,你就会有的更加简化视图JS对象世界。

更简单:对象->对象 (Simpler: Object -> Object)

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. --Antoine de Saint-Exupery

达到完美,不是在没有其他可添加的东西时,而是在没有其他东西可取的时候。 -安东尼·圣艾修伯里

For refresher, the same prototype-style code from above:

对于复习,上面相同的原型样式代码:

function Foo(who) {
    this.me = who;
}

Foo.prototype.identify = function() {
    return "I am " + this.me;
};

function Bar(who) {
    Foo.call(this,who);
}

Bar.prototype = Object.create(Foo.prototype);
// NOTE: .constructor is borked here, need to fix

Bar.prototype.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = new Bar("b1");
var b2 = new Bar("b2");

b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."

Now, let's instead consider this alternative snippet of code, which accomplishes exactly the same, but it does so without any of the confusion/distraction of "constructor functions", new.prototype, etc. It just creates several objects and links them together.

现在,让我们考虑一下这替代代码段,这些代码段实现的功能完全相同,但是这样做不会造成“构造函数”, new.prototype等的任何混乱/分散。它只是创建了多个对象并将它们链接在一起。

var Foo = {
    init: function(who) {
        this.me = who;
    },
    identify: function() {
        return "I am " + this.me;
    }
};

var Bar = Object.create(Foo);

Bar.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");

b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."

Let's try to take some comparison looks between this snippet and the previous one. They both accomplish the same thing, but there's some important differences in how we get there.

让我们尝试对该代码段和上一个代码段进行一些比较。 他们俩都完成同一件事,但是我们到达那里的方式存在一些重要差异。

First of all, Bar and Foo are now just objects, they're not functions or constructors anymore. I left them as capital letters just for the symmetry and because some people feel better with them. They make it clear that the objects being linked are what we cared about all along, so instead of the indirectness of linking Bar.prototype toFoo.prototype, we just make Foo and Bar objects themselves and link themAND, we only need one line of code to link them, instead of the extra ugly polymorphic linkage. Bam!

首先, BarFoo现在只是对象 ,它们不再是函数或构造函数。 我把它们留为大写字母,只是为了保持对称,因为有些人对它们感觉更好。 它们清楚地表明,链接的对象一直是我们一直关心的问题,因此,不是将Bar.prototype链接到Foo.prototype的间接性,我们只是使FooBar对象本身并链接它们AND ,我们只需要一行代码来链接它们,而不需要额外的难看的多态链接。 am!

Instead of calling function constructors like new Bar(..), we use Object.create(..), which is an ES5 helper that allows us to create a new object and optionally provide another object to [[Prototype]] link it to. We get the same outcome (object creation and linkage) as a constructor call, but without needing the constructor. BTW, there's a simple non-ES5 polyfill for Object.create(..), so you can safely use this style of code in all browsers without concern.

我们不使用new Bar(..)这样的函数构造函数,而是使用Object.create(..) ,它是ES5的帮助程序,允许我们创建一个新对象,并可以选择提供另一个对象以[[Prototype]]将其链接到。 我们得到与构造函数调用相同的结果(对象创建和链接),但不需要构造函数。 顺便说一句, Object.create(..)有一个简单的非ES5 polyfill ,因此您可以放心地在所有浏览器中使用这种样式的代码。

Secondly, notice that because we're not worried about constructors anymore, we have eliminated any concerns about awkward polymorphisms forcing us to do manual implied mixins to call Foo() from Bar(). Instead, we put the code we wanted to run to initialize our objects into a init() method, on Foo, and we can now callb1.init(..) directly via the delegation chain and it "magically" just works like we want.

其次,请注意,由于我们不再担心构造函数,因此消除了对笨拙多态性的任何担心,这些麻烦迫使我们执行手动隐式mixins从Bar()调用Foo() Bar() 。 相反,我们将要运行以将对象初始化的代码放入Foo上的init()方法中,现在我们可以直接通过委托链调用b1.init(..) ,它“神奇地”就像我们一样工作想。

So, we have a tradeoff here. We don't get automatic constructor calls, which means we create the object likevar b1 = Object.create(Bar) and then we have to additionally call b1.init("b1"). That's "more code".

因此,我们在这里进行权衡。 我们没有得到自动的构造函数调用,这意味着我们创建了像var b1 = Object.create(Bar)这样的对象,然后我们不得不另外调用b1.init("b1") 。 那就是“更多代码”。

But the benefits we get, which I think are much better and well worth it, are no awkwardness with the linkage between Foo and Bar -- instead we leverage [[Prototype]] delegation to get at the code reuse ininit(). Also, no more verbose/repetitive .prototype references, and neither do we need to use.call(this) nearly as often (especially if we avoid polymorphism!).

但是我们获得的好处( 我认为是更好和值得的)FooBar之间的联系并不尴尬-相反,我们利用[[Prototype]]委托来获得init()中的代码重用。 而且,不再有冗长/重复的.prototype引用,并且我们也几乎不需要频繁使用.call(this) (尤其是如果我们避免多态!)。

看起来就是一切 (Looks are everything)

And to visualize the simplicity this approach brings us, here's the diagram when we remove the functions entirely and focus only on the objects:

为了直观显示此方法带来的简单性,以下是当我们完全删除功能并仅关注对象时的示意图:

I don't know about you, but I just think that mental model is so much cleaner, and the bonus is that its semantics perfectly match the code.

我不了解您,但我只是认为心理模型要干净得多 ,而且好处是它的语义与代码完全匹配。

I have shown you simple enough code using only core JS syntax, that I don't need any helper libraries to wire up my objects. Of course, I could use one, but why? Simpler is better. KISS.

我仅使用核心JS语法向您展示了足够简单的代码,因此不需要任何帮助程序库即可连接对象。 当然可以用一个,但是为什么呢? 越简单越好。 吻。

Any fool can make something complicated. It takes a genius to make it simple. --Woody Guthrie

任何傻瓜都会使事情变得复杂。 使它变得简单需要天才。 -伍迪·格思里

For the record, I'm not even remotely the genius here. Brendan Eich, creator of our language, was the genius for making something so powerful yet so simple.

作为记录,我在这里甚至都不天才。 我们语言的创造者Brendan Eich是创造如此强大但如此简单的东西的天才。

物体自反射 (Object self-reflection)

Last thing to address: how does this simplification affect the process of reflecting on an object? In other words, can we inspect an object and find out about its relationships to other objects?

最后要解决的问题:这种简化如何影响物体反射的过程? 换句话说,我们可以检查一个对象并找出其与其他对象的关系吗?

For prototype-style code, reflection looks like this:

对于原型样式的代码,反射如下所示:

b1 instanceof Bar; // true
b2 instanceof Bar; // true
b1 instanceof Foo; // true
b2 instanceof Foo; // true
Bar.prototype instanceof Foo; // true
Object.getPrototypeOf(b1) === Bar.prototype; // true
Object.getPrototypeOf(b2) === Bar.prototype; // true
Object.getPrototypeOf(Bar.prototype) === Foo.prototype; // true

Notice that you're using instanceof and having to think in terms of the constructor functions that made your objects, and their .prototypes, rather than just reflecting on the objects themselves. Each of those reflections comes with slightly more mental tax as a result.

请注意,您使用的是instanceof ,必须考虑构成对象及其.prototype的构造函数,而不仅仅是考虑对象本身。 这些反映的每一个都会带来更多的精神负担。

And when there's only objects?

当只有物体时?

Bar.isPrototypeOf(b1); // true
Bar.isPrototypeOf(b2); // true
Foo.isPrototypeOf(b1); // true
Foo.isPrototypeOf(b2); // true
Foo.isPrototypeOf(Bar); // true
Object.getPrototypeOf(b1) === Bar; // true
Object.getPrototypeOf(b2) === Bar; // true
Object.getPrototypeOf(Bar) === Foo; // true

By contrast, reflection on objects is only about the objects. There's no awkward references to a constructor's.prototype property for the checks. You can just inspect if one object is related via [[Prototype]] to another object. Same capabilities as above, but with less mental tax.

相反,对对象的反射仅与对象有关。 没有笨拙的引用指向构造函数的.prototype属性进行检查。 您可以只检查一个对象是否通过[[Prototype]]与另一对象相关。 与上述功能相同,但精神税较少。

Moreover, as I mentioned in part 2, this sort of explicit object reflection is preferable and more robust/reliable than implicit detection through duck typing.

而且,正如我在第2部分中提到的那样,这种类型的显式对象反射比通过鸭子类型的隐式检测更可取,并且更健壮/可靠。

Object.wrapItUpAlready() (Object.wrapItUpAlready())

Take a deep breath! That was a lot to take in. If you've followed all 3 parts of the article series, I hope by now you see the bottom line: JS has objects and when we link them, we get powerful behavior delegation.

深吸一口气! 如果要遵循本系列文章的所有3部分,那么我希望现在您能看到底线:JS有对象,当我们链接它们时,我们将获得强大的行为委托。

There's just no need to pile on class-orientation on top of such a great system, because it ultimately just leads to the confusion and distraction that has kept JS' object mechanism shrouded and covered up by all these helper libraries and misunderstandings about JS syntax.

没必要在这样一个很棒的系统上增加面向类的知识,因为它最终只会导致混乱和分散注意力,从而使JS的对象机制笼罩着所有这些帮助程序库,并掩盖了对JS语法的误解。

If you stop thinking about inheritance, and instead think with the arrows headed the other way: delegation, your JS code will be simpler. Remember: it's just objects linked to objects!

如果您不再考虑继承,而是以相反的方向思考:委托,您的JS代码将更加简单。 请记住:这只是链接到对象的对象!

翻译自: https://davidwalsh.name/javascript-objects-deconstruction

js 对象解构

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值