我习惯使用静态类型语言,因此可能会看到Javascript中实际不存在的问题。无论如何,这里是我熟悉的用户特定对象的类型检查方法:
instanceof
只要您使用构造函数,就可以正常工作。由于我更喜欢factory functions和Object.create
,instanceof
是不可能的。
isPrototypeOf
与构造函数和工厂函数一起使用,但一旦序列化发挥作用(JSON或结构化克隆算法)就会中断。
适用于工厂函数和序列化,但可能导致细微错误,并且存在与外部库/框架不兼容的风险。
Promise / A +规范的可用对象(then-method)就是一个很好的例子。我觉得这很有害。
transducer.js(以及越来越多的其他)使用像@@transducer/step
这样的伪符号来防止命名冲突,从而为其对象提供类型概念。它只是鸭子打字的变种,但符合我的要求。
toString
我倾向于使用toString方法进行类型检查,因为它已经通过Object.prototype.toString
用于本机类型检查。通常toString
返回对象的字符串表示形式。我通过让toString返回一个类型标识符来扩展这种行为,如果使用参数调用该方法。以下示例高度简化:
var proto = {}, o;
proto.toString: function (_) { return _ === undefined ? JSON.stringify(this) : "someType"};
o = Object.create(proto);
o.a = 123, o.b = "abc", o.c = true;
o.toString(); // {"a":123,"b":"abc","c":true}
o.toString(true); // someType
由于toString主要用于记录,因此应该可以解决。但是,序列化期间toString
会丢失。
还有其他方法吗?
是否有可以推荐的最佳做法?
更新:我需要专门针对ad hoc多态(也就是函数/方法重载)以及与Web worker一起工作的上下文进行类型检查。 Raganwald在他的博客文章中使用结构和语义类型这两个术语,我认为这个术语非常适合这个主题。
答案 0 :(得分:0)
我可以说它是否是“最佳”练习,但如果您有权访问es6,则可以使用Symbol属性指定对象类型。
ebp = esp
符号是唯一的非字符串基元(尽管您可以指定字符串描述)。您不能通过迭代意外获取[Type]属性,并且可以使用
执行简单检查const Type = Symbol('Type');
const ExampleFactory = function(a, b, c) {
return {
a,
b,
c,
[Type]: 'ExampleFactory'
};
};
答案 1 :(得分:0)
Javascript是一种弱+"123" === 123 // true
动态类型var obj = {}, obj = 123; // 123
语言,带有原型系统。
问题是这种语言在运行时通过实例化它们的构造函数来区分对象是多么有用。实际上构造函数只是Javascript中的普通函数,无论你做什么,你总是比较原型的身份:
function Ctor() {}
var o = new Ctor();
var p = Object.create(o);
o instanceof Ctor === Ctor.prototype.isPrototypeOf(o) === true;
o.isPrototypeOf(p) === true;
那么为什么要使用构造函数呢?原型也只是普通的对象,因此工厂函数是所描述的继承和对象模型的逻辑结果。
对象区别应基于与所用语言的性质相对应的技术。正如我所说,Javascript是动态类型的,因此非常适合鸭子打字:
var o = {empty: function () { return this.keys().length === 0; }}
if (empty in o) {
"empty interface implemented";
}
由于对象中的键仅仅是基于字符串的,因此与duck typing相关的名称冲突是不可避免的。幸运的是,自ES2015以来,符号已经在Javascript中找到了自己的方式。
符号具有标识,因此可以共享而不会相互冲突:
Symbol("foo") === Symbol("foo"); // false
尽管它们具有标识,但符号属于Javascript中的原始类型,不像字符串一样可以用作对象键。用符号打字的鸭子从简单的哈希表查找演变为身份的比较。因此,符号可以可靠地声明某个对象是否已实现某个接口:
var o = {};
var empty = Symbol("empty");
o[empty] = function () { return Object.keys(this).length === 0; }
每当一个对象具有empty
属性时我们可以肯定,它已经实现了正确的接口。更好的是,我们可以混合使用各种其他接口(使用符号作为键),查找每个接口的存在(通过它们的身份),所有这些都没有名称冲突或增长原型链。
通过ad hoc多态(也就是函数重载)这样的事情可以轻松实现。
序列化的最后一句话:序列化意味着几乎失去一切:对象标识,原型链,函数,符号等。
因此,每当您使用不共享内存的Web工作者或消息传递系统时,您必须准备复杂对象以进行序列化并在之后恢复它们,例如使用folktale。