我正在研究LIPS project(JavaScript中基于方案的Lisp),我想添加一种向任何对象添加字符串表示形式的方法。代码如下:
注意:您可以忽略方案代码。最后有一个简化的用例,这仅用于上下文以及为什么我需要它。
(add-repr! HTMLElement (lambda (x)
(let ((tag (--> x.tagName (toLowerCase))))
(string-append "<" tag ">" x.innerHTML "</" tag ">"))))
我评估时效果很好:
(document.createElement "span")
;; or
(document.querySelector ".klas")
但是在评估代码时有问题:
(let ((div (document.createElement "div")))
(. div 'constructor 'prototype))
而且我的解释器认为这是HTMLElement的实例,JavaScript代码如下所示:
var repr = new Map();
// values in map are lisp lambdas that are JavaScript functions
// keys are constructor functions
...
var fn;
if (repr.has(constructor)) {
fn = repr.get(constructor);
} else {
repr.forEach(function(value, key) {
if (obj instanceof key) {
fn = value;
}
});
}
if (fn) {
if (typeof fn === 'function') {
return fn(obj, quote);
} else {
throw new Error('toString: Invalid repr value');
}
}
我检查obj是否为给定类型的实例(add-repr!
中的HTMLElement),并且对于原型返回true。
并抛出未定义x.tagName的异常,因为它不是实例,而是原型。
为了简化代码(我为上下文添加了方案代码),这是代码:
document.createElement('div').constructor.prototype instanceof HTMLElement;
因为原型为Object.create(HTMLElement)
,所以返回true。有没有一种方法可以检测该值是否实际上是任何值的原型而没有原始值。
var x = document.createElement('div').constructor.prototype;
// context is lost I can't access original value
// check if x is instanceof HTMLElement, but also it's constructed value and not prototype of any value.
并且如果您认为可以检查是否存在构造函数值,则此圆形对象:
document.createElement('div').constructor.prototype.constructor.prototype.constructor
总结一下这个问题,我想检测值是否是其中之一,但不能同时是两者:
document.createElement('div')
document.createElement('div').constructor.prototype
我在写这篇文章时才想到的想法是:
var x = document.createElement('div').constructor.prototype;
if (x instanceof HTMLElement && x !== x.constructor.prototype) {
// real instance
}
这是正确的方法吗?我也在看Object.getPrototypeOf
,但它只是返回HTMLElement
对象(我正在测试的对象)。对于任何嵌套的原型链,我都需要使用它,因为它是编程构造,并且用户可以使用任何东西。
为了检测某物是否是原型对象,无论HTMLElement
,我建议这样做
hasOwnProperty(x, "constructor") &&
typeof x.constructor == "function" &&
x.constructor.prototype == x
用户尝试评估的表达式的上下文无关紧要,他们不妨尝试直接打印(. HTMLElement 'prototype)
。
[另外,我建议不要通过Map
将实例的“表示”功能与其构造函数绑定。您的add-repr!
应该只在类的原型上创建一个.repr()
方法,并使用Symbol("lips-repr()")
作为属性键。