JavaScript对象创建模式比较指南

Comparison and principles of the four most common patterns

四种最常见模式的比较和原理

If you started learning programming with a traditional class-based language, JavaScript’s Object Oriented Programming might look confusing at first. You will find a lot of articles on different object creation patterns which might be a bit overwhelming. In this article, we will analyze 4 common object creation patterns. I will start with a brief review of some of the underlying concepts/features. Then, I will examine some code samples and discuss how each pattern works using some visual aid.

如果您开始使用传统的基于类的语言学习编程,那么JavaScript的面向对象编程一开始可能会令人困惑。 您会发现很多关于不同对象创建模式的文章,这些文章可能有点让人难以理解。 在本文中,我们将分析4种常见的对象创建模式。 我将首先简要回顾一些基本概念/功能。 然后,我将检查一些代码示例,并讨论如何使用视觉辅助来实现每个模式。

If you are a Launch School student and studying OOP JavaScript as part of JS225 or JS120, and preparing towards the assessment, this article would be a good read for you. The aim of this article is to build up how each object creation pattern implements prototypal inheritance in steps and solidify your mental model. Let’s start with some background.

如果您是入门学校的学生,并且正在学习作为JS225或JS120一部分的OOP JavaScript,并准备进行评估,那么这篇文章对您来说是一本好书。 本文的目的是建立每个对象创建模式如何逐步实现原型继承并巩固您的思维模型。 让我们从一些背景开始。

JavaScript的继承 (Inheritance in JavaScript)

The term inheritance in OOP, commonly refers to class-based languages where there is a sub-class/super-class relationship, being sub-classes inherit behavior and data from super-classes.

OOP中的“ 继承 ”一词通常是指存在子类/超类关系的基于类的语言,因为子类从超类继承行为和数据。

JavaScript does not have true-inheritance as we know it from class-based languages. Instead, it uses something called prototypal inheritance. When we create an object, say child from another object called parent , child references it's properties and methods to parent object's prototype. This way it can delegate an access request up in the prototype chain which is why we call this behavior delegation.

正如我们从基于类的语言中了解到的那样,JavaScript没有真正的继承。 相反,它使用一种称为原型继承的东西。 当我们创建一个对象时,从另一个称为parent的对象说childchild将其属性和方法引用到parent对象的prototype 。 这样,它可以在原型链中向上委派一个访问请求,这就是为什么我们称这种行为委派

Image for post
Photo by JJ Ying on Unsplash
图片由 JJ YingUnsplash拍摄

类属性与函数属性 (Class Properties vs. Function Properties)

Image for post
Properties of functions and objects | Key on top-right demonstrates object types
函数和对象的属性| 右上角的键演示对象类型
  • In JavaScript, prototype property only exists in Functions. It is basically an object that Function returns when invoked as a constructor with the new keyword.

    在JavaScript中, prototype属性仅存在于Functions中。 从本质上讲,它是使用new关键字作为构造函数调用时函数返回的对象。

  • [[Prototype]] is a property that all objects contain. This property points to prototype object that the initial object created from.

    [[Prototype]]是所有对象都包含的属性。 此属性指向创建初始对象的原型对象。

Let’s look at the above diagram to see what happens when we create an empty function vs an empty object.

让我们看一下上面的图,看看当我们创建一个空函数与一个空对象时会发生什么。

On the left, we have the Sample function. When we create a function, it initially inherits from Function.prototype which is referenced to it's [[Prototype]] property. Functions also contain a prototype property that contains a prototype object. This object contains a property called constructor that refers back to the function. We will make use of constructor later.

在左侧,我们具有Sample功能。 当我们创建一个函数时,它最初是从Function.prototype继承的,该Function.prototype引用了它的[[Prototype]]属性。 函数还包含一个prototype属性,该属性包含一个原型对象。 该对象包含一个称为constructor的属性,该属性返回该函数。 稍后我们将使用constructor

On the right, we have our object Sample with [[Prototype]] property. The value that [[Prototype]] points to depends on how the object is created. If it is created using the object literal syntax it will reference to Object.prototype . If the object is created by a constructor function, it will be referencing to prototype property of that function.

在右侧,我们的对象Sample具有[[Prototype]]属性。 [[Prototype]]指向的值取决于如何创建对象。 如果使用对象文字语法创建它,则将引用Object.prototype 。 如果对象是由构造函数创建 ,则它将引用该函数的prototype属性。

通过函数创建对象 (Creating Objects From Functions)

Lastly, let’s remember what happens when we invoke a constructor function with new keyword. Let's say we have a construction function ConstFunc and we have a line of code like this: let newObj = new ConstFunc(arg1, arg2);

