1

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".

1
  • "as this is the testFunc object" this will never refer to the function object itself. It refers to panElement.testNS if you call the function as spanElement.testNS.testFunc(). Commented Nov 1, 2017 at 19:52

3 Answers 3

3

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>

Sign up to request clarification or add additional context in comments.

7 Comments

Hey can we do this using Object.create()
@AnkitPandey I'm afraid I don't understand what you're asking.
Actually I am trying like HTMLSpanElement.prototype.testNS = Object.create() in create I want to put {testFunc: function()...}
I agree, I don't think I should be doing built-in types, it was just easier to use an example of a built-in type instead of pasting a huge amount of code. I will take a look at these examples I knew I could do the dual functions, I was just trying to learn how to keep the scope.
@AnkitPandey I can't think of a way to make that work here.
|
1

When you call a function as foo.bar() then this inside bar refers to foo. Hence if you call the function as spanElement.testNS.testFunc(), this refers to spanElement.testNS.

_this: this, cannot work because this cannot refer to a <span> element.

To get access to spanElement from testFunc you could implement testNS as a getter:

Object.defineProperty(HTMLSpanElement.prototype, 'testNS', {
  get: function() {
    var element = this;
    return {
      testFunc: function() {
        console.log(element.innerHTML);
      },
    };
  },
});

document.querySelector('span').testNS.testFunc();
<span>foo</span>

Comments

0

Because it's a strange requirement I wrote a an equivalent strange solution :-)
Basically the createElement has been overriden in order to add a namespace object literal and then define a new function testFunc on top of the namespace using the instance of the element binded to the function

!function(){
    var defaultNamespace = "testNS";
    var createElement = document.createElement;

    document.createElement = function(tag, namespace) {
        var element = createElement.apply(document, arguments);

        element[namespace || defaultNamespace] = {
            testFunc : function() {
                console.log(this.innerHTML);
            }.bind(element)
        };

        return element; 
    }    
}();

var span = document.createElement("span"); 

1 Comment

It is a bit strange, I was more curious than anything, I am attempting to learn more about scoping in javascript and I was playing around with it a bit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.