Callback Function Cannot Access Variable Within Parent Function'S Scope

Callback function cannot access variable within parent function's scope

You could use a closure over text for finding the element.

function removeText(text) {    array.splice(array.findIndex(findTextToDelete(text)), 1);}
function findTextToDelete(text) { return function (element) { return element === text; }}
var array = ['a', 'b', 'removeThis', 'c'];
removeText("removeThis");console.log(array)

can callback functions access parent function variables

You cannot access x like you want because it is outside the scope of the done function.

You need to pass x to the callback:

(function load(callback) 
{
return $.get("../somepage.aspx", {data:data}, function (response, status, xhr){
var x = 10;
if(typeof callback === 'function') { callback(x); }
}
})(done);

var done = function(x){
//do something with x here
alert(x);
}

I suspect this is what you want but to but I am taking a stab in the dark here seeing as how the code in the question has serious syntax problems (i.e. done is not a child of parent.)

How to access the correct `this` inside a callback

What you should know about this

this (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scopes like other variables (except for arrow functions, see below). Here are some examples:

function foo() {
console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

To learn more about this, have a look at the MDN documentation.



How to refer to the correct this

Use arrow functions

ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They don't have their own this binding. Instead, this is looked up in scope just like a normal variable. That means you don't have to call .bind. That's not the only special behavior they have, please refer to the MDN documentation for more information.

function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}

Don't use this

You actually don't want to access this in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self and that.

function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}

Since self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this value of the callback itself.

Explicitly set this of the callback - part 1

It might look like you have no control over the value of this because its value is set automatically, but that is actually not the case.

Every function has the method .bind [docs], which returns a new function with this bound to a value. The function has exactly the same behavior as the one you called .bind on, only that this was set by you. No matter how or when that function is called, this will always refer to the passed value.

function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}

In this case, we are binding the callback's this to the value of MyConstructor's this.

Note: When a binding context for jQuery, use jQuery.proxy [docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.

Set this of the callback - part 2

Some functions/methods which accept callbacks also accept a value to which the callback's this should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map [docs] is such a method. Its signature is:

array.map(callback[, thisArg])

The first argument is the callback and the second argument is the value this should refer to. Here is a contrived example:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

Note: Whether or not you can pass a value for this is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax method [docs] describes an option called context:

This object will be made the context of all Ajax-related callbacks.



Common problem: Using object methods as callbacks/event handlers

Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.

Consider the following example:

function Foo() {
this.data = 42,
document.body.onclick = this.method;
}

Foo.prototype.method = function() {
console.log(this.data);
};

The function this.method is assigned as click event handler, but if the document.body is clicked, the value logged will be undefined, because inside the event handler, this refers to the document.body, not the instance of Foo.

As already mentioned at the beginning, what this refers to depends on how the function is called, not how it is defined.

If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:

function method() {
console.log(this.data);
}


function Foo() {
this.data = 42,
document.body.onclick = this.method;
}

Foo.prototype.method = method;

The solution is the same as mentioned above: If available, use .bind to explicitly bind this to a specific value

document.body.onclick = this.method.bind(this);

or explicitly call the function as a "method" of the object, by using an anonymous function as callback / event handler and assign the object (this) to another variable:

var self = this;
document.body.onclick = function() {
self.method();
};

or use an arrow function:

document.body.onclick = () => this.method();

can ajax callback function see variables from parent function?

This is the JavaScript functional way to doing things. It's called closure: functions carry variable pointers from their current scope and from any other parent scope.
So it's not only good practice, but this is the pattern you should normally follow instead of pushing around parameter objects, etc.

Please notice that the "this" reference is special, it is never closured (as opposed to any other references) and thus always point the global object in anonymous functions.

In fact a developer needs some time to fully employ the power of the closure feature - this is a basic example you just written. In more advanced (and not solely async) scenarios closure helps you to create "fire and forget" behavior, or can provide you with "private" variables that are not accessible from client code (useful for library development). Closure also help you isolate your code so that it will not mess with the global scope.

1) Example: how to create protected variables with closures. You are able to acccess two methods that can access "protectedVariable" but you are not able to access it yourself - so the control is guaranteed.

function protectedScope(initData) {
var protectedVariable = initData;

return {
getter: function() { return protectedVariable; }
setter: function(v) { protectedVariable = v; }
}
}

var methods = protectedScope(10);

console.log(methods.getter());

2) isolation: the following code will not garbage the global scope even with "global" function definitions

var API = (function() {
var protectedVariable = 0;

var myNotGlobalFunction() {
return protectedVariable;
}

return myNotGlobalFunction;
})();

js: accessing scope of parent class

You set "this" to a variable in the parent function and then use it in the inner function.

var simpleClass = function () {         
this.status = "pending";
this.target = jqueryObject;

var parent = this;

this.updateStatus = function() {
this.jqueryObject.fadeOut("fast",function () {
parent.status = "complete"; //this needs to update the parent class
});
};
};

How to access variable from scope of parent function?

PHP has no concept of nested functions or scopes and it's terrible practice to nest functions. What happens is that PHP simply encounters a function declaration and creates a normal function second. If you try to call first again, PHP will again encounter a function declaration for second and crash, since the function second is already declared. Therefore, don't declare functions within functions.

As for passing values, either explicitly pass them as function parameters or, as you say, make a class if that makes sense.

Variable in parent scope not getting altered in anonymous function

This isn't a question about scope. It's about asynchronicity. Your anonymous function will update the session variable in the parent function; but because your anonymous function is asynchronous, it'll happen after generateSession has returned.

Instead, you'll need to modify generateSession to accept a callback, and execute the callback (passing the generated session), once it's completed;

function generateSession(session, cb){
opentok.createSession(function(error, sessionId){
if (error) {
throw new Error("Session creation failed.");
}

cb(sessionId);
});
}

generateSession(blahblahblah, function (session) {
// Access session here.
});

This is the exact same problem as for How do I return the response from an asynchronous call? (in that situation, it's the "success" callback that is asynchronous); there might be a more suitable duplicate, but I can't find one :(. It'll still be beneficial to read through it though.



Related Topics



Leave a reply



Submit