最后,让我们记住使用new关键字调用构造函数时发生的情况。 假设我们有一个构造函数ConstFunc并且我们有这样的代码行: let newObj = new ConstFunc(arg1, arg2);

  1. When invoked, constructor function ConstFunc creates a new object

    调用时,构造函数ConstFunc创建一个新对象

  2. this (execution context) assigned to this new object as part of the function call.

    this (执行上下文)分配给此新对象,作为函数调用的一部分。

  3. ConstFunc is executed in this context with passed in arguments.

    ConstFunc在此上下文中使用传入的参数执行。

  4. If there is no explicit return statement, the value of this will be returned.

    如果没有显式的return语句,则将返回this值。

The created object will have the below relationship with the constructor function

创建的对象将与构造函数具有以下关系

  • newObj.constructor === [ Function: ConstFunc ]

    newObj.constructor === [ Function: ConstFunc ]

  • ConstFunc.prototype === ConstFunc {}

    ConstFunc.prototype === ConstFunc {}

  • newObj.constructor.prototype === ConstFunc {}

    newObj.constructor.prototype === ConstFunc {}

Now we covered all the basics, the rest will be quite straightforward.

现在我们涵盖了所有基础知识,其余的将非常简单。

构造函数 (Constructor Functions)

Previously, we’ve discussed how constructor functions work while we explained how the new keyword works. The key takeaway from this pattern is Album.prototype. It is the prototype object for constructor function's return value.

之前,我们讨论了构造函数的工作原理,同时还解释了new关键字的工作原理。 该模式的关键之处在于Album.prototype 。 它是构造函数的返回值的原型对象。

Image for post
“Inheritance” mechanism of constructor functions
构造函数的“继承”机制

When we created the new objects using the constructor function Album , all shared methods are copied into instances of Album.prototype . Therefore, although [[Prototype]] property of the new objects point to Album.prototype , they will still use their own copy of the methods.

使用构造函数Album创建新对象时,所有共享方法都将复制到Album.prototype实例中。 因此,尽管新对象的[[Prototype]]属性指向Album.prototype ,但它们仍将使用它们自己的方法副本。

工厂模式 (Factory Pattern)

Factory pattern makes use of a function that returns an object when invoked. Above example makes use of the object literal syntax for object creation, but other ways such as new Object() can be also used.

工厂模式利用一个函数,该函数在调用时返回一个对象。 上面的示例将对象文字语法用于对象创建,但也可以使用其他方式,例如new Object()

Image for post
“Inheritance” mechanism of factory functions
工厂功能的“继承”机制

Factory pattern is computationally inefficient since all the methods will be copied to every single object that is returned. Unlike constructor functions it is not possible to track down how these objects are created, or how they are related to makeAlbum function. Also, it is not possible to update the "shared" behavior once the objects are returned by the factory function.

工厂模式的计算效率低下,因为所有方法都将复制到返回的每个对象中。 与构造函数不同 ,无法跟踪这些对象的创建方式或它们与makeAlbum函数的关系。 同样,一旦工厂函数返回了对象,就不可能更新“共享”行为。

伪古典模式 (Pseudo-Classical Pattern)

Pseudo-classical pattern combines constructor function and prototype pattern. This popular pattern resolves the problem of inefficiencies that have been discussed on the previous two patterns and introduces prototypal inheritance. Pseudo-classical pattern achieves this by creating a distinction between private properties and shared properties/methods. These are separated into a constructor function and the constructor function’s prototype.

伪古典模式结合了构造函数和原型模式。 这种流行的模式解决了前两种模式所讨论的效率低下的问题,并引入了原型继承 。 伪古典模式通过在私有属性共享属性/方法之间建立区别来实现此目的 它们分为构造函数构造函数的prototype

Image for post
Prototypal Inheritance mechanism of Pseudo-Classical Pattern
伪古典模式的原型继承机制

JavaScript class is introduced with ES6. Essentially, this does the same thing as the pseudo-classical model, it is just a syntactic sugar. It improves the organization of the code and provides a constructor method.

ES6引入了JavaScript class 。 本质上,这与伪古典模型具有相同的作用,只是语法上的糖 。 它改善了代码的组织并提供了构造方法。

Lastly, The Pseudo classical pattern can be combined in the constructor function:

最后,可以在构造函数中组合伪经典模式:

The if statement checks if the "to be created" object has already a property called readTag or type . Since inAbsentia is the first object that has been created by Album constructor, this will evaluate true and the shared behavior will be defined on the object's prototype. When we create other objects with the same constructor function, this block of code will be skipped by the if statement.

if语句检查“要创建”对象是否已经具有称为readTagtype的属性。 由于inAbsentiaAlbum构造函数创建的第一个对象,因此它将评估为true ,并且共享行为将在对象的原型上定义。 当我们使用相同的构造函数创建其他对象时, if语句将跳过此代码块。

Image for post
Photo by Steffen Gundermann on Unsplash
Steffen GundermannUnsplash拍摄的照片

OLOO(对象链接到其他对象) (OLOO(Object Linking to Other Object))

OLOO, is an object creation pattern based on creating new objects with Object.create method using prototype objects. It is a relatively simpler pattern since it is not dealing with constructors and prototype properties.

