For example if I have this code:
const text = "hello";
function func(){
return text;
}
func();
When I invoke the function func() is going to be generated a new execution context on the top of the global execution context and that's my problem, How the execution context of the function get access to the const text, has the execution context access directly to the data in the global execution context or attached to the function there is a closure so it has access to the data it needs?
Related
Why can we declare a const in a function and call the function multiple times without getting an error
Uncaught SyntaxError: Identifier 'newTab' has already been declared
It is creating a new const every time we call the function?
Code to illustrate :
function foo() {
const newTab = [123]
console.log(newTab);
}
Thanks if someone can enlighten me :)
Each function call will create a new block (Since the javascript is interpreted language and runs the code line by line so each function call will create a local execution context exactly where the function gets invoked) in memory (The last one will be removed in the garbage collection phase), so each instance of const newTab is separate from the previous one. It may seem it should throw an error due to the nature of defining the const element, but since the last one is removed and a new one is in the new memory block, so it will not throw any error.
NOTE: This article and this video may help you to learn more about local execution context.
function foo() {
const newTab = [123];
console.log(newTab);
}
foo();
foo();
foo();
Actually your code works fine, i can't see any errors, how do you call it? Every call const newTab will be created in local execution context where all variable isolated
The problem: I'm using nodejs vm to run a supplied script (trusted source but not in my control). The supplied script must call back to a function I provide in the context it receives.
Functions internal to the script work as expected. But when a function supplied in the context is called, it no longer has any variables from the context provided to the called script. This short program demonstrates the problem:
var vm = require('vm');
var sandbox={console:console, myfn:function() {
console.log("MYFN");
console.log("MYFN: a="+a);
}, a:42};
var ctx = new vm.createContext(sandbox);
vm.runInContext("function okfn() { console.log('OKFN'); console.log('OKFN: a='+a); } console.log('TEST'); console.log('TEST: a='+a);okfn(); myfn();", ctx, {filename:"TEST"});
When it runs, I expect to see output of:
TEST
TEST: a=42
OKFN
OKFN: a=42
MYFN
MYFN: a=42
but instead, the last console.log in myfn() produces a ReferenceError: a is not defined
Is there a way to retain the context passed to the script via runInContext(), when that script makes calls to a function provided in the context itself?
Functions are bound to the global scope in which they are defined. As myfn was defined in the main script file, it will use the globals of that file. In order to bind that function to another global context, it must be redefined/re-executed in that context:
var vm = require('vm');
function myfn() {
console.log("MYFN");
console.log("MYFN: a="+ a);
}
var sandbox={console:console, a:42};
var ctx = new vm.createContext(sandbox);
// Evaluates the source code of myfn in the new context
// This will ensure that myfn uses the global context of 'ctx'
vm.runInContext(myfn.toString(), ctx, {filename: "TEST"});
// Retained in the context
console.log(ctx.myfn);
// Use it
vm.runInContext("function okfn() { console.log('OKFN'); console.log('OKFN: a='+a); } console.log('TEST'); console.log('TEST: a='+a);okfn(); myfn();", ctx, {filename:"TEST"});
I am working on a JavaScript library while I need to load different modules accordingly, I use the callback to load different scripts:
Just add the main script in the page:
<script type="text/javascript" src="main.js"></script>
main.js:
(function () {
var actionForT2 = function (fun) {
fun && fun.apply(this);
}
var loadCallback = function (name, obj) {
if (name == "t2") {
actionForT2(obj);
}
}
window.__jsload = loadCallback;
var loadJs = function (js) {
var head = document.head || document.getElementsByTagName('head')[0];
var script = document.createElement("script");
script.setAttribute("src", js);
script.setAttribute("type", "text/javascript");
head.appendChild(script);
}
loadJs("js/t2.js");
})();
t2.js:
__jsload('t2', function () {
console.info("t2 loaded");
console.info(loadJs);
})
Now the t2.js will be loaded as expected. And I got the output:
t2 loaded
ReferenceError: loadJs is not defined
That's to say, the loadJs function is not accessed for the function defined in t2.js, then I wonder if I can change the Runtime context of the loaded function, for example, when the loaded function is being called:
fun && fun.apply(this);
Is it possible to make the call of fun under the context of current anonymous function, then all the defined variables, functions like the loadJs and etc can be accessed by fun without export them to the window?
Why I am interested in this kinds of solution is that I found google map use the callback, for example, when using google map v3, the following script will be loaded to the page:
https://maps.gstatic.com/maps-api-v3/api/js/18/3/main.js
Then another module map will be loaded:
https://maps.gstatic.com/cat_js/maps-api-v3/api/js/18/3/{map}.js
While when deep in to the codes, I found that the {map}.js can access the variables defined in the main.js. But I can not find the how the magic happen.
Is it possible to change function runtime scopeā¦I wonder if I can change the Runtime context of the loaded function, for example, when the loaded function is being called
No. Javascript is lexically scoped, so scope is entirely dependent on where functions are created in the code, not where they called from or how they are called
> fun && fun.apply(this);
That will only set one parameter of the function's execution context, its this parameter.
There is general confusion about the term context. In ECMA-262 (the standard for the language underpinning javascript) context is used exclusively in execution context, which is the entire environment inside a function and includes its this parameter.
The call and apply methods allow you to specify the value of a function's this, that's it, they have no other effect on the execution context (which is why this should never be called "context"). They do not have any effect on identifier resolution, which proceeds based on scope regardless of the value of this or where a function has been called from.
You might find the following article interseting: Identifier Resolution, Execution Contexts and scope chains.
You could make it global. Since you already have window.__jsload you could attach it there.
__jsload.loadJs = loadJs;
// // access like this :
// console.info(__jsload.loadJS);
Or you could pass it to your callback when you call it from __jsload.
__jsload('t2', function (loadJs) {
console.info("t2 loaded");
console.info(loadJs);
}
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?
I have several script blocks depend on each other. I need to perform them in one scope.
My attempt:
var scopeWrapper = {};
with(scopeWrapper) {
(function() {
this.run = function(code) {
eval(code);
};
}).call(scopeWrapper);
}
scopeWrapper.run('function test() { alert("passed"); }');
scopeWrapper.run('test();');
I get 'test is not defined' error. It seems that the code is executed in different scopes.
Why is this happening?
Edit: Bergi pointed out my original answer was wrong, he is correct. Since eval runs in its own scope and the function constructor still runs in function scope according to the spec this is not possible with either.
While I have done this sort of thing myself several times with node.js using the vm module where you get much finer grain of control over where your code executes, it seems browsers require a different approach.
The only way you can share variables in such a way is to do so in the global scope of JavaScript execution (possibly, in an iframe). One way you could do this is script tag injection.
function run(code){
var sc = document.createElement("script");
sc.setAttribute("type","text/javascript");
sc.innerHTML = code;
document.body.appendChild(sc);
}
run("var x = 5");
run("document.write(x)");
(here is this code in action)
As for the scope wrapper, instead of injecting them in the same frame inject them in another iframe. That will scope their window object to that iframe and will allow you to share context.
I humbly apologize for my previous answer, I misread the spec. I hope this answer helps you.
I'm leaving my previous answer here because I still believe it provides some insight into how eval and the Function constructor work.
When running code in non-strict mode eval runs in the current context of your page
After your function declaration is done, the scope it was declared in dies, and with it the function.
Consider using the Function constructor and then .calling it
In your case that would be something like:
var scopeWrapper = {};
scopeWrapper.run = function(code){
var functionToRun = new Function(code);
functionToRun.call(scopeWrapper);
}
scopeWrapper.run('this.test = function() { alert("passed"); }');
scopeWrapper.run("this.test()")
Here is a reference directly from the spec:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
Initialize the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
If this code is run in the node.js consider using the vm module. Also note that this approach is still not secure in the way it'll allow code you run to change your code.
test only exists in the scope of this.run and only at call time :
// global scope
(function(){
// local scope (equivalent of your "run" function scope)
eval('function f(){};');
console.log(f); // prints "function f(){}"
})();
console.log(f); // prints "ReferenceError: f is not defined"
Each call of run creates a new scope in which each code is evaluated separately.