[英]What is a difference between an object literal and a class with values in constructor in javascript?
I've been working on end to end test in testcafe and in their documentation I found following solution for Page Model:我一直在 testcafe 中进行端到端测试,在他们的文档中,我找到了以下页面模型的解决方案:
class Page {
constructor () {
this.nameInput = Selector('#developer-name');
}
}
export default new Page();
I've been doing some research and I cannot get my head around why it is not resolved with an object literal:我一直在做一些研究,但我无法理解为什么它没有用对象文字解决:
export const Page = {
nameInput: Selector('#developer-name');
}
What are consequences of using each of them?使用它们的后果是什么?
The difference is significant but fundamentally both are JavaScript objects, albeit with different properties and values.区别很明显,但基本上都是 JavaScript 对象,尽管具有不同的属性和值。 Listing all differences on the level of the language would be a long story and you'd be wise to read and understand on JavaScript prototype-based inheritance , but the most important differences are:
列出语言级别的所有差异将是一个很长的故事,阅读和理解基于JavaScript 原型的继承是明智的,但最重要的差异是:
Page
is a prototype object: whenever you create an object of class Page
with new Page()
, the constructor function is called with this
referring to the object being created, not Page
itself. Page
是一个原型对象:每当您使用new Page()
创建类Page
的对象时,构造函数都会被调用, this
指的是正在创建的对象,而不是Page
本身。 Any property you access on the object is searched along the so-called "prototype chain", including the so-called prototype object.您在对象上访问的任何属性都将沿着所谓的“原型链”进行搜索,包括所谓的原型对象。 This prototype object can be accessed with
Page.prototype
and in fact, all methods you define in the class Page
are also properties of this prototype object.这个原型对象可以通过
Page.prototype
访问,事实上,你在类Page
中定义的所有方法也是这个原型对象的属性。 Unlike own properties designed to refer to unique objects or primitives specific to an object, functions in JavaScript don't have to be bound to an object during object or function creation and can be shared between objects (belonging to the same class, for instance) and are called on the actual instance, not the prototype to which they may belong.与自己的属性旨在引用特定于对象的唯一对象或基元不同,JavaScript 中的函数不必在对象或函数创建期间绑定到对象,并且可以在对象之间共享(例如,属于同一类)并在实际实例上调用,而不是它们可能所属的原型。 In other words,
this.nameInput
in your constructor actually adds a property named nameInput
to the object being created with new Page()
, not the prototype, while the constructor itself ( constructor
) and any non-static methods you might add to Page
will be added as properties of Page.prototype
.换句话说,构造函数中的
this.nameInput
实际上向使用new Page()
创建的对象添加了一个名为nameInput
的属性,而不是原型,而构造函数本身( constructor
)和您可能添加到Page
任何非静态方法都会添加为Page.prototype
属性。 The constructor is accessed as Page.prototype.constructor
, by the way, as you'd naturally expect.顺便说一下,正如您自然期望的那样,构造函数可以作为
Page.prototype.constructor
访问。 Page.prototype.constructor === Page
evaluates to true
, by the way. Page.prototype.constructor === Page
计算结果为true
。
An expression of the form like { nameInput: ... }
creates an object which prototype is Object.prototype
, in practice the most basic form of object with "no prototype" and thus no superclass or any traits beyond what the fundamental object prototype object could provide.像
{ nameInput: ... }
这样的形式的表达式创建一个对象,其原型是Object.prototype
,实际上是最基本的对象形式,“没有原型”,因此没有超类或超出基本对象原型对象的任何特征可以提供。 Any properties any such { ... }
object may seem to have through its prototype chain, including methods, are properties of Object.prototype
.任何这样的
{ ... }
对象在其原型链中似乎具有的任何属性,包括方法,都是Object.prototype
属性。 This is why you can do ({}).toString()
or ({}).hasOwnProperty("foobar")
without actually having toString
or hasOwnProperty
properties in your object -- toString
and hasOwnProperty
are properties of Object.prototype
referring to two distinct methods called toString
and hasOwnProperty
, respectively, and JavaScript creates a special property on your object called __proto__
referring to Object.prototype
.这就是为什么你可以做
({}).toString()
或({}).hasOwnProperty("foobar")
而不必实际toString
或hasOwnProperty
在你的对象属性- toString
和hasOwnProperty
是性能Object.prototype
提及两个分别称为toString
和hasOwnProperty
不同方法,JavaScript在您的对象上创建一个特殊的属性,称为__proto__
引用Object.prototype
。 This is how it knows how to "walk the prototype chain".这就是它知道如何“走原型链”的方式。 The names of functions themselves do not matter like that, by the way -- I may add a property on an object referring to an anonymous function:
var foo = ({}); foo.bar = function() { };
顺便说一下,函数的名称本身并不重要——我可以在引用匿名函数的对象上添加一个属性:
var foo = ({}); foo.bar = function() { };
var foo = ({}); foo.bar = function() { };
and call said unnamed function with foo.bar()
.并使用
foo.bar()
调用所述未命名函数。
One mistake you appear to be making is confusing an object of a class with the class, otherwise you wouldn't compare export default class Page { ... }
to export const Page = { nameInput: Selector(...) }
-- the former creates a class accessible as Page
which is used as the prototype object whenever objects of the class are created, while the latter creates an object accessible as Page
which contains nameInput
referring to result of evaluating expression Selector("#developer-name")
(calling Selector
with the sole argument "#developer-name"
).您似乎犯的一个错误是将类的对象与类混淆,否则您不会将
export default class Page { ... }
与export const Page = { nameInput: Selector(...) }
前者创建一个可作为Page
访问的类,每当创建该类的对象时,该类都用作原型对象,而后者创建一个可作为Page
访问的对象,其中包含nameInput
指的是计算表达式Selector("#developer-name")
(使用唯一参数"#developer-name"
调用Selector
)。 Not the same thing at all, not to mention that former has Page
refer to a class (invariably a prototype in JavaScript), while latter has Page
refer to an object that does not seem to fit the pattern of a class.根本不是一回事,更何况前者有
Page
指的是一个类(在 JavaScript 中总是一个原型),而后者有Page
指的是一个似乎不符合类模式的对象。
The interesting things start when you realize that since a class is an object like any other in JavaScript, any object can be used as a class if you know how prototype-based inheritance works:有趣的事情开始于您意识到,由于类是与 JavaScript 中的任何其他对象一样的对象,如果您知道基于原型的继承是如何工作的,则任何对象都可以用作类:
new (function() { this.nameInput = Selector("#developer-name"); })();
What happens here?这里会发生什么? You create a new object with an unnamed function as the object constructor.
您使用未命名的函数作为对象构造函数创建一个新对象。 The effect is absolutely equivalent to otherwise creating the object with
new Page
with Page
being your original ES6 class (ECMAScript 6 is the language specification that adds class
syntax to JavaScript).效果绝对等同于以其他方式使用
new Page
创建对象,其中Page
是您原来的 ES6 类(ECMAScript 6 是将class
语法添加到 JavaScript 的语言规范)。
You can also do this, again equivalent to if you defined Page
with class Page ...
:您也可以这样做,同样等效于如果您使用
class Page ...
定义Page
:
function Page() {
this.nameInput = Selector("#developer-name");
}
var foo = new Page();
Page.prototype
will be the prototype object for foo
, accessible as foo.__proto__
and otherwise making it possible for you to call instance methods on foo
like foo.bar()
, provided you define bar
property on at least Page.prototype
: Page.prototype
将是foo
的原型对象,可作为foo.__proto__
访问,否则您可以像foo.bar()
一样在foo
上调用实例方法,前提是您至少在Page.prototype
上定义了bar
属性:
function Page() {
this.nameInput = Selector("#developer-name");
}
Page.prototype.bar = function() {
console.log(this.nameInput);
}
var foo = new Page();
foo.bar();
In fact, the above is what browser would do internally if it had to interpret the following code:事实上,如果浏览器必须解释以下代码,上面的内容就是它在内部会做的事情:
class Page {
constructor() {
this.nameInput = Selector("#developer-name");
}
bar() {
console.log(this.nameInput);
}
}
It is beyond the scope of my answer to list differences between the two last approaches (isn't the same thing as the two approaches you proposed), but one difference is that with class Page ...
, Page
is not a property of window
in some user agents while with function Page ...
it is.列出最后两种方法之间的差异超出了我的回答范围(与您提出的两种方法不同),但一个区别在于
class Page ...
, Page
不是window
的属性在某些用户代理中,同时具有function Page ...
它是。 It's partly historical reasons, but rest assured that so far defining constructors and prototype using either approach is pretty much the same, although I can imagine smarter JavaScript runtimes will be able to optimize the latter form better (because it's an atomic declaration, and not just a sequence of expressions and statements).这部分是历史原因,但请放心,到目前为止,使用任一方法定义构造函数和原型几乎相同,尽管我可以想象更智能的 JavaScript 运行时将能够更好地优化后一种形式(因为它是原子声明,而不仅仅是表达式和语句的序列)。
If you understand prototype-based inheritance at the heart of all of this, all your questions about this will fall away by themselves as very few fundamental mechanisms of JavaScript support 99% of its idiosyncrasies.如果您了解所有这一切的核心是基于原型的继承,那么您对此的所有问题都会消失,因为 JavaScript 的基本机制很少能支持其 99% 的特性。 You'll also be able to optimize your object design and access patterns, knowing when to choose ES6 classes, when not to, when using object literals (
{ prop: value, ... }
) and when not to, and how to share fewer objects between properties.你还可以优化你的对象设计和访问模式,知道什么时候选择 ES6 类,什么时候不选择,什么时候使用对象文字(
{ prop: value, ... }
),什么时候不选择,以及如何共享属性之间的对象更少。
Classes can be thought of as a blueprint, they both provide an object in the end.类可以被认为是一个蓝图,它们最终都提供了一个对象。 But as the object literals name implies, you literally create it there and then with this 'literal' syntax.
但正如对象字面量名称所暗示的那样,您实际上是在那里创建它,然后使用这种“字面量”语法。 A class however, we would use to instantiate new instances from 1 base blueprint.
但是,我们将使用一个类从 1 个基本蓝图实例化新实例。
let x = { myProp: undefined }
let y = { myProp: undefined }
x.myProp = "test";
y.myProp // undefined
Here we see we make two separate instances, but we will have to repeat code.在这里,我们看到我们创建了两个单独的实例,但我们将不得不重复代码。
class X { }
let x = new X();
let y = new X();
A class does not need to repeat the code, as it is all encapsulated in the idea of what X should be, a blueprint.一个类不需要重复代码,因为它全部封装在X应该是什么的想法中,一个蓝图。
Similar to above [in the literal] we have two separate instances but it's cleaner, more readable, and any change we wish to make to every instance of this 'X' object can now be changed simply in the class.与上面[在文字中]类似,我们有两个单独的实例,但它更清晰、更具可读性,并且我们希望对这个“X”对象的每个实例进行的任何更改现在都可以在类中简单地更改。
There's a plethora of other benefits and even a paradigm dedicated to Object-Oriented Programming, read here for more: https://www.internalpointers.com/post/object-literals-vs-constructors-javascript还有许多其他好处,甚至是专门用于面向对象编程的范式,请阅读此处了解更多信息: https : //www.internalpointers.com/post/object-literals-vs-constructors-javascript
To go further into the constructor question... In other languages we have fields.进一步探讨构造函数问题...在其他语言中,我们有字段。 I believe when you assign a field in the constructor, it just creates an underthehood like field (I say underthehood like because JavaScript is prototype based, and the class syntax is syntactical sugar to help write prototypes easier for programmers familiar with class syntax in other languages).
我相信当你在构造函数中分配一个字段时,它只会创建一个类似底层的字段(我说底层是因为 JavaScript 是基于原型的,并且类语法是语法糖,以帮助熟悉其他语言的类语法的程序员更容易地编写原型)。
Here is an example in C#.这是 C# 中的示例。
public class X{
private int y;
X() {
this.y = 5;
}
}
It's more a convention to assign fields in the constructor in other languages, so I assume this has something to do with it in JavaScript.在其他语言中在构造函数中分配字段更像是一种约定,所以我认为这与 JavaScript 中的它有关。
Hope this helps.希望这可以帮助。
By declaring it as a Class you can later identify what type of object it is with .constructor.name:通过将其声明为类,您可以稍后使用 .constructor.name 确定它是什么类型的对象:
class Page { constructor () { this.nameInput = "something"; } // No comma anotherMethod() { } } const pageClass = new Page(); const pageLiteral = { nameInput: "something" , // must have a comma anotherMethod() { } } console.log("Name of constructor for class: ", pageClass.constructor.name); // Page console.log("Name of constructor for literal: ", pageLiteral.constructor.name); // Object
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.