OLOO是一种对象创建模式,它基于使用原型对象的 Object.create 方法 创建新 对象 。 这是一种相对简单的模式,因为它不处理构造函数和原型属性。

With OLOO, object creation and initialization occur at different times. The latter can be done by using an optional init method as shown above.

使用OLOO, 对象创建和初始化发生在不同的时间。 后者可以通过使用可选的init方法完成,如上所示。

Because OLOO Pattern does not use constructors, examining inheritance with methods like Object.prototype.constructor will not work as expected and simply return Object.prototype (the top element in the prototypal inheritance hierarchy). Instead, isPrototypeOf and Object.getPrototypeOf could be used for such purpose. Also, we can not use instanceof because the right-hand operand has to be a function.

因为OLOO Pattern不使用构造函数,所以使用Object.prototype.constructor方法检查继承将无法按预期方式工作,而是仅返回Object.prototype (原型继承层次结构中的顶级元素)。 而是可以将isPrototypeOfObject.getPrototypeOf用于此目的。 同样,我们不能使用instanceof因为右侧操作数必须是一个函数。

Above inAbsentia and unknowAlbum have different properties, but they share the same methods as Album.

以上的inAbsentiaunknowAlbum具有不同的属性,但它们与Album具有相同的方法。

Image for post
Prototypal Inheritance mechanism of OLOO Pattern
OLOO模式的原型继承机制

This approach provides behavior delegation rather than copying of all methods at object creation time. Therefore, when we add a new method check into prototype object and call this method on the already created instance inAbsentia , this method call will be delegated to its prototype.

这种方法提供行为委托,而不是在对象创建时复制所有方法 。 因此,当我们向原型对象中添加新方法check并在已经创建的实例inAbsentia调用此方法时,此方法调用将委派给其原型。

TL;DR

TL; DR

There are a lot of good articles on object creation patterns in JavaScript. In this article, I’ve tried to focus on the underlying principles first.

关于JavaScript中的对象创建模式 ,有很多不错的文章。 在本文中,我首先尝试着重于基本原理。

  • [[Prototype]] is a hidden property contained in all objects. It is referenced to initial object's prototype object.

    [[Prototype]]是所有对象中包含的隐藏属性。 它被引用到初始对象的原型对象。

  • prototype is a functions prototype object that it returns when invoked with new keyword.

    prototype是一个函数原型对象,当使用new关键字调用时会返回该对象。

  • When an object is created invoking a constructor function the returned object has a constructor property that points to constructor function.

    创建调用构造函数的对象时,返回的对象具有指向构造函数的constructor属性。

At the second part I’ve discussed and compared 4 common object creation patterns with code samples and diagrams. Some key takeaways:

在第二部分中,我讨论了4种常见的对象创建模式,并将它们与代码示例和图表进行了比较。 一些要点:

  • Factory Pattern gathers object creation functionality in a single function, prevents repetition (more on DRY. Each function invocation creates a new object. It is inefficient, hard to trace the sources of the objects. Objects have a copy of their own methods.

    Factory Pattern在单个函数中收集对象创建功能,防止重复( 有关DRY的更多信息 。每个函数调用都会创建一个新对象。效率低下,难以跟踪对象的来源。对象具有自己方法的副本。

  • Constructor Function, takes one step further from factory pattern by introducing a fake prototypal inheritance. Created object will still have their own copy of methods, but their source can be traced by using constructor property on the instances.

    构造函数,通过引入伪造的原型继承,比工厂模式更进一步。 创建的对象仍将具有其自己的方法副本 ,但可以通过在实例上使用constructor属性来跟踪其源。

  • Pseudo-Classical Pattern is a combination of constructor function and prototype pattern. It resolves the above inefficiencies by introducing prototypal inheritance. It achieves this by separating private properties and shared properties into constructor function and it’s prototype.

    伪经典模式是构造函数和原型模式的组合。 它通过引入原型继承来解决上述低效率问题 它通过将私有属性和共享属性分成构造函数和它的原型来实现。

  • OLOO(Object Linking to Other Object), is a simpler solution that uses a prototype object instead of a constructor function. It relies of creating new object using Object.create with prototype object passed in as argument.

    OLOO(对象链接到其他对象)是一种使用原型对象而不是构造函数的更简单解决方案。 它依赖于使用Object.create创建新对象,并将原型对象作为参数传入。

We now know all these patterns but which is “the best”? My answer would be It depends… The latter two are better practices when it comes to building your code organization. However, the former two are also important to understand the principals behind prototypal inheritance and can save you some time when dealing with smaller problems.

现在我们知道所有这些模式,但是哪个是“ 最好的” ? 我的答案是……这取决于……在构建代码组织时,后两种是更好的做法。 但是,前两个对理解原型继承原理也很重要,在处理较小的问题时可以节省一些时间。

翻译自: https://medium.com/launch-school/a-comparative-guide-to-javascript-object-creation-patterns-31c9c3cfede1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值