ulak blade
ulak blade

Reputation: 2665

How do I change the "this" pointer in JavaScript?

I made an object like this:

var MyObj = function(arg)
{
  var PublicMethods = { 
          SomeMethod: function(someArg) 
          { 
            if(SomeCheck(arg)
            {
              PublicMethods.SomeFunc2 = somethingElse;
            }
          }
        };

  return PublicMethods;
};

However MyObj doesn't seem to be persistent and between calls, PublicMethods doesnt preserve the new methods added to it, so I tried moving it to global scope, however then it doesn't recognize the 'args' passed from MyObj anymore.

If I make MyObj like this:

var MyObj = (function()
{
//..
}());

Then it becomes a persistent object, but I'm not sure - can I call it like a function again? jQuery seems to have a persistent object and at the same time it can be called like a function, how do they achieve that?

I want to be able to do this:

MyObj("Something Here").SomeMethod("Something Else");

and thus to be able to create a SomeFunc2 method that I can later call too:

MyObj("Something Here").SomeFunc2("Something Else");

Upvotes: 2

Views: 366

Answers (2)

guest271314
guest271314

Reputation: 1

You can create a jQuery method which extends jQuery or jQuery.fn and can also set this context within the method.

(function($) {

  jQuery.addMethod = function addMethod({methodName, method, type}) {
  
    let bool = {
      [type]: false
    };
    
    let _jQuery_jQueryFn_ = Object.keys(bool).pop();
    
    if (type === "jQuery") {
      for (let prop in jQuery) {
        if (prop === methodName 
           || prop.toUpperCase() === methodName.toUpperCase()) {
                bool[type] = true;
                break;
        }
      }
    }

    if (type === "fn") {
      for (let prop in jQuery.fn) {
        if (prop === methodName 
            || prop.toUpperCase() === methodName.toUpperCase()) {
                 bool[type] = true;
                 break;
        }
      }
    }

    if (type === "jQuery" && bool[_jQuery_jQueryFn_] === false) {
      jQuery[methodName] = method;

    }

    if (type === "fn" && bool[_jQuery_jQueryFn_] === false) {
      jQuery[type][methodName] = method;
      
    }
    
    if (bool[_jQuery_jQueryFn_] === true) {
      return Promise.reject(
        new ReferenceError(
          methodName 
          + " previously defined at " 
          + _jQuery_jQueryFn_
      ));
      
    } else {
        console.log(methodName + " defined at " + _jQuery_jQueryFn_);
    }

    return {methodName:methodName, type}; 
  }
})(jQuery);

$(function() {

  Promise.resolve($.addMethod({
      methodName: "add",
      method: function add(a, b, context) {
        console.log(a + b);
        return (context || this)
      },
      type: "jQuery"
    }))
    .then(function({methodName, type}) {
      if (type === "jQuery" && methodName in window[type]) {
          jQuery[methodName](10, 10)
      } else {
        if (methodName in window["jQuery"][type]) {
          jQuery[type][methodName](10, 10);
        }
      }                   
    })
    .catch(function(err) {
      console.error(err)
    });

});

$(function() {

  Promise.resolve($.addMethod({
      methodName: "add",
      method: function add(a, b, context) {
        console.log(a + b);
        return (context || this)
      },
      type: "fn"
    }))
    .then(function({methodName, type}) {
      if (methodName === "jQuery" && methodName in window[type]) {
        jQuery[methodName](10, 10)
      } else {
        if (methodName in window["jQuery"][type]) {
          jQuery("span")[methodName](10, 10);
        }
      }      
    })
    .catch(function(err) {
      console.error(err)
    });

});

$(function() {
  
  
  Promise.resolve(
      $.addMethod({
        methodName: "reverseText",
        method: function reverseText(_text, context) {
          let text = [...(_text || this.text())].reverse().join("");
          (context || this).text(text);
          return (context || this)
        },
        type: "fn"
      }))
    .then(function({methodName, type}) {
      if (type === "jQuery" && methodName in window[type]) {
        jQuery[methodName]()
      } else {
        if (methodName in window["jQuery"][type]) {
          // set context `this` to `span`
          let span = jQuery("section")[methodName]("321", $("span"))    
          .css("color", "sienna");       
          
          console.log(
            span.is(document.querySelector("span"))
          );
        
          jQuery("section")[methodName]()
          .css("color", "green");
        }
      } 
    })
    .catch(function(err) {
      console.error(err)
    });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js">
</script>
<section>section</section>
<span>span</span>

Upvotes: 0

Shadowfacts
Shadowfacts

Reputation: 1053

Simply store the result of the initial MyObj call in a local variable:

var obj = MyObj("Something Here");
obj.SomeMethod("Something Else");
obj.SomeFunc2("Something else");

The PublicMethods variable is specific to each call of MyObj, so when you call MyObj for the second time, you get a different instance of PublicMethods. By using a variable to store the result of the first MyObj call, you can use the same instance of PublicMethods for both the SomeMethod and SomeFunc2 functions.

As a side note, you may want to look into constructor functions which would allow you to define functions more simply, instead of returning an object. For example:

function Example() {
    this.a = function() {
        return "a";
    };
    this.b = function() {
        this.a = function() {
            return "b";
        }
    }
}

var example = new Example();
example.a(); // => "a"
example.b();
example.a(); // => "b"

Upvotes: 1

Related Questions