Search code examples
javascriptnamespacesprototype

Using Prototype with "Namespace" for existing object


I am looking to achieve something along the following.

HTMLSpanElement.prototype.testNS = {
  _this: this,
  testFunc: function() {
    console.log(this.innerHTML)  //undefined as expected as this is the testFunc object
  },
  testFunc2: function() {
    console.log(this._this) //Window object
  }
}

My goal is to add some helper functions directly to a span element in this case.

So, if I had the following:

<span>test</span>

I could find the span and call this code to return "test"

spanElement.testNS.testFunc()

I know that a function retains scope of it's parent when I do it like so...

HTMLSpanElement.prototype.testFunc = function() {
   console.log(this.innerHTML)
}

But I am attempting to organize the code a bit and make it more obvious where the functions are coming from when I add them, and I can't seem to find a way to retain scope, when I do a normal JSON object grab the this scope into _this: this it just returns the global scope of "window".


Solution

  • Disclaimer: You shouldn't be trying to modify the prototypes on built-in types, especially host objects. It's a bad idea.


    The reason your approach isn't working for you is that the functions are being called with the testNS object as the this.

    You can get this to work if you define testNS as a property with a getter function, using Object.defineProperty. The reason this works is that the get function runs in the context of the object on which the property is being accessed (which would be the span):

    Object.defineProperty(HTMLSpanElement.prototype, 'testNS', { 
      get: function() {
        var _this = this;
        return {
          testFunc: function() {
            console.log(_this.innerHTML)
          },
          testFunc2: function() {
            console.log(_this)
          }
        }
      }
    });
    var span = document.getElementById('mySpan');
    
    span.testNS.testFunc();
    span.testNS.testFunc2();
    <span id="mySpan">Wah-hoo!</span>

    A more "vanilla" approach is to just have testNS be a plain function and call it like one. This works because testNS is called in the context of the object on which it is being called (again, the span):

    HTMLSpanElement.prototype.testNS = function() {
      var _this = this;
      return {
        testFunc: function() {
          console.log(_this.innerHTML)
        },
        testFunc2: function() {
          console.log(_this)
        }
      }
    }
    
    var span = document.getElementById('mySpan');
    
    span.testNS().testFunc();
    span.testNS().testFunc2();
    <span id="mySpan">Wah-hoo!</span>