views:

215

answers:

3

I have an instance of a logging class "logger", this class has a function "log(txt)", which works.

Now I am declaring a different class "Runner" and I pass it the logger instance in the constructor. Everything works until line 5, but line 7 does not write to the log:

var Runner = function (logger) {
  // constructor:
  logger.log("this way it works");
  this.logger = logger; //line 4
  this.logger.log("this also works"); //line 5
  this.logf = this.logger.log; //create a shorthand for logging
  this.logf("this is not written to log. why? i thought i can create a var for a function"); //line 7
};

var logger = new Logger(); //class not shown here
var runner = new Runner(logger);
var otherinstancce = new OtherClass(logger) //line 12

Can you explain what is my mistake?

+1  A: 

I think the issue might be this.logf is running within the scope of the runner object as opposed to the logger. If your logger.log function uses "this" at all, it will be using the runner instead of the logger.

SB
+7  A: 

When you assign the log function to the logf member of the current object it becomes a member function of this current object. When you call is, the this object inside of the function will refer to it's new object, not to this.logger anymore. Therefore the function will not find variables other functions it wants to call on the logger object.

To avoid this, you can write a simple function that forwards to the logger object:

this.log = function() { return this.logger.log(arguments); }

Or, using a closure:

var obj = this.logger;
this.log = function() { return obj.log(arguments); }
sth
After reading your answer several times I believe I understand. Another question is coming up: could there be any harm in what I do in line 4? Will the logger object (that I pass to instances of other classes also, see line 12) somehow be harmed or changed?
@user89021: The `this.logger` will simpley refer to the original `logger` object and this will not be "harmed" in any way. While methods need to know which object they belong to, objects don't care if they are contained by another object.
sth
+4  A: 

unlike some other languages, "this" in javascript is dynamic, that is, "this" is the context in which the function was called, not where is was defined.

function Foo() {
   this.x = 1;
   this.func = function() { alert(this.x) }
}

foo = new Foo
foo.func() // works

var copy = foo.func
copy() // nope, because func() lost its context

to create coupling between function and its context you have to "lock" the context in a closure. Most frameworks provide a sugar for this in form of bind() method, which, in simplified form, looks like this

Function.prototype.bind = function(o) {
  var p = this;
  return function() { p.apply(o) }
}

with locked context function assignment works as expected

bar = foo.func.bind(foo)
bar() // yes!
stereofrog
`function.bind` is not just a framework feature, it is a standard method of the next version of JavaScript, ECMA262-5. As such you shouldn't hack it into the function prototype without checking it's not already there, and any method you put there should support the full features of the standard `bind` method. This one doesn't handle argument-passing. See eg. the bottom of http://stackoverflow.com/questions/1558065/access-event-object-in-event-handler/1558289#1558289 for a complete implementation.
bobince
I intentionally omitted implementation details of bind() as they are not directly related to OP's question.
stereofrog