I have this Javascript constructor-
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
inter = setInterval(this.start, 200);
}
var test = new TestEngine();
test.startMethod();
Gives me this error -
Uncaught TypeError: Object [object global] has no method 'fooBar'
I tried console.log and found out that when I call this.start from within setInterval, this points to the window object. Why is this so?
The this pointer can point to one of many things depending upon the context:
In constructor functions (function calls preceded by new) this points to the newly created instance of the constructor.
When a function is called as a method of an object (e.g. obj.funct()) then the this pointer inside the function points to the object.
You can explicitly set what this points to by using call, apply or bind.
If none of the above then the this pointer points to the global object by default. In browsers this is the window object.
In your case you're calling this.start inside setInterval. Now consider this dummy implementation of setInterval:
function setInterval(funct, delay) {
// native code
}
It's important to understand that start is not being called as this.start. It's being called as funct. It's like doing something like this:
var funct = this.start;
funct();
Now both these functions would normally execute the same, but there's one tiny problem - the this pointer points to the global object in the second case while it points to the current this in the first.
An important distinction to make is that we're talking about the this pointer inside start. Consider:
this.start(); // this inside start points to this
var funct = this.start;
funct(); // this inside funct (start) point to window
This is not a bug. This is the way JavaScript works. When you call a function as a method of an object (see my second point above) the this pointer inside the function points to that object.
In the second case since funct is not being called as a method of an object the fourth rule is applied by default. Hence this points to window.
You can solve this problem by binding start to the current this pointer and then passing it to setInterval as follows:
setInterval(this.start.bind(this), 200);
That's it. Hope this explanation helped you understand a little bit more about the awesomeness of JavaScript.
Here is a neat way to do OOP with javascript:
//Global Namespace:
var MyNamespace = MyNamespace || {};
//Classes:
MyNamespace.MyObject = function () {
this.PublicVar = 'public'; //Public variable
var _privatVar = 'private'; //Private variable
//Public methods:
this.PublicMethod = function () {
}
//Private methods:
function PrivateMethod() {
}
}
//USAGE EXAMPLE:
var myObj = new MyNamespace.MyObject();
myObj.PublicMethod();
This way you encapsulate your methods and variables into a namespace/class to make it much easier use and maintain.
Therefore you could write your code like this:
var MyNamespace = MyNamespace || {};
//Class: TestEngine
MyNamespace.TestEngine = function () {
this.ID = null;
var _inter = null;
//Public methods:
this.StartMethod = function (id) {
this.ID = id;
_inter = setInterval(Start, 1000);
}
//Private methods:
function Start() {
FooBar();
console.log(this.ID);
}
function FooBar() {
this.ID = 'bar';
return true;
}
}
//USAGE EXAMPLE:
var testEngine = new MyNamespace.TestEngine();
testEngine.StartMethod('Foo');
console.log(testEngine.ID);
Initially, the ID is set to 'Foo'
After 1 second the ID is set to 'bar'
Notice all variables and methods are encapsulated inside the TestEngine class.
Try this:
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
var self = this;
var inter = setInterval(function() {
self.start();
}, 200);
}
var test = new TestEngine();
test.startMethod();
setInterval calls start function with window context. It means when start gets executed, this inside start function points to window object. And window object don't have any method called fooBar & you get the error.
Anonymous function approach:
It is a good practice to pass anonymous function to setInterval and call your function from it. This will be useful if your function makes use of this.
What I did is, created a temp variable self & assigned this to it when it is pointing your TestEngine instance & calling self.start() function with it.
Now inside start function, this will be pointing to your testInstance & everything will work as expected.
Bind approach:
Bind will make your life easier & also increase readability of your code.
TestEngine.prototype.startMethod = function() {
setInterval(this.start.bind(this), 200);
}
Related
I have tried to organize my code in an object oriented way (as explained in MDN). In my case however, this refers to the window object. Because of that I get the error
Uncaught TypeError: Cannot read property 'render' of undefined
in
this.renderer.render(this.stage);
Why does this refer to the window object when it doesn't on MDN?
var GAME = GAME || {};
GAME.Application = function() {
this.renderer = PIXI.autoDetectRenderer(800, 600,{backgroundColor : 0x1099bb});
document.getElementById("game").appendChild(this.renderer.view);
this.stage = new PIXI.Container();
requestAnimationFrame(this.render);
}
GAME.Application.prototype.render = function() {
this.renderer.render(this.stage);
}
var app = new GAME.Application();
You need to bind your render function. This is probably the most straight forward solution.
requestAnimationFrame(this.render.bind(this));
Or instead, you could do
var context = this;
requestAnimationFrame(function() {
context.render();
});
Or you could avoid creating the free variable and use an IIFE
requestAnimationFrame((function(context) {
context.render();
})(this)));
Or, if you're using ES6, you can use an arrow function
requestAnimationFrame(() => this.render());
Another easy improvement you could make is passing the render element into your Application constructor
function Application(elem) {
this.renderer = ...
elem.appendChild(this.renderer.view);
}
new Application(document.getElementById("game"));
Lets talk about this, context, and functions
A good way to think about it, is that this refers to the object on the left of the . of the method that calls it.
var someObj = {
name:'someObj',
sayName: function(){
console.log(this.name);
}
};
someObj.sayName(); // prints someObj
Functions that are not methods of an object are bound to the window object.
window.name = 'window';
function sayName(){
console.log(this.name);
}
sayName(); //prints window
The above is equivalent to
window.sayName(); // window is on the left of the dot, so it is `this`
When you pass a method of an object as a parameter, or assign it to a variable, it loses its original context. Below, the sayName method of someObj loses someObj as the context and gains someOtherObj.
var someOtherObj = {
name:'someOtherObj'
};
someOtherObj.sayName = someObj.sayName;
someOtherObj.sayName(); // prints someOtherObj
To get around it, You can bind a context to a function
var yetAnotherObj = {
name: 'yetAnotherObj'
};
var sayYetAnotherObj = sayName.bind(yetAnotherObj);
sayYetAnotherObj(); // prints yetAnotherObj
Or pass an anonymous function that calls the the method on the object itself
var OneLastObj = function(){
var self = this;
this.someValue = aFunctionTakingAcallback(function(){
return self.doSomeStuff();
});
}
Something to remember when passing functions around as a parameters is that you are passing a reference to the function. The function itself is not bound to the object that it may be a method of.
function Base(x) {
this.x = x;
this.sub = new (function Sub() {
this.method = function() {
return function() {**this.super.x**}; // return an anonymous function that uses the outer x, but for reasons the anonymous function can't take parameters
}
});
}
var base = new Base(5);
console.log(base.sub.method()())
basically, in this example I'm trying to create an object Base who has a sub object sub, which has a 'method' that uses an anonymous function. The anonymous function needs the x but cannot take in any parameters..
Is there any way to access the this.x of Base?
At the beginning I tried the following.
This works well when Base is not inherited by other objects.
But in the following
"use strict";
function Base(x) {
var that = this;
this.sub = new (function Sub() {
this.method = function() {
return function() {
console.log(that); // displays Base *note[1]
return that.x;
}; // return an anonymous function that uses the outer x, but for reasons the anonymous function can't take parameters
}
});
}
function Other() {
Base.call(this); // why do I need to call Base.call(this) ?
this.setX = function(x) {this.x = x;}
}
Other.prototype = new Base();
var other = new Other();
other.setX(5);
console.log(other.sub.method()()); // otherwise undefined?
Base is extended by Other, after struggles I figured out I need to call Base.call(this); in order to make it works. Otherwise console.log(other.sub.method()()); will be undefined.
If I put a console.log(that) at *note[1], it will actually says the that is a Base object even though I construct it using var other = new Other(); I guess the problem would be solved if that is an Other object? What am I misunderstanding here?
My question is why do I need to call Base.call(this)? Why does it works after calling it? Is there any better solution to this situation?
If I understand you correctly you'd like Base to have a Sub and Sub is aware of the Base instance?
At the moment you're declaring the Sub type over and over again every time you create a Base, not the most effective way of doing it. Maybe try something like this:
function Sub(baseInstance){
this.baseInstance = baseInstance;
}
Sub.prototype.displayBaseX=function(){
console.log('base.x in sub:', this.baseInstance.x);
};
function Base(){
this.x = 22;
this.sub = new Sub(this);
}
You should not create an instance of Parent to set prototype of Child, use Object.create instead.
As for Parent.call(this, arguments) that is to re use the constructor function of Parent in Child when inheriting. More info about that can be found here: https://stackoverflow.com/a/16063711/1641941
The reason why your code doesn't work without Base.call(this); is because Base constructor defines and creates it's Sub type and instance. Not re using the Base constructor code when creating a Other type would cause the Other type not to have a Sub instance.
Constructor code deals with instance specific members and prototype can hold members that are shared by all instances. So usually behavior (functions) and a value for immutable default data members (numbers, string, boolean) can be found on the prototype.
I'm not exactly sure what you're end goal is here. But to solve your scope issue, this seems to work.
"use strict";
function Base(x) {
this.sub = new (function Sub(_this) {
this.method = (function(_this) {
return function() {
return _this.x;
};
})(_this);
})(this);
}
function Other() {
Base.call(this); // why do I need to call Base.call(this) ?
this.setX = function(x) {this.x = x;}
}
Other.prototype = new Base();
var other = new Other();
other.setX(5);
console.log(other.sub.method()); // otherwise undefined?
And a fiddle.
I want to create a prototype function that has its own scope. For this, I use an anonymous function but I cannot find a way to access the members of the object.
Here is a simplified version of what I am trying to achieve:
function F() {
this.counter = 0;
}
F.prototype.increment = (function() {
var lastIncrementTime = -1;
var caller = this; // <--- it fails here because this is the Window object
return function(time) {
if (time > lastIncrementTime) {
caller.counter++;
lastIncrementTime = time;
return caller.counter;
}
return caller.counter;
}
})();
f = new F();
f.increment();
I know it fails because this does not refer to F or the f object.
Is there a way to access it?
The immediately invoked function expression (IIFE) itself only gets invoked once, all calls to increment will use the variables as they were last left and not re-var them.
Change the invocation context using call, apply or bind
F.prototype.increment = (function() {
// this === F.prototype
// ...
}).call(F.prototype);
The this in this example context will not be instance specific, but be the prototype.
It seems like you actually want to achieve something a little bit different, where you have an independent function to initialise an instance-specific property with it's own closure, when the instance is constructed. These types of actions can consume a bit of memory so don't store too much unique data.
function F() {
this.counter = 0;
this.__init_increment(); // create `this.increment`
}
F.prototype.__init_increment = function () {
var lastIncrementTime = -1;
this.increment = function (time) {
if (time > lastIncrementTime) {
this.counter++;
lastIncrementTime = time;
}
return this.counter;
};
};
var f = new F();
f.increment(0); // 1
f.increment(0); // 1
f.increment(5); // 2
In this example, this.increment is a different function for each instance, which means you have a different closure for each instance. They are generated by a function in the prototype, which sets the instance property. The generator does not have to be in the prototype, just remember about the invocation context when applying it to your instance.
Move your var caller = this inside the anonymous function, where this will have been set appropriately.
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 thought scope chain would make the first "test = new test();" work, but it doesn't. why?
var tool = new Tool();
tool.init();
function Tool(){
var test;
function init(){
//does not work, undefined
test = new Test();
//does work
this.test=new Test();
console.log(test);
}
}
function Test(){
}
EDIT: by not working i mean, it says that test is 'undefined'
It's simple. Your Tool instance does not have a init method. The init function in your code is merely a local function of the Tool constructor. Tool instances do not inherit such functions.
If you want your Tool instances to have a init method, you can:
assign it as a method inside the constructor:
function Tool () {
this.init = function () { ... };
}
or assign it to Tool.prototype (outside of the constructor!):
Tool.prototype.init = function () { ... };
The second option performs better, since all instances share the same init function. (In the first option, each instance gets its own init function which is created during the constructor call.)
Are you trying to access test in the scope of Tool, or on the object returned by it? They are two different variables. I've labeled them A and B:
var tool = new Tool();
function Tool(){
var testA; // Private
this.init = function(){
testA = 1;
this.testB = 9; // Public
}
this.getTestA = function(){ // Public method to access the private testA
return testA;
}
}
tool.init();
console.log( tool.getTestA() ); // 1
console.log( tool.testB ); // 9
testA is known as a Private variable, only accessible through Tool's methods, while testB is public.
Does this cover what you're looking for?
By the way, if you're making a lot of instances of Tool, remember to use Tool's prototype to define the functions instead, so your code is more memory efficient, like so:
function Tool(){
var testA;
}
Tool.prototype.init = function(){
testA = 1;
this.testB = 9;
}
Tool.prototype.getTestA = function(){ return testA; }