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);
}
Related
I have a class that I use to load external resources via an XMLHttpRequest (this is for WebGL) so I'm loading models, shaders etc. My plan was to put a loading display up whilst it did all these requests and then when it's finally complete I want it to run a callback function from the original function that created it. However, I'm getting strange results when I try to run that call back (such as it has no access of any of the objects within the class that did the loading).
I can get around this problem by passing "this" into the loading class and then doing
self = this;
promise(self.callback());
but I'd much rather specify the function that I want it to callback to after its done the loading. Does anyone know if this can be done? My code looks like this:
Main Class
this.loadingClass = new LoadingClass(this.LoadingComplete, resources);
Main.prototype.LoadingComplete = function()
{
// Returns undefined if i specify the callback function instead of just "this"
console.log(this.loadingClass.anyOfTheMembersOfThisClass);
}
Loading Class
LoadingClass = function(callback, resources) {
..
Promise.all(resources).then(function(loadedResources)
{
..
callback();
});
}
When you pass the function object as
(this.LoadingComplete, resources)
the object to which it was bound, will not be passed. So, only the function object LoadingComplete is passed to LoadingClass and when it is invoked as
callback()
the this value will be undefined (in strict mode).
To fix this,
you need to bind the this object, like this
new LoadingClass(this.LoadingComplete.bind(this), resources)
if your environment supports ES2015's Arrow functions
new LoadingClass(() => this.LoadingComplete(), resources);
In both these cases, when the LoadingComplete is invoked from LoadingClass, the this will be retained.
You are detouching callback (read about "this") function from the root object, so of course it loses context. Specify bindingContext explicitly with Function.prototype.bind method:
this.loadingClass = new LoadingClass(this.LoadingComplete.bind(this), resources);
I read some JS module design patterns recently. I came across this small code snippet as below.
(function(window) {
var Module = {
data: "I'm happy now!"
};
window.Module = Module;
})(window);
Still not quite understand this code well, my questions are:
How to use/call this module outside this particluar JS file? Need I
assign a variable to this module? e.g. var module1 = (...)(...);
Could anyone explain what the window parameter here stands for?
Is it a good practice to have two/three such kind of modules in the
same file?
The main reason to create an anonymous function in this case is to prevent global object pollution. It's not really a module pattern.
The problem arise when you declare a variable. Without the function scope, the variable will be added to the global object (window). If you were to declare the variables within a function. It would add the variable to the function scope without polluting the global object window.
What happen is that a javascript file could add a variable named foo and on a different file use that variable named foo too. Unless you really want to have a variable shared by two javascript files, it would probably create conflicts and bug that are difficult to fix.
For example: a.js
var foo = "one"
b.js
var foo = "two"
c.js
alert(foo)
In this case, the alert box might show either "one" or "two", depending of the order in the javascript files are included.
But having this a.js:
(function () {
var foo = "one"
})()
b.js
(function () {
var foo = "two"
})()
c.js
(function () {
alert(foo)
})()
This would create an error as you cannot alert a non declared variable.
One way to detect undefined variables, is to make sure to execute the javascript code in strict mode.
To do that, add the string "use strict" at the top of the file or function.
function () {
"use strict"
...
}
Undeclared variable will raise errors and it should be possible to fix the code that way.
Also, if you forget to declare a variable with the var keyword, it might end up adding the variable to the global scope even if the code is scoped into a function. The only way to prevent global scope pollution is to run the code in strict mode.
In the code snippet that you provided, the module with name Module is explicitly added to the window object. You can access the window object from any place in javascript unless the window name is ghosted by an other variable.
Now, back to the modules. If you want to define modules, it can be done in many ways. As an exemple, you could create an object on the window object called modules. In this object, you'll insert your modules.
module.js
window.modules = {}
foo.js
(function (window) {
var module = {}
...
window.modules.foo = module
})(window)
This variant isn't super good as you have to manually add the module to the modules object. You have to manually modify the window object, and that can be subject to errors.
modules.js
window.modules = {}
function define(name, constructor) {
var module = {exports: {}}
constructor(module)
window.modules[name] = module.exports
}
function require(name) {
return window.modules[name]
}
foo.js
define("foo", function (module) {
module.exports.one = function () {
return 1
}
module.exports.plus = function (a, b) {
return a + b
}
})
bar.js
define("bar", function (module) {
module.exports = function () {
var foo = require("foo")
return foo.plus(foo.one(), foo.one())
}
})
This is a module definition that looks a bit like module defined with http://requirejs.org/. It is quite basic as it doesn't take into account module dependencies so if bar is loaded and used before foo. Then the require method won't be able to return the module.
Also, if you want to store modules without having them visible into the global scope, you can only define the require and define method on the window object and hide modules into an anonymous scope like this:
(function (window) {
var modules = {}
function define(name, constructor) {
var module = {exports: {}}
constructor(module)
modules[name] = module.exports
}
function require(name) {
return modules[name]
}
window.define = define
window.require = require
})(window)
This way, define and require are the only function that can give you access to modules. Other modules won't be able to modify other modules without requiring them first. This can be useful when using third parties script that could conflict with your module system.
In fact this is not a module, but a Self-Invoking Ananymous function or an Immediate function which gets an object in parameter and assign a Module property to it:
The page window is a parameter passed to this function.
So an object named Module containing a data property is assigned to window.
JavaScript Self-Invoking Functions:
A self-invoking expression is invoked (started) automatically, without being called.
Function expressions will execute automatically if the expression is
followed by ().
You cannot self-invoke a function declaration.
You have to add parentheses around the function to indicate that it is
a function expression
So As you can see Immediate Functions can't be called as its name states it will be immediately executed and by its self, no other function or scope can execute it.
For better reference take a look at :
Javascript Self Invoking Functions.
Self-Invoking Functions section in JavaScript Function Definitions.
And concerning your last question about its benefits and good practices as shown on the given Article reference:
Where to use self-executing functions?
One obvious situation is when you want to auto-run a function like I
showed in above example but that is trivial. If you are in a situation
where you want to run a piece of code repeatedly like updating
something in the database based on user interaction or fetching
records from database every 10 seconds or you want to load new stories
via ajax similar to how facebook does on its homepage or some other
similar situation, one would normally go for setInterval function
something like this:
setInterval(doStuff, 10000);
Above, doStuff function will get called every 10 seconds. That is the
normal approach most developers seem to go with. However, there is a
huge problem with that.
The setInterval will call doStuff function exactly at specified time of 10 seconds again and again irrespective
of whether doStuff function actually finished doing what it is
supposed to do. That is bad and will certainly get you into unexpected
results.
That is one example of where setInterval is "bad" and should be
avoided.
This is exactly where self-executing functions come in handy. We can
do the same task with the help of self-executing function along with
setTimeout like this:
function foo(){
// your other code here
setTimeout(foo, 10000);
}();
This code will also repeat itself again and again with one difference.
setTimeout will never get triggered unless doStuff is finished. A much
better approach than using setInterval in this situation.
Calling it from another file:
And if this function is on another file it will be called automatically if this file is included.
Why do we use Self-Invoking Functions in JavaScript?
And if you ask yourself why do we use these functions, self-invoked function are used to manage Variable Scope.
Take a look at the answer here for further information.
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.
If I want to give a JavaScript variable global scope I can easily do this:
var myVar;
function functionA() {
myVar = something;
}
Is there a similarly simple and clean way -- without creating an object -- to separate the "declaring" and the "defining" of a nested function? Something like:
function functionB; // declared with global scope
function functionA() {
functionB() { // nested, would have local scope if declared here
CODE;
}
}
I should clarify that I'm referring to the scope of the function name itself -- so that if it is in an iframe it can be called from a script in the parent document. (Nothing to do with scope of variables inside nested functions.)
You can create global variables and functions by creating instances on the window object:
function _A()
{
// scoped function
function localFunctionInTheScopeOf_A()
{
}
// global function
window.globalFunctionOutsideTheScopeOf_A = function ()
{
};
}
In your case, though, all you need to do is this:
var myFn; // global scope function declaration
function randomFn()
{
myFn = function () // global scope function definition
{
};
}
Note: It is never a good idea to clog up the global scope. If you can; I'd recommend that you re-think how your code works, and try to encapsulate your code.
Perhaps I'm misunderstanding the question, but it sounds like you want something like this:
var innerFunc;
function outerFunc() {
var foo = "bar";
innerFunc = function() {
alert(foo);
};
}
You cannot globalize variables/functions cross windows/iframes that way. Each window/iframe has it's own global scope and to target variables/functions in another window/iframe, you need explicit accessor code and conform to the same origin policy. Only variables/functions inside the windows/iframes global scope are accessible.
code in top window.
var iframe = document.getElementById('iframeId');
var iframeContext = iframe.contentWindow || iframe;
// this will only work if your iframe has completed loading
iframeContext.yourFunction();
You could also possibly define functions/variables in the top window instead and simply work in one scope by binding the stuff you need from the iframe through a closure. Again, assuming you meet the same origin policy. This will not work cross domain.
code in iframe.
var doc = document;
var context = this;
top.myFunction = function(){
// do stuff with doc and context.
}
It is also important to note, that you need to check if your iframe content and it's scripts are fully loaded. Your top page/window will inadvertidly be done and running before your iframe content is done, ergo variables/functions might not be declared yet.
As for exposing a private function, others have awnsered this, but copy/pasting for completeness.
var fnB;
var fnA = function(){
var msg = "hello nurse!";
fnB = function(){
alert(msg);
}
}
I have the habbit of declaring stand alone functions as variables (function expression) and only use function statements to signify constructors/pseudo-classes. It also avoids a few possible embarrasing mistakes.. In any case, fnB resides in the global scope of the iframe and is available to the top window.
Why exactly you want this beats me, seems it makes matters more complicated to debug or update a few months later.
You can kind of do what you want.
You can create a function that acts like a namespace for properties and methods, and then you could essentially call either...
functionB();
or
functionA.functionB();
There is an article on how to do it here:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/JavaScript-Name-Spacing/
In response to the update...
Is the iframe on the same domain as the parent site? You can't call JavaScript across the domain boundary, which may explain the problem.
I was reading the jQuery source and I was wondering why the entire source file was wrapped in an anonomous function.
(function(){
...
})();
Is this something which helps not to pollute the global namespace? Why is it there and how does it work?
It uses the function body to provide its own scope rather than introducing a large number of globals that could be accidentally changed by external code.
Eg.
(function (){
var someConstantValue = ...;
myCoolFunction = function(){ return someConstantValue * 5; }
})();
myCoolFunction();
If the function scope were not introduced it would be possible to accidentally change someConstantValue by introducing other code (or another library)
someConstantValue = someOtherValue; // this won't change the behaviour of myCoolFunction
You're right that it will prevent pollution of the global namespace.
All of the variables and functions that jQuery needs are created inside of that function, which keeps those functions and variables from bleeding out into the global namespace. If you look at this block of code:
var jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
};
it's actually connecting the jQuery initializer to the outside world by setting window.jQuery and window.$ to the initialization function. That's the only place where the variables inside the wrapper function are directly available outside of the wrapper.
Notice too that the whole function is wrapped like this (function,,,)() which will execute that function as soon as the file loads.