Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple way to access current object

Tags:

javascript

I'm trying to find a simple way to refer to the current instance of an object from the object methods itself (similar to this keyword in any decent language).

I've tried many variations of "storing a pointer for itself" (like window.window.window do), but always something go wrong. For example:

function ClassA() {
  var mySelf = this;

  this.myMethod = function() {
    console.log(mySelf); //Once you extend this class, mySelf will not be available
  }
}

function ClassB() {
  this.mySelf = this; //Ok, now you can extend this and mySelf will be there

  this.myMethod = function() {
    console.log(mySelf);//"mySelf is not defined" because it's a property now
    console.log(this.mySelf);//Value of 'this' and 'self' will vary
    console.log(RandomContainer.ClassBInstance.mySelf);//I can't use a defined path
  }
}

Since everything about OOP in JavaScript is hackish, I have to ask...

Is there any magic to refer to the current object that a method is being called from?


EDIT

A lot of possible solutions in the comments, thanks guys! But I still need to improve my question. So I'll add some piece of code with my failed attempts, and then try out the proposed solutions...

function BaseController()
{
    var myPriv = 42;
    return {
        getAnswer: function() {
            return myPriv;
        }
    };
}

function SomeController()
{
    return {
        showAnswer: function()
        {
            var answer;
            answer = myPriv; //I need some way to access this
            answer = getAnswer(); //Also a way to refer to my own methods 
            //(I don't even find a way to call 'showAnswer' from this context)

            console.log('The answer is ' + answer);
        }
    };
}

//That's how I was extending my classes so far...
var someControllerInstance = jQuery.extend(
    new BaseController(),
    new SomeController()
    );

someControllerInstance.getAnswer(); //Works!
someControllerInstance.showAnswer(); //Reference errors...
like image 837
kbtz Avatar asked Mar 07 '26 06:03

kbtz


1 Answers

take time to learn the idiosyncrasies of js, be warned though, it's like marmite.

If you'll allow me to be blunt for a moment, you are approaching JavaScript with the wrong frame of mind. It is not designed for classical inheritance, nor for protected properties or methods, and there is no benefit in bending it that way. To be honest I find towering stacks of inheritance a pain to read and navigate, unless you have a singing-all-dancing IDE that may take a week to load. The closer to flat and open you can achieve — whilst still keeping things flexible — the better you are at coding, and the more other coders that may take over your work will thank you. (obviously that is opinion)

For more information on prototype inheritance read the following informative post:

http://davidwalsh.name/javascript-objects-deconstruction

Below is an example of prototype inheritance, it should be noted that Object.create and isPrototypeOf are relatively new and do not exist for older JavaScript interpreters. Approximate polyfills can be used in most cases however.

Put simply JavaScript becomes much more powerful when you think in terms of objects borrowing methods from wherever they may be found, and not instances rigidly inheriting from slightly less specific instances; or worse, constructors that keep rebuilding the same functions again and again.

The following is just an example, and across my 16 years of coding ECMAScript I have barely ever needed anything that approaches classical inheritance, nor have I needed objects that heavily inherit on the prototype chain either. More often than not my js is basically just a number of newly created objects, properly name-spaced, that borrow methods from fixed pools of functions; any type detections are duck-typed, and I'm careful to keep everything as local as possible.

Anyway, here's something I don't often use:

var Make = function(construct){
  return Object.create(construct);
};

var Extend = function(proto, props){
  var instance = Object.create(proto);
  for ( var i in props ) { instance[i] = props[i]; }
  instance.super = proto; // this should never be needed really
  return instance;
};

var Animal = {
  keepBreathing: function(){
    console.log('animal', this);
  }
};

var Monkey = Extend(Animal, {
  climbTree: function(){
    console.log('monkey', this);
    console.log('monkey', this.super);
  }
});

var KeyserSoze = Make(Monkey);
    KeyserSoze.keepBreathing(); /// animal, instance of KeyserSoze
    KeyserSoze.climbTree();     /// monkey, instance of KeyserSoze

console.log('an instance of monkey', KeyserSoze);
console.log('has animal as proto', Animal.isPrototypeOf(KeyserSoze)); // true
console.log('has monkey as proto', Monkey.isPrototypeOf(KeyserSoze)); // true

Whilst the above does follow a kind of classical layout i.e. Monkey inherits methods from Animal, you could approach things in a different way. The following is more open to dynamic changes, in the fact you could switch out the behaviours object for another interface entirely. Again, this is just an illustration that you don't have to construct things in a fixed way.

Something I'm more likely to use:

var AnimalBehaviours = {
  keepBreathing: function(){
    (this.breathCount === undefined) && (this.breathCount = 0);
    this.breathCount++;
  }
};

var ApeLikeDescendant = (function( behaviours ){
  return {
    create: function( config ){
      return Object.create(this).prep(config);
    },
    prep: function( config ){
      /// do your instance specific set up here
      return this;
    },
    climbTree: function(){
      console.log('ape-like', this);
    },
    keepBreathing: function(){
      return behaviours.keepBreathing.apply(this, arguments);
    },
    switchBehaviours: function(newBehaviours){
      behaviours = newBehaviours;
    }
  };
})(AnimalBehaviours);

var DouglasAdams = ApeLikeDescendant.create({});

You could adapt the above to behave more in a manner similar to mixins i.e. you'd take the behaviours, step through them and merge them to the ApeLike object... it's really quite open to whatever your goal is.

Something that I use regularly:

var ElementEdgeCases = {
  makeWorkOnNetscape47: function(){
    this.target; /// Intentionally left almost blank.
  }
};

var ElementFinagler = (function(){
  var finagle = {
    target: null,
    prep: function( element ){
      this.target = element;
      return this;
    },
    addClass: function(){
      var c = this.target.getAttribute('className'); /// and so on ...
      return this;
    },
    elaborate: function( mixin ){
      for ( var i in mixin ) {
        if ( mixin.hasOwnProperty(i) ) {
          this[i] = mixin[i];
        }
      }
      return this;
    }
  };
  return function( element ){
    return Object.create(finagle).prep(element);
  };
})();

var elm = document.getElementsByTagName('body')[0];

ElementFinagler(elm)
  .elaborate(ElementEdgeCases) // extend the object if we need
  .addClass('hello world')
;

The main thing to keep in mind with JavaScript is that no function is owned by anything, not really. Every time you execute a function, the function's context is implied by the way you call that function — the context is computed at call time. This allows a great deal of flexibility, and whilst you mention that calling function.apply(context, args) or function.call(context, arg, arg, ...) every time is cumbersome; it is very easy to codify your own system for hiding that repetition away.

Oh, and before I forget, one other thing to take away from the code above is that there is no duplication of function creation. Every instance shares the same functions in memory. This is an important thing to bear in mind if you are planning to create large scale applications, as you can quickly eat up memory with multiple instances of functions.

So just to recap:

  1. Forget inheritance, it's rarely required for many js projects.
  2. Create flexible objects that can be extended when required.
  3. Borrow methods when you need them from other objects, using apply or call to change context.
  4. Keep your code open, there is no need for private; open is more extensible.
  5. Private no, but having properties not enumerable is a different story, see defineProperty.
  6. Make sure you do not duplicate functions — unless you have to — instead create them once and reference.
like image 50
Pebbl Avatar answered Mar 08 '26 18:03

Pebbl