How to call a function from `setInterval`? - javascript

function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function() {
this.func2();
}, 200);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
I have the following exception:
this.func2(); ^ TypeError: this.func2 is not a function
at Timeout._onTimeout(C: \Users\ Admin\ Desktop\ CANVAS\ test.js: 15: 12)
at ontimeout(timers.js: 365: 14)
at tryOnTimeout(timers.js: 237: 5)
at Timer.listOnTimeout(timers.js: 207: 5)
Why this.func2 is function when I call without setInterval and is not a function when I call from setInterval?

Cause this is not referring to Obj in your case. That context has been changed and this is referring to setInterval context.
NOte the line var self = this;, which storing this for later usage.
This should work
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function(){
self.func2();
}, 200);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();

Maybe you wanted to do something like this:
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function(){
this.func2();
}.bind(this), 200);
}
}
But this will soon lead to an overflow, because you create a new interval on each execution. Maybe you wanted to use setTimeout?

You can bind this
this.func2 = function() {
setInterval(function() {
this.func2();
}.bind(this), 200);
}
DEMO

The problem is the context inside of setTimeout is the top-level, not the context of your object, so this will point to window (in a browser) instead of your object. There are a couple of ways to solve the problem.
The most recent version is to use the bind method to bind the context of your function.
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
console.log('hi');
setTimeout(this.func2.bind(this), 1000);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
Notice that I use this.func2.bind(this) as the callback for setTimeout (same for setInterval, just your example would be quite spammy if I left it with that). That means that no matter where it's called, it's this will always be your object.
The slightly older way to do it is to wrap it up in self-calling function that'll isolate it to a scope that has this set to your object.
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
console.log('hi');
setTimeout((function(self){
return function () {
self.func2();
}
})(this), 1000);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
This way is a bit more involved, but basically I just wrapped what you originally had in a self-calling function that passes in this as a parameter for self, then I use self.func2() inside of it.

this is not passed to your setInterval callback because it is always called in the global context, just like when you use setTimeout.
this is either undefined (in 'use strict' mode) or the window object (in regular/loose mode). You can pass extra arguments to setInterval, though, and these are passed on to your callback:
this.func2 = function() {
setInterval(function(self) {
self.func2()
}, 200, this)
}
Demo Snippet
function Obj() {
this.func1 = function() {
this.func2()
}
this.func2 = function() {
setInterval(function(self) {
console.log(typeof self.func2) //=> 'function'
}, 200, this)
}
}
function main() {
var obj = new Obj()
obj.func1()
}
main()

Use arrow function:
this.func2 = function() {
setInterval(() => { this.func2(); }, 200);
}

Related

Javascript Function Self Invoking Inside Object

