简体   繁体   English

javascript中的构造函数中的对象字面量和具有值的类有什么区别?

[英]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")而不必实际toStringhasOwnProperty在你的对象属性- toStringhasOwnProperty是性能Object.prototype提及两个分别称为toStringhasOwnProperty不同方法,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.

相关问题 javascript中构造函数和class的区别 - difference between constructor and class in javascript JSON 和 Object Literal Notation 有什么区别? - What is the difference between JSON and Object Literal Notation? JavaScript:通过构造函数和对象常量创建对象之间的区别是什么? - JavaScript: What is the diff between creating an object via constructor and via object literal? Object构造函数和Global Object之间有什么区别 - What is the difference between Object constructor and Global Object class X 和 class X 扩展 Object 之间的区别是什么? - What is the difference between class X and class X extends Object in JavaScript? object 和 object 在 Z686155AF75A60A0F6E9D80C1F7EDD3 中有什么区别? - What is the difference between an object and an object from a class in JavaScript? JavaScript在构造函数和作为构造函数调用的函数返回对象之间有什么区别? - What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor? Javascript-这些构造函数之间有什么区别? - Javascript - What is the difference between these constructor functions? `new Object()` 和对象文字符号有什么区别? - What is the difference between `new Object()` and object literal notation? Javascript中类和对象之间的区别 - Difference between a class and an object in Javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM