I'm working on my LIPS project (Scheme-based Lisp in JavaScript) and I want to add a way to add string representation to any object. The code look like this:
NOTE: you can ignore scheme code. At the end there is simplified use case, this is only for context and why I need this.
(add-repr! HTMLElement (lambda (x)
(let ((tag (--> x.tagName (toLowerCase))))
(string-append "<" tag ">" x.innerHTML "</" tag ">"))))
it works fine when I evaluate:
(document.createElement "span")
;; or
(document.querySelector ".klas")
but it have problem while evaluating the code:
(let ((div (document.createElement "div")))
(. div 'constructor 'prototype))
and my interpreter thinks that this is instance of HTMLElement, the JavaScript code look like this:
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');
}
}
I check if obj is instanceof given type (HTMLElement from add-repr!
) and it return true for prototype.
And throw exception that x.tagName is not defined because it's not instance but prototype.
To simplify the code (I've added scheme code for context) this is the code:
document.createElement('div').constructor.prototype instanceof HTMLElement;
it return true because prototype was Object.create(HTMLElement)
. Is there a way to detect if the value is in fact a prototype of any value without have that original value.
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.
and if you think that you can check if there is constructor value, this circular object:
document.createElement('div').constructor.prototype.constructor.prototype.constructor
to sum this question up I want to detect if value is any of but not both:
document.createElement('div')
document.createElement('div').constructor.prototype
My idea that just came to my mind while I was writing this was this:
var x = document.createElement('div').constructor.prototype;
if (x instanceof HTMLElement && x !== x.constructor.prototype) {
// real instance
}
is this correct approach? I was also looking at Object.getPrototypeOf
but it just return HTMLElement
object (the one I'm testing). I need this to work for any nested prototype chain, because it's programming construct and user may use anything.
For detecting whether something is a prototype object, irrespective of HTMLElement
, I would suggest doing
hasOwnProperty(x, "constructor") &&
typeof x.constructor == "function" &&
x.constructor.prototype == x
The context of the expression the user is trying to evaluate doesn't matter, they might as well try printing (. HTMLElement 'prototype)
directly.
Separately, I would suggest not tying the "representation" function for instances to its constructor through a Map
. Your add-repr!
should just create a .repr()
method on the prototype of the class, using Symbol("lips-repr()")
as the property key.