Instead using setInterval i can use this, to repeatly call an function.
function foo(){
setTimeout(foo, 1000);
}();
The problem is, i want to do the same thing, inside an object, here the snippet.
var evt;
var init;
evt = function() {
return {
cycle:function(str) {
setTimeout(function(str) {
this.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
Then the error shows up, it said
this.cycle() is not a function.
I'm trying to make an variable as this at the above line of my codes, here, like this
var evt;
var init;
evt = function() {
var parent;
parent = this;
return {
cycle:function(str) {
setTimeout(function(str) {
parent.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
But still getting.
parent.cycle() is not a function
Is there a way to do this, what i want here is, went i call evt.cycle("Hello World") after first attempt showing Hello World it will repeatly showing Hello World in every next seconds.
I need to keep the function inside the object that generated by that function. Thanks for any correction.
When you return a new object a new scope is defined. So you should bind this pointer to the function. Or you can use .bind(this) function in this way:
setTimeout((function(str){
}).bind(this), 1000)
For more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Or you can use call or apply: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
In es6 you could use ()=>{} (arrow) function, when the this pointer is inherited.
Other working solution:
var evt;
var init;
evt = function() {
var parent;
parent = this;
return {
cycle:function(str) {
var me = this;
setTimeout(function(str) {
console.log("cycle");
me.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
const evt = function() {
return {
i: 0,
cycle: function(str) {
const _this = this;
setTimeout(() => {
console.log(str.substring(0, this.i));
_this.cycle(str, ++this.i);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hello world");
I extended the example a little bit to illustrate the effect of this a little more.

Why is "this" scope wrong in a defined property?

I am creating a debounce function in my code, I have made a really simple example however when using it in combination with Object.defineProperty the value of "this" is questionable.
This is the code:
// Debounce a function call
Object.defineProperty(Function.prototype, "debounce", {
enumerable: false,
writable: false,
value: function(ms, scope) {
var fn = this,
time = ms,
timer;
function debounced() {
var args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function() { fn.apply(scope, args); }, time);
}
return debounced;
}
});
And it is called with (where the .bind... is done as part of an external library):
function() {
console.log(this.something);
}.debounce(300).bind(someObject)();
And it works, but the value of "this" cannot be used. Within the value function "this" points to the original function. Is there anyway I can get the bound scope with this design style?
Example
var a = {
b: function() {
console.log(this.something);
}.debounce(300),
};
var c = {
something: true,
};
// I can't control this code -> 3rd party library
a.b.bind(c)();
You need to use the this of debounced when debounced is called
in old school JS
Object.defineProperty(Function.prototype, "debounce", {
value: function value(ms) {
var fn = this,
time = ms,
timer;
function debounced() {
var _this = this;
var args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
return fn.apply(_this, args);
}, time);
}
return debounced;
}
});
or simpler in ES2015+
Object.defineProperty(Function.prototype, "debounce", {
value: function(ms) {
var fn = this,
time = ms,
timer;
function debounced() {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), time);
}
return debounced;
}
});
You can also remove enumerable:false, writable:false as false is the default value for these properties - makes the code a little smaller :p

Function is undefined in JS object

I create object var myObj = new functon () {...}.
In that object i add functions like :
var myObj = new function () {
this.func1 = function() {
func2();
}
this.func2 = function() {
...
}
}
As you can see in func1 I try to call func2 but it is always undefined. Why? Cause everything is in one object.
Change your scripts to
var myObj = function () {
var self = this;
this.func1 = function () {
self.func2();
};
this.func2 = function () {
...
};
};
On top of solutions provided by others. If you are going to call a javascript function that is defined like this
var func = function(){}
the function definition needs to come before the function call.
In the other way of defining a function this does not matter.
function func(){}
So Overall Code should be
var myObj = function(){
this.func2 = function(){
...
}
this.func1 = function(){
func2();
}
}
It's undefined because you don't have local variable func2. So correct reference should be this.func2().
However even in this case your code is not ideal construction object like this (mixing constructor and anonymous function) (although correct). In this case it's better to use object literal in the first place rather then create constructor function for just creating one single object instance:
var myObj = {
func1: function () {
this.func2();
},
func2: function () {}
};
You should call func2 like this
var myObj = new function () {
this.func1 = function () {
this.func2();
}
this.func2 = function () {
console.log('func2');
}
}
myObj.func1();
if you want call func2 with this. and without, you can do it like this
var myObj = new function () {
function func2() {
console.log('func2');
}
this.func1 = function() {
this.func2();
func2();
}
this.func2 = func2;
}
myObj.func1();
you can call like this.
Calling func2() directly, searches the function of window object.
var myObj = functon(){
var current = this;
this.func1 = function(){
current.func2();
}
this.func2 = function(){
...
}
};

Public function in Singleton calling itself

I'm trying to use a singleton pattern but I am having trouble with implementing a recursive public function.
var singleton = (function(){
var self = this;
function privateFunc(){
console.log('I can only be accessed from within!');
}
return{
publicFunc: function(){
//More stuff here
setTimeout(self.publicFunc, 1000);
}
}
})();
I am calling it with singleton.publicFunc
I get this error Uncaught TypeError: Cannot call method 'publicFunc' of undefined.
My understanding is var self is actually the Window object in this instance, so I have to pass singleton.publicFunc as the callback for this to work, but it doesn't seem very "DRY" (Don't repeat yourself). Is there
a better way to accomplish this while using a singleton?
With API calls
var wikiAPI = (function(){
var self = this;
return {
getRandomArticle : function() {
return $.getJSON("http://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&prop=extracts&exintro=&format=json&callback=?", function (data) {
});
},
fireAPICalls : function() {
self.getRandomArticle().done(function(data) {
for(var id in data.query.pages) {
this.data = data.query.pages[id];
}
console.log(this.data);
setTimeout(self.fireAPICalls, 1000);
});
}
}
})();
You can use a named function expression like so:
var singleton = (function(){
var self = this;
function privateFunc(){
console.log('I can only be accessed from within!');
}
return{
publicFunc: function nameVisibleOnlyInsideThisFunction(){
//^-------------------------------^
//More stuff here
setTimeout(nameVisibleOnlyInsideThisFunction, 1000);
}
}
})();
I just saw your edit. What would help is having a reference to the functions you are trying to call. So how about something like this:
var wikiAPI = (function(){
var self = this;
var randomArticle = function() {
return $.getJSON("http://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&prop=extracts&exintro=&format=json&callback=?", function (data) {
});
};
var repeatFunc = function fireApi() {
randomArticle().done(function(data) {
for(var id in data.query.pages) {
this.data = data.query.pages[id];
}
console.log(this.data);
setTimeout(fireApi, 1000);
});
};
return {
getRandomArticle : randomArticle,
fireAPICalls : repeatFunc
}
})();
Use bind in the setTimeout() to bind the function to the right scope:
publicFunc: function() {
setTimeout(this.publicFunc.bind(this), 1000);
}
Demo: http://jsfiddle.net/te3Ru/
You can't use this in a IIFE. If you want to use this properly you need to create an object/instance of a function, like so:
var singleton = (function () {
// allow to omit "new" when declaring an object
if (!(this instanceof singleton)) return new singleton();
var self = this, // self now points to "this"
privateFunc = function () {
console.log('I can only be accessed from within!');
};
this.publicFunc = function() {
console.log(this); // this now points to the correct object
setTimeout(function () {
self.publicFunc.call(self); // call function in the "self" scope
}, 1000);
};
return this;
});
singleton().publicFunc();
it's not much of a singleton now, but you can have the closest thing to private and public that javascript has!

Accessing a containing object from with in its method?

In the snippet below, an object literal holds properties, one of which is a method that needs access to the the object literal.
However, b.c. it is only used as an event handler callback, this always points to the element that triggered the event.
I need to access the containing object.
Otherwise, I'm forced to put a function in a function which seems odd.
/***************************************************************************************************
**MSimMenu - simple drop down menu
*/
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
// in mouseout this points to the element that triggered the event
// need access to containing object
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
init: function () {
var self = this;
// tempoaray fix - function in function seems odd
function mouse_out() {
self.A.time_out_id = NS.setTimeout(self.hideBottom, self.A.TIME_DELAY);
}
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
self.E.hold_name.addEventListener("mouseout", mouse_out, false);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
Final Code Using Bind
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
init: function () {
var self = this;
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", self.mouse_out.bind(self), false);
self.E.hold_name.addEventListener("mouseout", self.mouse_out.bind(self), false);
},
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
I have seen alot of people create a variable to assign the object to and then use the variable.
var that = {
myfunc:function(){
console.log(that)
}
};
NS.parsel(that);
I actually like moving most of the logic into the init method. Provides nice encapsulation with an easy way to declare public and private methods/variables. For example:
NS.parsel({
init: function() {
var self = this;
//public instance variable
self.Name = 'MSimMenu';
//private instance variables
var A = {
time_out_id: null,
TIME_DELAY: 1000
};
var E = {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
};
//public instance method
self.showBottom = function () {
E.wrap_bottom.style.visibility = 'visible';
};
//private instance method
E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
function mouse_out() {
A.time_out_id = NS.setTimeout(self.hideBottom, A.TIME_DELAY);
}
}
});
There's a lot of ways you can get what you want.
One trick you can do is to not use the mouse_out function directly, but provide a helper function like get_mouse_out() that returns a bound version of the function.
var myobject = {
data:"Some data",
_mouse_out: function() { console.log(this.data); }
get_mouse_out: function() {
var self = this;
return function(){ return Function.apply(self._mouse_out,self,arguments); }
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//This doesn't work.
do_callback( myobject._mouse_out);
//But this does
do_callback( myobject.get_mouse_out() );
EDIT: Improved version inlining _mouse_out and using bind.
var myobject = {
data:"Some data",
get_mouse_out: function() {
function _mouse_out() { console.log(this.data); }
return _mouse_out.bind(this);
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//But this does
do_callback( myobject.get_mouse_out() );
If you're willing to have init be called as setup before mouse_out is used then you can do this.
var myobject = {
data:"Some data",
init: function() {
function _mouse_out() { console.log(this.data); }
this.mouse_out = _mouse_out.bind(this);
}
}
myobject.init();
fn( myobject.mouse_out );
Finally there's a nice variant on Shanimals that works a similar way, but provides encapsulation.
NS.parcel( (function(){
var myobj = {};
myobj.data = "Some data";
myobj.mouse_out = function(){ console.log(myobj.data); }
return myobj;
})()
);

Categories

Resources