So i was playing around with the concept of callbacks and i ran into a situation where I wanted to make sure i knew what i thought was happening, was actually happening.
function greet(callback) { // 'greet' function takes a callback
var greeting = "hi"; // 'greeting' variable is set in the scope
callback() // 'callback' function is invoked
}
greet(
function() {
console.log(greeting)
});
//Console logs nothing
Sorta weird, you'd expect the callback() to look in its next closest scope and figure out what greeting is. However, once i declare the greeting variable to the global execution context, greeting is logged in the console.
var greeting = 'hi' // 'greeting' variable is set in global context
function greet(callback) { // 'greet' function takes a callback
callback() // 'callback' function is invoked
}
greet(
function() {
console.log(greeting)
});
//Console logs: 'hi'
Is this because technically the callback() function that's logging the variable is actually defined in the global context and is simply being invoked inside of greet()? So it's not going to look inside of greet() first, like a normal function expression would, but right into the global context because that's where it's defined.
I just wanted to make sure i understand what's happening here and not some weird scope/block issue that i don't realize.
You're right - the function has access to the scope which it is defined in.
Your example is effectively this (moving your callback to its own function):
var greeting = 'hi';
function greet(callback) {
callback();
}
function logGreeting() {
console.log(greeting);
}
greet(logGreeting);
As you can see, logGreeting has access to greeting at this point because it is defined in the same (or higher) scope.
However, when we move greeting into the greet function:
function greet(callback) {
var greeting = 'hi';
callback();
}
function logGreeting() {
console.log(greeting);
}
greet(logGreeting);
greeting is no longer in the same or higher scope to logGreeting, it's in a different scope entirely, so logGreeting cannot look upwards through the scope in order to find greeting.
The scope which logGreeting has access to is the scope in which it is defined rather than which it is called, so it doesn't evaluate the scope when it's called with callback() like you mentioned in your question.
That's an example of lexical scope which is how JavaScript handles scope, as opposed to dynamic scope. In a nutshell lexical scope determines the context based on where things are defined and dynamic is based on where it's called.
For your first example to work like you thought would be dynamic scope.
You can read a bit more detail here in the "Lexical scope vs. dynamic scope" section.
https://en.wikipedia.org/wiki/Scope_(computer_science)
Javascript uses Static Scoping
TLDR; Variables are resolved according to their location in the source code.
greeting is only defined with the closure of the greet function. Only code defined within the greet function closure can see the greeting variable.
function greet(callback) { // 'greet' function takes a callback
var greeting = "hi"; // 'greeting' variable is set in the scope
callback() // 'callback' function is invoked
}
greet(function() {
console.log(greeting)
});
Here though, greeting is defined in a scope accessible by the nested closures of the greet function and the anonymous function used in the console.log call.
var greeting = 'hi' // 'greeting' variable is set in global context
function greet(callback) { // 'greet' function takes a callback
callback() // 'callback' function is invoked
}
greet(function() {
console.log(greeting)
});
NOTE: this keyword has a different set of rules.
Related
I'm trying to find a way of accessing a scoped function - take this code for example:
(function() {
one();
console.log('IIFE END');
function scoped() {
console.log('scoped');
}
scoped();
})();
function one() {
console.log('one');
}
scoped(); // error
How can I access the call to the function to prevent the error? Using the return keyword fails
What you're doing is more or less saying "I locked the keys in the car. How do I access the keys." You can't. You locked them up in a way that makes them impossible to retrieve (well, unless you break the window, but you get my point). If you could simply open the door to get the keys there wouldn't be any point to locking the car.
That "scoped" function is the keys locked in the car.
What you want to do, is declare "scoped" outside of that anonymous function (before it, actually). Now, you can call it from within your anon function and outside of it.
You could either:
let scope;
...in anon function...
{
scope = { ...some function };
}
or
const scope = { some function }
... in anon function
scope();
Either way, outside of the anon function, you can now call "scope ()".
I guess I would add, that in JS, it's almost always possible to grab something that isn't intended to be grabbed. You could probably work with a top-level "this", or something, or just name your anon function (so essentially creating a named object that is of type function) and accessing the members of it by bracket notation. You would probably have to remove the immediate return though and call it in another line of code (after the function declaration).
But that's all hackery voodoo. You're breaking the window. In the day to day, what you say you want to do here is by design verboten.
Depending on how flexible you are, you can do this:
const scoped = (function() {
one();
console.log('IIFE END');
function scoped() {
console.log('scoped');
}
scoped();
return scoped;
})();
function one() {
console.log('one');
}
scoped(); // NO error :)
Or if your function's return is reserved for something else, just do this:
var scoped; // If you don't want this pre-declaration, you can just use a global scope instead (e.g. window.scoped)
(function() {
one();
console.log('IIFE END');
scoped = function() {
console.log('scoped');
}
scoped();
})();
function one() {
console.log('one');
}
scoped(); // NO error :)
I am trying to understand how an anonymous function inside of a callback function is invoked.
For example:
const callbackExample = function(param, callback) {
console.log('This is an example of a callback function');
callback(param);
};
callbackExample('What', function(param) {
console.log(param);
})
My question is how does a anonymous function get invoked? If I substitute the callback to equal the anonymous function below.
Is the callback being substituted for the anonymous function.
Does the callback === function(param) { console.log(param) }
What I mean is I cannot invoke the function like this.
function() { console.log('Not') } ();
There are only three ways to declare and invoke a function.
assign a anonymous function a name: function expression
give a function a name: function declaration
Immediate Invocation function express
My theory is when not using a function express or function declaration for the callback function then Javascript parses the code and detects a anonymous function and uses iife to invoke the anonymous function.
I cannot find anything on the internet nor an api that describes what is happening behind the scenes, so I ask can somebody explain this to me.
IIFE (immeadiately invoked function expression) is just a name coined by the community for this specific construct:
(function() { /*...*/ })()
It's a function expression, that is directly followed by a function call to that function. That's it. You don't have an IIFE in your code.
My question is how does a anonymous function get invoked?
Functions get invoked using a reference to them, their name is just a debugging feature (and it's a reference to the function itself inside the functions body). Functions don't need a name:
let test = function /*irrelevant*/ () { };
let test2 = test;
test(); test2();
If you use a function declaration, the name is not only used as the function name, but also as the name of a variable that references the function:
function named() { }
is barely equal to (let's ignore "hoisting" here):
var named = function named() { }
If you invoke a function using a function expression as one of it's arguments:
function called(reference) { }
called(function irrelevant() { })
then the function expression gets evaluated, a function gets created, and a reference to it gets passed to the called function as an argument, which can then be accessed using the reference variable.
So i was playing around with the concept of callbacks and i ran into a situation where I wanted to make sure i knew what i thought was happening, was actually happening.
function greet(callback) { // 'greet' function takes a callback
var greeting = "hi"; // 'greeting' variable is set in the scope
callback() // 'callback' function is invoked
}
greet(
function() {
console.log(greeting)
});
//Console logs nothing
Sorta weird, you'd expect the callback() to look in its next closest scope and figure out what greeting is. However, once i declare the greeting variable to the global execution context, greeting is logged in the console.
var greeting = 'hi' // 'greeting' variable is set in global context
function greet(callback) { // 'greet' function takes a callback
callback() // 'callback' function is invoked
}
greet(
function() {
console.log(greeting)
});
//Console logs: 'hi'
Is this because technically the callback() function that's logging the variable is actually defined in the global context and is simply being invoked inside of greet()? So it's not going to look inside of greet() first, like a normal function expression would, but right into the global context because that's where it's defined.
I just wanted to make sure i understand what's happening here and not some weird scope/block issue that i don't realize.
You're right - the function has access to the scope which it is defined in.
Your example is effectively this (moving your callback to its own function):
var greeting = 'hi';
function greet(callback) {
callback();
}
function logGreeting() {
console.log(greeting);
}
greet(logGreeting);
As you can see, logGreeting has access to greeting at this point because it is defined in the same (or higher) scope.
However, when we move greeting into the greet function:
function greet(callback) {
var greeting = 'hi';
callback();
}
function logGreeting() {
console.log(greeting);
}
greet(logGreeting);
greeting is no longer in the same or higher scope to logGreeting, it's in a different scope entirely, so logGreeting cannot look upwards through the scope in order to find greeting.
The scope which logGreeting has access to is the scope in which it is defined rather than which it is called, so it doesn't evaluate the scope when it's called with callback() like you mentioned in your question.
That's an example of lexical scope which is how JavaScript handles scope, as opposed to dynamic scope. In a nutshell lexical scope determines the context based on where things are defined and dynamic is based on where it's called.
For your first example to work like you thought would be dynamic scope.
You can read a bit more detail here in the "Lexical scope vs. dynamic scope" section.
https://en.wikipedia.org/wiki/Scope_(computer_science)
Javascript uses Static Scoping
TLDR; Variables are resolved according to their location in the source code.
greeting is only defined with the closure of the greet function. Only code defined within the greet function closure can see the greeting variable.
function greet(callback) { // 'greet' function takes a callback
var greeting = "hi"; // 'greeting' variable is set in the scope
callback() // 'callback' function is invoked
}
greet(function() {
console.log(greeting)
});
Here though, greeting is defined in a scope accessible by the nested closures of the greet function and the anonymous function used in the console.log call.
var greeting = 'hi' // 'greeting' variable is set in global context
function greet(callback) { // 'greet' function takes a callback
callback() // 'callback' function is invoked
}
greet(function() {
console.log(greeting)
});
NOTE: this keyword has a different set of rules.
var user = {
firstName: "Alex",
sayHi: function() {
alert(this.firstName);
}
};
setTimeout(function() {
user.sayHi(); // Alex
}, 1000);
They say that the user gets from the closure.
Do I understand correctly that the method user.sayHi wrapped in a function that is declared in the global context, and that has access to the user object? This forms a closure?
user.sayHi() is wrapped in an anonymous function which is in the global scope`. This anonymous function creates a closure, though the closure is of no consequence in this case. Since the anonymous function is in the global scope, and the user in the global scope, the anonymous function has access to the user object.
I want to create a closure dynamically. See code below for explanation.
function myFunction(){
parentScopedVar(); //Would like to be able to call without using 'this'.
}
function myDynamicFunc(dynamicClosure){
//What do I need to do here to dynamically create
//a var called 'parentScopedVar' that can be referenced from myFunction?
myFunction.call(self);
}
myDynamicFunc(
{
parentScopedVar : function() { alert('Hello World'); }
});
Javascript uses lexical scope (based on where the code is declared), not dynamic scope.
If you are determined to try to do something that the language doesn't really encourage, you can force a string of code to be evaluated in your current execution context using eval(string of code here). In fact, you can do all sorts of odd things with eval(), but I'd much rather write code in a way that leverages the strengths of Javascript than to use a coding style that goes against the main design theme of the language (that's my opinion).
It's not entirely clear to me what problem you're trying to solve, but you can just pass a function as an argument and then call it via the argument from the called function.
// declare your function that takes a function reference an argument
function myFunction(callback) {
// call the function that was passed
callback();
}
function myDynamicFunc(){
// declare a local function
function myAlert() {
alert('Hello World');
}
// call your other function and pass it any function reference
myFunction(myAlert);
}
This will not pass an entire execution context. To do that, you'd have to package up the context in an object and pass a reference to the object, then dereference the properties from the object. That is typically how you pass an environment in JS.
You can use locally declared functions to provide access to parent scope from a callback (again lexical scope):
// declare your function that takes a function reference an argument
function doSomething(callback) {
// call the function that was passed
callback();
}
function myFunc() {
var myLocal1 = "Hello";
var myLocal2 = "World";
function callback() {
// when this is called, it has access to the variables of the parent scope
alert(myLocal1 + " " + myLocal2);
}
doSomething(myFunc);
}
You can even use it as a lasting closure:
// declare your function that takes a function reference an argument
function doSomething(callback) {
// call the function that was passed
callback();
}
function myFunc() {
var myLocal1 = "Hello";
var myLocal2 = "World";
function callback() {
// when this is called, it has access to the variables of the parent scope
// which are still alive in this closure even though myFunc has finished
// executing 10 minutes ago
alert(myLocal1 + " " + myLocal2);
}
// call the callback function 10 minutes from now,
// long after myFunc has finished executing
setTimeout(callback, 10 * 60 * 1000);
}
Here are some reference articles on lexical and dynamic scope in Javascript:
Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
Are variables statically or dynamically "scoped" in javascript?
What is lexical scope?