Javascript Function Self Invoking Inside Object - javascript

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.

Related

How to call a function from `setInterval`?

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);
}

UnCaught TypeError - Nested objects in Javascript? Why is this not allowed? Object literal notation works

Playing around with some JS tests and I'm trying to instantiate some nested objects in my v namespace. As you'll see below, ClassA and ClassB work as expected. When I try and nest some objects under another property (myCustomProperty) I start running into issues! Could someone explain?
Below is the original code:
var v = (v) ? v : {};
v.someClassA = (function() {
this.hello = function() {
console.log("Class A Hello!");
}
});
v.someClassB = (function() {
this.hello = function() {
console.log("Class B Hello!");
}
});
// this all works!
var myClassA = new v.someClassA();
var myClassB = new v.someClassB();
v.myCustomProperty = (function() {
function someClassC() {
this.hello = function() {
console.log('C');
}
}
function someClassD() {
this.hello = function() {
console.log('D');
}
}
return {
someClassC: someClassC,
someClassD: someClassD
}
});
// Uncaught TypeError: v.myCustomProperty.someClassC is not a function! Why?
var myClassC = new v.myCustomProperty.someClassC();
var myClassD = new v.myCustomProperty.someClassD();
myClassA.hello();
myClassB.hello();
myClassC.hello();
myClassD.hello();
If I change my declaration of v.myCustomProperty to use object literal notation, then it ALL WORKS! :
v.myCustomProperty = {
someClassC: function() {
this.hello = function() {
console.log('C');
}
},
someClassD: function() {
this.hello = function() {
console.log('D');
}
}
}
I guess my question really is how would I make this work using the notation in my original snippet? Possible? Horrible practice to do it that way?
Thanks!
v.myCustomProperty is a function that returns an object. You have to call the function first:
new (v.myCustomProperty().someClassC)();
// ^^
Otherwise, v.myCustomProperty.someClassC() tries to access the property someClassC of the function, and we all know (hopefully) that functions don't have such a property.
Or maybe you intended to execute the function immediately and assign the object to myCustomProperty?
v.myCustomProperty = (function() {
// ...
}()); // <- call function

Calling an object's prototype method from another object's method

I am trying to create a simple callback system that would fire upon hitting a button. Rather than the callback being a factory function, it is a prototype method of a different object. I've gotten it to work but I don't understand it. Why do I need to use .bind(object) to get the object to fire its method? Originally I tried no bind, and then bind(this), which both failed.
function Bar() {}
Bar.prototype = {
getStuff: function () {
return "Hello";
},
setStuff: function () {
console.log( this.getStuff() );
}
}
function Foo() {
this.afterSubmit = null;
var self = this;
$('button').click(function () {
self.submit()
});
return this;
}
Foo.prototype = {
submit: function () {
if (this.afterSubmit !== null) {
this.afterSubmit();
}
$('#msg').append('clicked ');
return this;
},
setAfterSubmit: function (callback) {
this.afterSubmit = callback;
return this;
}
}
var bar = new Bar();
var foo = new Foo().setAfterSubmit(bar.setStuff.bind(bar));
// Why do I need to bind bar ?
Please take a look at my fiddle
https://jsfiddle.net/j5qfuzna/
this.afterSubmit();
This is setting the context to the Foo instance. Binding it to the Bar instance prevents that from happening.

How to call one method from another inside function

