[英]What is a difference between an object literal and a class with values in constructor in javascript?
我一直在 testcafe 中进行端到端测试,在他们的文档中,我找到了以下页面模型的解决方案:
class Page {
constructor () {
this.nameInput = Selector('#developer-name');
}
}
export default new Page();
我一直在做一些研究,但我无法理解为什么它没有用对象文字解决:
export const Page = {
nameInput: Selector('#developer-name');
}
使用它们的后果是什么?
区别很明显,但基本上都是 JavaScript 对象,尽管具有不同的属性和值。 列出语言级别的所有差异将是一个很长的故事,阅读和理解基于JavaScript 原型的继承是明智的,但最重要的差异是:
Page
是一个原型对象:每当您使用new Page()
创建类Page
的对象时,构造函数都会被调用, this
指的是正在创建的对象,而不是Page
本身。 您在对象上访问的任何属性都将沿着所谓的“原型链”进行搜索,包括所谓的原型对象。 这个原型对象可以通过Page.prototype
访问,事实上,你在类Page
中定义的所有方法也是这个原型对象的属性。 与自己的属性旨在引用特定于对象的唯一对象或基元不同,JavaScript 中的函数不必在对象或函数创建期间绑定到对象,并且可以在对象之间共享(例如,属于同一类)并在实际实例上调用,而不是它们可能所属的原型。 换句话说,构造函数中的this.nameInput
实际上向使用new Page()
创建的对象添加了一个名为nameInput
的属性,而不是原型,而构造函数本身( constructor
)和您可能添加到Page
任何非静态方法都会添加为Page.prototype
属性。 顺便说一下,正如您自然期望的那样,构造函数可以作为Page.prototype.constructor
访问。 Page.prototype.constructor === Page
计算结果为true
。
像{ nameInput: ... }
这样的形式的表达式创建一个对象,其原型是Object.prototype
,实际上是最基本的对象形式,“没有原型”,因此没有超类或超出基本对象原型对象的任何特征可以提供。 任何这样的{ ... }
对象在其原型链中似乎具有的任何属性,包括方法,都是Object.prototype
属性。 这就是为什么你可以做({}).toString()
或({}).hasOwnProperty("foobar")
而不必实际toString
或hasOwnProperty
在你的对象属性- toString
和hasOwnProperty
是性能Object.prototype
提及两个分别称为toString
和hasOwnProperty
不同方法,JavaScript在您的对象上创建一个特殊的属性,称为__proto__
引用Object.prototype
。 这就是它知道如何“走原型链”的方式。 顺便说一下,函数的名称本身并不重要——我可以在引用匿名函数的对象上添加一个属性: var foo = ({}); foo.bar = function() { };
var foo = ({}); foo.bar = function() { };
并使用foo.bar()
调用所述未命名函数。
您似乎犯的一个错误是将类的对象与类混淆,否则您不会将export default class Page { ... }
与export const Page = { nameInput: Selector(...) }
前者创建一个可作为Page
访问的类,每当创建该类的对象时,该类都用作原型对象,而后者创建一个可作为Page
访问的对象,其中包含nameInput
指的是计算表达式Selector("#developer-name")
(使用唯一参数"#developer-name"
调用Selector
)。 根本不是一回事,更何况前者有Page
指的是一个类(在 JavaScript 中总是一个原型),而后者有Page
指的是一个似乎不符合类模式的对象。
有趣的事情开始于您意识到,由于类是与 JavaScript 中的任何其他对象一样的对象,如果您知道基于原型的继承是如何工作的,则任何对象都可以用作类:
new (function() { this.nameInput = Selector("#developer-name"); })();
这里会发生什么? 您使用未命名的函数作为对象构造函数创建一个新对象。 效果绝对等同于以其他方式使用new Page
创建对象,其中Page
是您原来的 ES6 类(ECMAScript 6 是将class
语法添加到 JavaScript 的语言规范)。
您也可以这样做,同样等效于如果您使用class Page ...
定义Page
:
function Page() {
this.nameInput = Selector("#developer-name");
}
var foo = new Page();
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();
事实上,如果浏览器必须解释以下代码,上面的内容就是它在内部会做的事情:
class Page {
constructor() {
this.nameInput = Selector("#developer-name");
}
bar() {
console.log(this.nameInput);
}
}
列出最后两种方法之间的差异超出了我的回答范围(与您提出的两种方法不同),但一个区别在于class Page ...
, Page
不是window
的属性在某些用户代理中,同时具有function Page ...
它是。 这部分是历史原因,但请放心,到目前为止,使用任一方法定义构造函数和原型几乎相同,尽管我可以想象更智能的 JavaScript 运行时将能够更好地优化后一种形式(因为它是原子声明,而不仅仅是表达式和语句的序列)。
如果您了解所有这一切的核心是基于原型的继承,那么您对此的所有问题都会消失,因为 JavaScript 的基本机制很少能支持其 99% 的特性。 你还可以优化你的对象设计和访问模式,知道什么时候选择 ES6 类,什么时候不选择,什么时候使用对象文字( { prop: value, ... }
),什么时候不选择,以及如何共享属性之间的对象更少。
类可以被认为是一个蓝图,它们最终都提供了一个对象。 但正如对象字面量名称所暗示的那样,您实际上是在那里创建它,然后使用这种“字面量”语法。 但是,我们将使用一个类从 1 个基本蓝图实例化新实例。
let x = { myProp: undefined }
let y = { myProp: undefined }
x.myProp = "test";
y.myProp // undefined
在这里,我们看到我们创建了两个单独的实例,但我们将不得不重复代码。
class X { }
let x = new X();
let y = new X();
一个类不需要重复代码,因为它全部封装在X应该是什么的想法中,一个蓝图。
与上面[在文字中]类似,我们有两个单独的实例,但它更清晰、更具可读性,并且我们希望对这个“X”对象的每个实例进行的任何更改现在都可以在类中简单地更改。
还有许多其他好处,甚至是专门用于面向对象编程的范式,请阅读此处了解更多信息: https : //www.internalpointers.com/post/object-literals-vs-constructors-javascript
进一步探讨构造函数问题...在其他语言中,我们有字段。 我相信当你在构造函数中分配一个字段时,它只会创建一个类似底层的字段(我说底层是因为 JavaScript 是基于原型的,并且类语法是语法糖,以帮助熟悉其他语言的类语法的程序员更容易地编写原型)。
这是 C# 中的示例。
public class X{
private int y;
X() {
this.y = 5;
}
}
在其他语言中在构造函数中分配字段更像是一种约定,所以我认为这与 JavaScript 中的它有关。
希望这可以帮助。
通过将其声明为类,您可以稍后使用 .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.