Access variable within callback function - javascript

Hi I have the following code where I am calling a function that takes a callback function that takes a parameter. Basically I am trying to get the contents of e into this.items but I cant escape the callback function?
function stash(){
this.items = new Array();
this.get = function(){
opr.stash.query({},function(e){
console.log(this.items); //this is not defined
});
}
}
s = new stash();
s.get();

Problem: callback function's context object (this) no longer refers to the context object of get() method.
Solution: bind your callback function to the target object, like this:
opr.stash.query({},(function(e){
console.log(this.items);
}).bind(this));

this is no longer in scope on the callback so make a reference to it that you can user later. The .bind(this) is a good solution in modern browsers but may fail in older versions of Internet Explorer as that method may not be available.
function stash(){
var self = this;
this.items = new Array();
this.get = function(){
opr.stash.query({},function(e){
console.log(self.items);
});
}
}

I ran into a similar problem in d3.chart.
I don't have access to opr.stash at the moment so i couldn't test this first, but have you tried something like the following:
function stash()
{
var myStash = this;
myStash.items = new Array();
myStash.get = function(){
opr.stash.query({},function(e){
console.log(myStash.items); //this is not defined
});
}
}
s = new stash();
s.get();

Related

How to access javascript object Arrays inside prototype function

Hello I'm having a problem that haven't searched before
I'm trying to recode my javascript used for accessing google api's so that I can create objects to access them in further projects .
Let me Explain the Code because I'm not posting the original code here below code is the just like an example of my code
I'm a having constructor function and it has arrays declared with 'this' keyword. and Then I have an prototype of the constructor function and inside that prototype I had a nameless function created to access jquery requested output from a server.But I'm unable to access those array objects
function cons(){
this.objectarray = [];
}
cons.prototype.profun = function(){
this.objectarray[0] = 'working';
alert(this.objectarray[0]);//Access is possible
$.get('requesting url',{'parameters'},function(data){
alert(this.objectarray[0]);//Access is not possible
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
when I try to access the object inside that jquery function I'm getting this error from browser console
TypeError: this.YouTubeTitle is undefined
You have to cache the this object so that when you use the this keyword in your callback, it refers to the correct object:
function cons(){
this.objectarray = [];
}
cons.prototype.profun = function(){
// this is volatile in JavaScript. It's meaning changes depending
// on the invocation context of the code that contains it. Here,
// this will refer to your "cons" object instance.
var self = this;
this.objectarray[0] = 'working';
alert(this.objectarray[0]);
$.get('requesting url','parameters',function(data){
// But here, "this" will be bound to the AJAX object
alert(self.objectarray[0]); // <-- Use cached this object
});
};
//*************************
var c = new cons();
c.profun();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Inside the GET-Callback there is a local this which overrides your Prototype-Function's this. Store the outer this in a named variable and call that inside the callback and you will be fine.
let outerThis = this;
somethingWithCallback(function(){
alert(outerThis);
})
I've found solution for this from one of the comments
the below code is the solution
function cons(){
this.objectarray = [];
}
cons.prototype.profun = function(){
this.objectarray[0] = 'working';
alert(this.objectarray[0]);
$.get('requesting url',{'parameters'},function(data){
alert(this.objectarray[0]);
}.bind(this));//by binding 'this' we can access the 'objectarray'
}
Thanks for the solution #Redu from comments
You can wrap your anonymous function with $.proxy to persist the outer this context.
function cons() {
this.objectarray = [];
}
cons.prototype.profun = function() {
this.objectarray[0] = 'working';
alert(this.objectarray[0]);
$.get('requesting url', {
'parameters'
}, $.proxy(function(data) {
alert(this.objectarray[0]);
}, this));
}

how can I access this inside a function without using that or self in javascript?

Im trying to write some OOP in javascript, and I stumbled upon a problem that I am sure Im going to have later on in my code and wish to deal with it now.
for example take this code:
var FeedClass = function(){
this.init = function(){
this.loadItems();
},
this.loadItems = function(){
var that = this; // heres my problem
function inner(){
that.startLoading();
}
inner();
},
this.startLoading = function(){
alert("started Loading");
}
this.init();
};
var feed = new FeedClass();
the problem is Im going to use a lot of inner functions that will call "this", my code will be messy if I kept writing var that = this inside every scope. Is there another pattern I can use or a way to get around it?
You can use the call method to set the context for the function:
this.loadItems = function(){
function inner(){
this.startLoading();
}
inner.call(this);
},
The apply method works similarly, the difference is how you specify parameters in the call.
You can also use the bind method to set the context of a function. That allows you to bind the context to the function and pass the function reference on to be called later:
this.loadItems = function(){
function inner(){
this.startLoading();
}
var i = inner.bind(this);
i();
},
Note: The bind method is not supported in IE 8 or earlier.

JS - called method is not a function

I have object:
var devark = {
init: function() {
var obj = this;
obj.assignHandlers();
},
assignHandlers: function() {
var obj = this;
document.getElementById("menu-toggler").onclick = obj.documentFunctions[0];
},
documentFunctions: [
function() {
toggleClass(this, "opened");
}
]
};
on window.load, I am calling the init method. That works fine but when it calls another object method assignHandlers, it throws an error:
[17:54:33.192] TypeError: obj.assignHandlers is not a function
Why is it?
Like #Bergi said, it's a this value issue that can be solved by doing:
window.onload = function () {
devark.init();
};
The difference between both ways is the value of this within the init function. To determine the natural value of this, look at the left-side of the . in a method call, such as obj.someFn();. Here the value of this within someFn will be obj. However, when you do window.onload = devark.init;, you can imagine the handler will later be invoke like window.onload(). Which means the value of this within the onload function, which is really the init function will be window, not devark.
You can also use Function.prototype.bind to bind a specific this value to a function.
window.onload = devark.init.bind(devark);

How to properly pass functions to other functions in javascript

I am trying to understand the way that javascript passes functions around and am having a bit of a problem groking why a prototype function can NOT access a var defined in a function constructor while a function defined in the constructor can access the var. Here is code that works:
var model = function model() {
this.state = 1;
this.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
}
var othermodel = function othermodel(mdl) {
this.GetStateFn = mdl.GetState;
}
othermodel.prototype.WriteState = function() {
console.log(this.GetStateFn.call());
};
var m = new model();
var o = new othermodel(m)
o.WriteState();
This works and makes sense - the GetState() function can access this.state.
However, if I create GetState as follows:
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
The result will be an error that scope is not defined.
I would prefer to have this work with the prototype method as I do not want a copy of the function in ever model, but it would seem that prototype can't work because it can't access the specific instance of the model.
So, can someone provide me with a good explanation of a) what I need to do to get this to work with prototype (assuming I can) and b) if I can't get it to work with prototype, what is the reason so I can understand better the underpinnings of the issue.
Why not simply write the function this way
model.prototype.GetState = function() { return this.state; }
var othermodel = function othermodel(mdl) {
this.GetStateFn = mdl.GetState.bind(mdl);
}
othermodel.prototype.WriteState = function() {
console.log(this.GetStateFn.call());
};
The above code will work, as in most cases you will execute code like m.GetState(). That is an example of invoking a function as an object method. In that case, this is guaranteed to point to the object m. You seem to know how the prototype chains work, so I won't go there.
When assigning the function reference to the other model, we use the .bind to ensure that within GetState, this points to mdl. Reference for bind: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
Your original IIFE's were in effect your implementation of bind. The issue was the value of this was wrong. Currently, every time you need to assign models function to some other function, you will need to use bind at all those times. You have tagged your question as node.js, bind is available on the Function prototype in node.js and any ES5 compatible browser. If you need to run the above code on older browsers or environments that do not support bind, replace bind with your IIFE.
As for why your code isn't working,
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
Here, this doesn't refer to the eventual model object (m). this can refer to any one of 5 options in javascript. Refer: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this
Lets assume the above code is in an html file inside some script tag. Then this will refer to the window object. window doesn't have any property called state, hence the undefined. If you were to console.log(this.m, this.o) at the end of you script, you would see the respective m and o objects.
When defined like this:
var model = function model() {
this.state = 1;
this.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
}
the anonymous function is declared and immediately executed. this is passed as a parameter to that self-executing function. As a result - a new function is returned, but this function has scope parameter in its closure - so that it is accessible after the scope has exited. As a result - the function, when invoked, can still access state property of that "enclosed" this (the one that became scope and was closed upon).
If you define it like this:
model.prototype.GetState = (function(scope){
return function(){ return scope.state;};
})(this);
the mechanism is the same, it's just this is not. It is now the context of the scope you execute the above code in. Assuming it's done in global scope - it would be window object.
If you don't want to use bind because of its support with older browsers, you could try this:
http://jsfiddle.net/j7h97/1/
var model = function (state) {
this.state = state || new Date().getTime();
};
model.prototype.GetState = function () {
return this.state;
};
model.prototype.WriteState = function () {
console.log("model WriteState: " + this.GetState());
};
var othermodel = function othermodel (mdl) {
this.GetStateFn = function () {
return mdl.GetState.call(mdl);
};
};
othermodel.prototype.WriteState = function () {
console.log("othermodel WriteState: " + this.GetStateFn());
};
var model1 = new model();
model1.WriteState();
var othermodel1 = new othermodel(model1);
othermodel1.WriteState();
var model2 = new model();
model2.WriteState();
var othermodel2 = new othermodel(model2);
othermodel2.WriteState();
Seems to do what you want without bind. I created the model.prototype.WriteState for testing purposes.
It depends on where it is called. If it's in global scope, this will not refer the the model. If it's running in a browser it will refer to the global window object instead.

Javascript - revealing object and variable scope

http://jsfiddle.net/ZLH7J/1/
What the jsFiddle and code below shows are two examples that essentially do the same thing. When trying to call first(); or this.first(); in either example, an undefined error is thrown. I can call the functions later through the instance, but not when trying to instantiate the object using init(){...}() like a constructor. I put init() at the bottom thinking it was an order of operations thing, but that is not the case. This does not work the way I thought it would work.
I am curious to understand how this is supposed to be done, and why this cannot be done.
//create and return an obj
var fishSticks = function(){
return {
first: function(){
document.getElementById('output').innerHTML="Success";
},
init: function(){
try{
first(); //err
this.first(); // also err
}catch(e){
document.getElementById('output').innerHTML=e.toString();
}
}()
}
}
//do function stuff and then return 'this'
var fishFillet = function(){
var first = function(){
document.getElementById('output2').innerHTML="Success";
}
var init = function(){
try{
first(); //err
this.first(); // also err
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}()
return this;
}
var test = new fishSticks();
var test2 = new fishFillet();
​
You need to understand two things:
1) JavaScript does not automatically insert this like Java does, so the first() call will only look through the lexical scope for a definition of first, it will nok look at the this object. Therefore the call to first() should work but this will be bound to something else than what you might expect inside first.
2) Local variables in a constructor do not become members of the constructed object.
In your second example, if you comment out the call in "init" to this.first() then you get the "Success" message.
The first version doesn't work because JavaScript simply does not allow for references to be made within an under-construction object to the object itself. There's just no way to do it.
The second one works (well the simple reference to "first" works) because "first" is declared as a local variable. Local variables are not properties of any object, and in particular they're not properties of the object allocated when the function is called with new. That's why this.first() doesn't work.
In the second one, you could make this.first() work by declaring things differently:
var fishFillet = function(){
this.first = function(){
document.getElementById('output2').innerHTML="Success";
}
var init = function(){
try{
this.first(); //will work
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}()
return this;
}
Also, for what it's worth, the weird anti-pattern of
var something = function() { ... }
is not as useful as
function something() { ... }
There's no reason to use the var declaration instead of the function declaration.
How about...
var fishFillet = function () {
var first = function () {
document.write( 'Success' );
};
var init = function () {
first();
};
init();
return {
first: first
};
};
And then:
var ff = fishFillet(); // calls init() which calls first()
ff.first(); // call first() manually
Live demo: http://jsfiddle.net/uaCnv/
So, first you define all your functions, next you manually invoke init, and last you return an object containing those functions which should be available through the resulting object (as methods).
Since you are using both as a constructor, format them as such:
function fishFillet(){
this.first = function(){
document.getElementById('output2').innerHTML="Success";
}
this.init = function(){
try{
this.first();
}catch(e){
document.getElementById('output2').innerHTML=e.toString();
}
}
}
var food = new fishFillet();
food.init();
The reason it wasn't working for you is b/c "first" is created as a local varaible, and is deleted after execultion. Init isn't being called until after the execution of the constructor has finished

Categories