1

Update: I can't answer the question, since it's locked, but I have my own solution to this problem at the bottom, working with Jonas' answer

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: ƒ})

Is there a way to make 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.


My Solution

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;
  }
}
1

1 Answer 1

1

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
);
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.