When I call one function, "this" contains all the the "stuff", but when I call a function that calls another functions, "this" returns undefined inside the second function.
Code:
class test {
test() {
console.log(this) // => returns class functions ect..
this.run(this.test2);
}
run(func){
func()
}
test2() {
console.log(this) // => returns undefined
this.function3(); // => ERROR
}
function3() {
console.log("hey")
}
}
var t = new test();
t.test();
Why does the code behave this way? and how do I resolve this issue
Ah, those are colled closures. The context (this) will be different, depending on from wich context the function will be called. For javascript a function is an object and it's binding to a class object is week. It can be detached from the class and used separately with on problem. That's why when you call this.someFunc() it gives you only a function, but not the function of this actual instance.
The most common way to bypass it is to save an object context to a separate variable and use in istead of this. Something like that:
class test {
var self = this;
test() {
console.log(self)
this.run(self.test2);
}
run(func){
func();
}
test2() {
console.log(self);
self.function3();
}
function3() {
console.log("hey");
}
}
When the function passed into run is executed, it's this is pointing to a new execution context, not the instance of the class that you are expecting.
This can be 'solved' by binding its execution context to the instance of the class, either when passed to run, e.g.
this.run(this.test2.bind(this))
Or I'm the constructor, e.g.
constructor () {
this.test2 = this.test2.bind(this)
}
Related
Using this keyword inside functions of an object works, but refering to other functions without this doesn't. When I put out those functions before the object, both ways work. Why is that so?
var Obj = {
func1: function () {
console.log(this.func2()); // works
console.log(func2()); // doesn't work
},
func2: function () {
return 5;
}
};
Obj.func1();
But doing the same in IIFE:
var Obj = (function () {
function func1() {
console.log(this.func2()); // works
console.log(func2()); // works
}
function func2() {
return 5;
}
return {
func1: func1,
func2: func2
};
})();
Obj.func1();
This has nothing to do with the IIFE, in the first example you have an object literal, in the second regular functions.
Of course, object literals would need the name of the object (or this if the this-value is the object, like inside an object literal) followed by the property to access it, while named functions can be accessed anywhere in that scope by just using the name.
var obj = {
prop : function() {...}
}
prop(); // fail ... undefined
obj.prop(); // works
// then _______________________________________________________________
function prop() {...}
prop(); // works fine
This is really what you're doing, you've just wrapped it up in different objects and immediately invoked function expressions, so it looks somewhat the same, but it's not.
There is no difference.
In both cases, you're working with an object. So, you need to use this or the object name to call the function defined under an object.
[This is related to Bound function instead of closure to inject extra arguments, but that was neither clearly asked nor answered.]
I'm calling a function that expects a function as its argument. I want to pass a method from my class, bound to an instance of my class. To make it clear, assume my class looks like:
var MyClass = function() {}
MyClass.prototype.myMethod = function() { ... }
var my_instance = new MyClass();
Is there any substantive difference between using bind:
doSomething(my_instance.myMethod.bind(my_instance))
and wrapping the call in an anonymous function:
doSomething(function() { my_instance.myMethod(); })
?
If a prototype within your class needs to generate a callback, it may not know its instance's name. As a result, you'll need to use this, but the value of this depends on where the callback was executed.
Consider the following example:
var MyClass = function (x) { this.something = x; };
MyClass.prototype.makeCall = function () {
var myBadCallback = function() { console.log(this.something); };
var myGoodCallback = function() { console.log(this.something); }.bind(this);
// When called, the value of "this" points to... we don't know
callMeBack( myBadCallback );
// When called, the value of "this" points to this instance
callMeBack( myGoodCallback );
};
function callMeBack( callback ) { callback(); };
var foo = new MyClass('Hello World!');
var bar = new MyClass('Goodbye!');
// Probably prints "undefined", then prints "Hello World!"
foo.makeCall();
// Probably prints "undefined", then prints "Goodbye!"
bar.makeCall();
In the above example, the first output probably prints undefined because the context (what this refers to) has changed by the time the callback has executed.
This example may seem contrived, but these sort of situations do arise, a common case being AJAX callbacks.
I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo().
How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
The reason your original code didn't work is that the value of this inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this being a reference to bar because bar is to the left of the . on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this still references bar.
In fact, if you call the plain function callback(); as defined above, you'll find that this is a reference to the global object, usually window in web browsers. That's because all global variables and functions are properties of window, so callback(); is implicitly window.callback();
The fact that the value of this depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call let's you call a function with the value of this explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn() when you call bar.callback(). Since when we call foo.callback.bind(foo);, obj is foo and fn is foo.callback, the result is that calling bar.callback() becomes the same as calling foo.callback().
That's where Dalorzo's answer comes from. He uses call to explicitly set the context.
There's also another function for setting the context called apply that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind() if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Try using these changes:
Use call to set context:
bar.onSomeAction.call(foo);
And I think your callback function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};
Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
VoilĂ -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.
I keep getting an error saying that my functions are not defined when I was trying to call the prototype functions in the constructor and I dont know whats wrong with it.
Here's the code I have:
function Renderer()
{
initialiseWebGL();
initialiseShader();
initialiseBuffer();
}
Renderer.prototype.initialiseWebGL()
{
//Do stuff.
};
Renderer.prototype.initialiseShader()
{
//Do Shader's stuff
};
Renderer.prototype.initialiseBuffer()
{
//Do Buffers
};
What is wrong with it?
Your syntax is wrong. Use this:
function Renderer() {
this.initialiseWebGL();
this.initialiseShader();
this.initialiseBuffer();
}
Renderer.prototype.initialiseWebGL = function () {
//Do stuff.
};
Renderer.prototype.initialiseShader = function () {
//Do Shader's stuff
};
Renderer.prototype.initialiseBuffer = function () {
//Do Buffers
};
After that you can create new object and use it by:
var rendererInstance = new Renderer();
There are a few things wrong with your Code
1.initialiseWebGl() would look for a function declared in the Global scope -> there is no function
You should use this.initialiseWebGl() to access the Objects Method
Note: this refers to the Instance of Renderer in this case
2.You are not assigning a function with Renderer.prototype.initialiseWebGL() instead you try to invoke the Renderers prototype method initialiseWebGl which gives you an error, as its not defined
3.Because the { are moved down a line they get interpreted as a Block -> this code gets executed.
If you'd had them after your () you would get a Syntax Error ->
Renderer.prototype.initialiseWebGL() {... would result in Uncaught SyntaxError: Unexpected token {
Heres an Commented Example
function Renderer() {
initialiseWebGL(); // I call the global declared function
this.initialiseShader(); //I call the Prototypes function
this.initialiseBuffer(); //Me too
}
Renderer.prototype.initialiseWebGL = function (){ //Here a function gets assigned to propertie of `Renderer`s `prototype` Object
//Do stuff.
};
Renderer.prototype.initialiseShader = function (){
console.log("Do Shader Stuff");
};
Renderer.prototype.initialiseBuffer = function (){
console.log("Do initialise stuff");
};
Renderer.prototype.initialiseBuffer() // I invoke the method above
{
console.log("I'm a Block statement");
};
function initialiseWebGL () { //I'm the global declared function
console.log("Global");
}
var ren1 = new Renderer();
/*"Do initialise stuff"
"I'm a Block statement"
"Global"
"Do Shader Stuff"
"Do initialise stuff"*/
As you can see in the consoles Output
Heres a JSBin
Since your instances inherit the (method) properties from the prototype objects, you need to access them as properties and not as plain variables:
function Renderer() {
this.initialiseWebGL();
this.initialiseShader();
this.initialiseBuffer();
}