Suppose I have this addition to a prototype. I'll use HTMLElement as an example.
HTMLElement.prototype.MyNamespaceGetThis = function() {
return this;
}
document.body.MyNamespaceGetThis()
will return document.body
But if I want to nest it in an object
HTMLElement.prototype.MyNamespace = {};
HTMLElement.prototype.MyNamespace.GetThis = function() {
return this;
}
document.body.MyNameSpace.GetThis()
will return document.body
's MyNameSpace
({GetThis: ƒ}
)
this
or any variable return a reference to the base-object? document.body
in this case?I've tried a few variants, like
HTMLElement.prototype.MyNameSpace = (function() {
let that = this
let obj = Object.defineProperties({}, {GetThis: {
value: function() {
console.log(that)
},
enumerable: false}})
return obj;
})()
but this fails for completely understandable reasons. The function is only run once and returns a reference to window
I've tried a few experiments with .bind()
, but none return the desired result, for predictable reasons.
What I didn't like about Jonas' answer, was that it's basically the same as MyNamespace().method
, where MyNamespace
returns a set of methods.
Persisting members are also impossible. If I wanted to store member data, I needed a separate object somewhere to do that, and I didn't like that.
My solution was to use a class
and then invoke it in a special memory-light way.
class MyNamespace {
constructor(parent) {
this.parent = parent;
}
GetThis() {
return this.parent;
}
}
And then, for this example, you add it to the HTMLElement prototype like this
Object.defineProperties(HTMLElement.prototype, {
MyNamespace: {
enumerable: false, writeable: true,
get: function() {
let ret = new MyNamespace(this);
Object.defineProperty(this, 'MyNamespace', {
enumerable: false,
writeable: false, // note about this
value: ret
});
return ret;
},
enumerable: false, writeable: false
},
})
The first call to say, document.body.MyNamespace.GetThis()
will return a new instance of the class MyNamespace
and then invoke GetThis()
from that. It will also change document.body
's MyNamespace
to directly reference the created instance, rather than create a new one each time. This means persistent data.
Something else I like about this, is that each element isn't carrying around a full MyNamespace
instance, unless it's called upon in the life of the document.
Once the reference is updated, I have it set to freeze it so that it can't be overwritten, but it's easy to imagine where a person might want a destroy
method. You would change writable
to true and something like this.
class MyNamespace {
constructor(parent) {
this.parent = parent;
}
GetThis() {
return this.parent;
}
destroy() {
Object.defineProperties(HTMLElement.prototype, {
MyNamespace: {
enumerable: false, writeable: true,
get: MyNamespace.factory(this, true),
enumerable: false, writeable: false
},
})
}
renew() {
this.parent.MyNamespace = MyNamespace.factory(this.parent, true)
// HTMLElement.prototype.MyNamespace can also be set
// to MyNamespace.factory(this)
}
static factory(parent, writeable) {
return Object.defineProperty(parent, 'MyNamespace', {
enumerable: false, writeable: writeable,
value: new MyNamespace(parent)
}).MyNamespace;
}
}
Turn MyNamespace
into a getter and store a reference to the parent:
function Namespace(parent) {
return {
parent,
/* Your functions here */
};
}
Object.defineProperty(HTMLElement.prototype, "namespace", {
get() { return Namespace(this); },
});
console.log(
document.body.namespace.parent === document.body // true
);