How can I go about getting an instance of a script that is loaded in Java Script?
I'm using the function load("script.js") and I need to call a function on that script, but I need the instance in a variable so i can store it in a map.
I need something like var script = load("script.js")
Then I can call script.unload() // a function defined in script.js
When you load a script, it's executed and any bindings it creates in the environment are created. You cannot unload a script. If you know what all of its bindings and other effects are, you could clear them all (for instance, if loading Underscore, you could do _ = undefined to clear that binding), but it's unlikely that all of the effects of loading the script will be undone.
(This isn't a Nashorn thing, it's a JavaScript thing. There's one environment shared by all loaded scripts. ES2015's modules help organize that better, but there's still just one overall environment.)
Related
I'm looking through the excellent peepcode demo code from the backbone.js screencasts. In it, the backbone code is all enclosed in an anonymous function that is passed the jQuery object:
(function($) {
// Backbone code in here
})(jQuery);
In my own backbone code, I've just wrapped all my code in the jQuery DOM 'ready' event:
$(function(){
// Backbone code in here
});
What's the point/advantage of the first approach? Doing it this way creates an anonymous function that is then executed immediately with the jQuery object being passed as the function argument, effectively ensuring that $ is the jQuery object. Is this the only point - to guarantee that jQuery is bound to '$' or are there other reasons to do this?
The two blocks of code you have shown are dramatically different in when and why they execute. They are not exclusive of each other. They do not serve the same purpose.
JavaScript Modules
(function($) {
// Backbone code in here
})(jQuery);
This is a "JavaScript Module" pattern, implemented with an immediately invoking function.
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
The purpose of this code is to provide "modularity", privacy and encapsulation for your code.
The implementation of this is a function that is immediately invoked by the calling (jQuery) parenthesis. The purpose of passing jQuery in to the parenthesis is to provide local scoping to the global variable. This helps reduce the amount of overhead of looking up the $ variable, and allows better compression / optimization for minifiers in some cases.
Immediately invoking functions are executed, well, immediately. As soon as the function definition is complete, the function is executed.
jQuery's "DOMReady" function
This is an alias to jQuery's "DOMReady" function: http://api.jquery.com/ready/
$(function(){
// Backbone code in here
});
jQuery's "DOMReady" function executes when the DOM is ready to be manipulated by your JavaScript code.
Modules vs DOMReady In Backbone Code
It's bad form to define your Backbone code inside of jQuery's DOMReady function, and potentially damaging to your application performance. This function does not get called until the DOM has loaded and is ready to be manipulated. That means you're waiting until the browser has parsed the DOM at least once before you are defining your objects.
It's a better idea to define your Backbone objects outside of a DOMReady function. I, among many others, prefer to do this inside of a JavaScript Module pattern so that I can provide encapsulation and privacy for my code. I tend to use the "Revealing Module" pattern (see the first link above) to provide access to the bits that I need outside of my module.
By defining your objects outside of the DOMReady function, and providing some way to reference them, you are allowing the browser to get a head start on processing your JavaScript, potentially speeding up the user experience. It also makes the code more flexible as you can move things around without having to worry about creating more DOMREady functions when you do move things.
You're likely going to use a DOMReady function, still, even if you define your Backbone objects somewhere else. The reason is that many Backbone apps need to manipulate the DOM in some manner. To do this, you need to wait until the DOM is ready, therefore you need to use the DOMReady function to start your application after it has been defined.
You can find plenty of examples of this around the web, but here's a very basic implementation, using both a Module and the DOMReady function:
// Define "MyApp" as a revealing module
MyApp = (function(Backbone, $){
var View = Backbone.View.extend({
// do stuff here
});
return {
init: function(){
var view = new View();
$("#some-div").html(view.render().el);
}
};
})(Backbone, jQuery);
// Run "MyApp" in DOMReady
$(function(){
MyApp.init();
});
As a minor sidenote, sending in $ as an argument to an anonymous function makes $ local to that function which has a small positive performance implication if the $ function is called a lot. This is because javascript searches the local scope for variables first and then traverses down all the way to the window scope (where $ usually lives).
It ensures you can always use $ inside that closure even if $.noConflict() was used.
Without this closure you'd be supposed to use jQuery instead of $ the whole time.
It is to avoid a potential conflict of the $ variable.
If something else defines a variable named $, your plugin may use the wrong definition
Refer to http://docs.jquery.com/Plugins/Authoring#Getting_Started for more details
Use both.
The self invoking function in which you pass in jQuery to prevent library conflicts, and to just make sure jQuery is available as you would expect with $.
And the .ready() shortcut method as required to run javascript only after DOM has loaded:
(function($) {
$(function(){
//add code here that needs to wait for page to be loaded
});
//and rest of code here
})(jQuery);
I would like to be able to expose a function-scoped variable that is part of a website's (not mine) javascript.
Here's a simplified example:
function addHooks(e, t, n, i) {
var props = { // Would like to expose this
update: function() { ... },
remove: function() { ... },
find: function() { ... },
};
...
}
In the above example, I would like to expose the props variable so that my webextension can then access this to modify the behavior of the website. Please note, that the website that serves this JS file isn't under my control and thus, I cannot simply modify the source file to export this variable. However, I'm fully open to modifying the final code that the browser runs (I just don't know how to). The javascript file containing the addHooks function appears to be added dynamically via XHR.
Although I have no idea on how to accomplish this programmatically, I have had some success setting a breakpoint and then issuing window.siteController = props in the browser's developer console. Unfortunately, manual user-intervention is not something I can package and distribute.
One method that I have been toying with is the idea of making an AJAX request for the JS file, modifying its script contents and appending it to the page.
Is there a canonical, programmatic way in which a function-scoped variable can be exposed?
if it's the props object variable that you want exposed, you could return he entire object from the functin, like in closures.
Or , you could, simple declare a the enclosing function as a constructor function, set props as this.props and then, invoke the same function elsewhere, using the new keyword.
Let me know if this helps
EDIT:
Thanks to Makyen who pointed out my error.
Apparently you might be able to do it if you re-define the function in a page script and insert it as a script element. (See the comment for two references).
Just be sure that your <script> element is inserted after the original definition and then you can either return props at the end of the function, or use this.props and use the function either as a constructor or using thecallfunction, passing an object that will get the props property added.
Old answer ( __ wrong__ apparently)
Short answer: you can't.
Full explanation: if you can't change the code of the function and you can only use the function (call it) you cannot alter its variable's scope (and good thing you can't, too. There's a reason why the variable was limited to its current scope).
And you can't change the website's JavaScript. As a Firefox or Chrome extension (and possibly the same fires to other browsers I'm not sure) you can't even access the function (let alone it's inner variables). I mean to say you can't even call it from the extension of it's in the website's code.
content scripts cannot see JavaScript variables defined by page scripts
More info and source
Firefox/Chrome runs your JavaScript code in a parallel environment, isolated from the website's environment. This is of course for security reasons. It also helps for you to be able to develop without knowing the exact inner workings of each website your code might be injected into, as you don't need to bother regarding naming conflicts (which would be impossible if it weren't for the separation environments).
The only thing you do have access to is the DOM, through which you can gather information, alter the website and even talk with the website in some cases (the website should follow a protocol you decide upon as well).
I am building a framework in which I have merged all JavaScript files into one file (minify).
Example:
function A() {} function B() {}
Through minified file i want to load function asynchronous and remove from HTML when its work is done.
Example: load function A when it is required but not function B.
I have seen one framework Require.js but in that it loads JavaScript file asynchronous based on requirement.
Is there any framework which loads JavaScript functions on demand which are defined in same js file.
The downside to concatenation is you get less fine-grained control over what you are including on a given page load. The solution to this is, rather than creating one concatenated file, create layers of functionality that can be included a little more modularly. Thus you don't need all your JS on a page that may only use a few specific functions. This can actually be a win in speed as well, since having just one JS file might not take advantage of the browsers 6 concurrent connections. Moreover, once SPDY is fully adopted, one large file will actually be less performant than more smaller ones (since connections can be reused). Minification will still be important, however.
All that said, it seems you are asking for something a little difficult to pull off. When a browser loads a script, it gets parsed and executed immediately. You can't load the file then... only load part of the file. By concatenating, you are restricting yourself to that large payload.
It is possible to delay execution by wrapping a script in a block comment, then accessing it from the script node and eval()ing it... but that doesn't seem like what you are asking. It can be a useful strategy, though, if you want to preload modules without locking the UI.
That's not how javascript works. When the function's source file is loaded, the function is available in memory. Since the language is interpreted, the functions that are defined would be loaded as soon as the source file was read by the browser.
Your best bet is to use Require.js or something similar if you want to have explicit dependency chains.
Looking at this JS code by David Fowler he wraps every "class" with an anonymous self executing method where he sends in jQuery and window. I get that this is a way for ensuring that $ and window are actually the global jQuery and winndow variables you'd expect them to be.
But isn't this a bit over-protective? Should you protect yourself from someone else changing the $ and window variable - is there actually code that does that, and if so why? Also, are there other advantages to wrapping everything like this?
If I remember correctly there are some other libraries than jQuery using the $.
The concept of using function context to create local / function scopes is all about to protect your own code. This especially makes sense if you expect your Javascript code to run in an environment where multiple other (maybe even unknown) scripts may get loaded.
So, if some other Javascript code which got load before your own, assigns window.$ with some other value (it might load the prototype framework for instance), then your code is already screwed if you try to access jQuery specific things.
Another point there is the "a--hole"-effect. Someone creates a line like
window.undefined = true;
...
Now, all your checks against undefined will pretty much fail. But by explicitly creating those variables/parameters and filling them with the values you expect, you can avoid all those problems.
(function(win, doc, $, undef) {
}(window, window.document, jQuery));
Generally most top-level JavaScript code should be wrapped in an IIFE, whether they pass in particular variables or not. This prevents variables created with var from polluting the global scope.
Another small benefit is minification: if you minify your code, making window into a "local variable" by passing it in as a parameter allows the minifier to rename it. This benefit goes away if you gzip, however, so it's not a real win.
Mostly people just pick a pattern for their IIFEs and stick with it, so in his case he's decided this is they way he writes his .js files and he does so uniformly. Having a uniform standard is valuable in and of itself.
To clarify, the code can be written in slightly longer form with
(function () {
var $ = jQuery;
var window = window;
// ...
}());
The code inside the anonymous function is the the function scope, so it prevent confliction with the global variables.
Imaging that if every jQuery plugin is not wrapped in a anonymous function, then there will be a global variables hell.
It's just to preserver integrity.
$ can be Prototype, so sending jQuery as argument will save your code if someone else add a library/variable which overwrite $.
About the second argument "window", I see it as a module you want to write on.
I am currently making a sort of web app and part of it works by dynamically loading and adding js scripts to the page. For this I am using JQuery's $.getScript() method which loads it in.
I have it set as cached.
With my program if a script already exists it is loaded again anyway, from what appears to be cache. What I am wondering though is how much would this effect the site and performance. Does a newly loaded script that has the same src as an existing one overwrite the previous one or is the new one added alongside the old one?
Further more as my site is an AJAX site its possible for several scripts from different pages to eventually be loaded up over time. Is there any browser restrictions on how many scripts one can have loaded?
It will affect site performance. Even if your script is cached on the client with expiration set the browser still needs to parse and execute newly included script. More than that, there's a very good chance you will run into javascript errors because you scripts will override variables already set by previous version. JavaScript parsing and executing is still a blocking operation in all browsers, so while your file is being processed your UI will lock up.
To answer second part of the question, as far as I know there's no limit of number of javascript files on a given page. I've seen pages with over 200 javascripts that didn't throw any exceptions.
I think Ilya has provided some great points to consider, and I think that answers your specific question. If what you're trying to accomplish, however, is rerunning a $(document).ready() handler, you could do:
(function(){
var myreadyfunction = function(){
$('#affected').toggleClass('blue');
};
$(document).ready(myreadyfunction);
$(document).ready(function(){
$('button').click(myreadyfunction);
});
})();
http://jsfiddle.net/Z97cm/
I've scoped it into an anonymous (function(){})(); to keep out of the global scope, so you might want to consider that if you need to access that function from outside that scope. But that gives you the general idea of what I was talking about.