I have tried to create javascript object like
function Caller() {
this.init = function() {
makeCall();
};
this.makeCall = function(){/*come code here*/}
}
var a = new Caller();
a.init();
I got error function is not defined, same thing happens when I try to call this.makeCall();
When I remove this from makeCall definition it works, but when I remove also from init it doesn't work. How to solve this ?
Use this.makeCall(). Additionally define makeCall() before its use.
function Caller() {
this.makeCall = function () { /*come code here*/ }
this.init = function () {
this.makeCall();
};
}
var a = new Caller();
a.init();
DEMO
Use this.makeCall() it is a method so you can use it.
Try this this.makeCall() function is executed successfully here.
<html>
<head>
</head>
<body>
<script>
function Caller() {
this.init = function() {
this.makeCall();
};
this.makeCall = function(){
alert('hello');
}
}
var a = new Caller();
a.init();
</script>
</body>
</html>
You need to call the function properly using this
makeCall(); => this.makeCall();
You can use IIFE to declare Caller:
var Caller=(function () {
function constructor() {
}
function makeCall() {
/*come code here*/
}
constructor.prototype.init=function () {
makeCall.apply(this, arguments);
};
return constructor;
})();
var a=new Caller();
a.init();
and be sure that Caller is loaded before it is invoked.
To answer your question and to enhance your code you should have a look at this:
function Caller(){};
This is your constructor.
You can then add methods to your constructor like this:
Caller.prototype.init = function () {
// do something
}
When you create instances of your "class" Caller
var a = new Caller();
every instance will have the methods you described in the Caller.prototype object.
And to solve your problem:
If one prototype method wants to call another prototype method, you need to do this:
// do some setup in your constructor
function Caller(phoneNumber) {
this.phoneNumber = phoneNumber;
};
Caller.prototype.init = function () {
// the code word "this" will be the instance itself
this.makeCall();
};
Caller.prototype.makeCall = function () {
// do whatever this method needs to do
alert('calling ' + this.phoneNumber);
};
var a = new Caller(0123456789);
a.init();
The main thing to understand is, that in every prototype method, the keyword this references the current instance of the constructor (in this case Caller).

avoid needing to declare 'var me = this' for javascript prototype functions

Currently, I create objects in javascript by declaring a construction (regular function) then add methods to the prototype like so
function Test(){
}
Test.prototype.test1 = function(){
var me = this;
}
However, I would like to avoid having to declare var me = this at the top of every function. The following seems to work, but seems like it would be very inefficient:
$(document).ready(function(){
var n = 0;
(function(){
function createTest(){
var me;
function Test(){
this.n = n;
this.testArr = [1, 2, 3, 4];
n++;
}
Test.prototype.test1 = function(){
me.test2();
};
Test.prototype.test2 = function(){
alert(me.n);
$.getJSON('test.php', {}, function(reply)
//want to be able to use 'me' here
me.newField = reply;
});
};
var t = new Test();
me = t;
return t;
}
window['createTest'] = createTest;
})();
var t = createTest();
t.test1();
var t2 = createTest();
t2.test1();
t.test1();
});
This code outputs the expected, but is it actually as inefficient as it looks (the Test object being re-declared every time you call createTest())?
Anyhoo, this would seem a bit hacky... is there a completely different way to do this that is better?
EDIT: The real reason I would like to do this is so that callbacks like the one in test2 will have references to the correct this.
What you can do is bind the current this value to a function and store a copy somewhere. (For the sake of efficiency.)
if (!Function.prototype.bind) {
// Most modern browsers will have this built-in but just in case.
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () { },
bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
function Test(n) {
this.n = n;
this.callback = (function () {
alert(this.n);
}).bind(this)
}
Test.prototype.test1 = function () {
this.test2();
}
Test.prototype.test2 = function () {
doSomething(this.callback);
}
function doSomething(callback) {
callback();
}
var t = new Test(2);
t.test1();
I realize your question was not tagged with jQuery, but you are using it in your example, so my solution also utilizes jQuery.
I sometimes use the $.proxy function to avoid callback context. Look at this simple jsfiddle example. Source below.
function Test(){
this.bind();
}
Test.prototype.bind = function(){
$('input').bind('change', $.proxy(this.change, this));
// you could use $.proxy on anonymous functions also (as in your $.getJSON example)
}
Test.prototype.change = function(event){
// currentField must be set from e.target
// because this is `Test` instance
console.log(this instanceof Test); // true
console.log(event.target == $('input')[0]); // true
this.currentField = event.target; // set new field
};
function createTest(){
return new Test();
}
$(function(){ // ready callback calls test factory
var t1 = createTest();
});
Most of the time, I just declare a local variable that references this, wherever I need a reference to this in a callback:
function Foo() {
}
Foo.prototype.bar = function() {
var that=this;
setTimeout(function() {
that.something="This goes to the right object";
}, 5000);
}
Alternatively, you can use bind() like this:
Function Foo() {
this.bar = this.bar.bind(this);
// ... repeated for each function ...
}
Foo.prototype.bar = function() {
}
What this gives you is that every time you create a new Foo instance, the methods are bound to the current instance, so you can use them as callback functions for setTimeout() et al.

Categories

Resources