I need to include a reference to JavaScript written by a third party on my website. Sadly, the developers that wrote this script decided to define all of their functions globally. You know, like this:
function AwesomeStringHelper() {
// ...
}
function MyGreatFunction() {
// ...
}
When I reference this script using a <script> tag, both of those methods will be added to the window object.
Since I prefer to not pollute the global scope, is there a way that I can change the scope of an external script? Ideally I'd like to be able to refer to these methods similar to ExternalLibrary.MyGreatFunction(), etc. I am not able to modify the third party script as it is hosted externally, and it changes frequently.
In the first instance, try to edumacate the third party developers on how to correctly write their modules.
If that doesn't work, do:
var ExternalLibrary = ExternalLibrary || window;
at the top of your code.
You can then use ExternalLibrary.MyGreatFunction() throughout to refer to their functions (even though they remain visible in the global window scope), and then later once the third party devs have fixed their scope issues then at most you need a one line change to maintain compatibility (or no change at all, if they happen to use the same ExternalLibrary name as you do).
Alternatively, use two simple snippets of code either side of the <script> tag which remember the keys of the window object, then move the newly appeared keys into a new object (at the same time deleting them from window):
Pre-load:
var ExternalLibrary = { _current: Object.keys(window) };
Post-load:
Object.keys(window).forEach(function(key) {
if (ExternalLibrary._current.indexOf(key) < 0) {
ExternalLibrary[key] = window[key];
delete window[key];
}
});
delete ExternalLibrary._current;
I've used a similar approach in the past (before strict mode was common) to check for leaking global variables.
If your third-party module assigns to the window object directly (like window.myGlobal = someValue), and you are able to download the source code manually, you should be able to "wrap" the entire script in a function, where the window object has been overloaded:
function wrapModule(code) {
// create a "fake" window object that inherits from the global object
var fakeWindow = Object.create(window);
// create a function wrapping the code
// note that "window" is a parameter name in this function, shadowing
// the global object
var func = Function("window", code);
// call function
func.call(fakeWindow, fakeWindow);
// return fake window object
return fakeWindow;
}
// run code
const fakeWindow = wrapModule(`
var x = 0; // local variable (will not be exported)
y = 1; // global variable (will still be leaked)
window.z = 2; // assignment to window
this.w = 3; // assignment to this
`);
// check what variables are exposed
console.log('window.x', typeof x); // window.x undefined
console.log('window.y', typeof y); // window.y number
console.log('window.z', typeof z); // window.z undefined
console.log('window.w', typeof w); // window.w undefined
// check what variables are exposed in fakeWindow
console.log('fakeWindow.x', typeof fakeWindow.x); // fakeWindow.x undefined
console.log('fakeWindow.y', typeof fakeWindow.y); // fakeWindow.y number
console.log('fakeWindow.z', typeof fakeWindow.z); // fakeWindow.z number
console.log('fakeWindow.w', typeof fakeWindow.w); // fakeWindow.w number
Assuming you know the specific functions being defined, then after the script is loaded, would this not work?
const ThirdPartyLib = {AwesomeStringHelper, MyGreatFunction};
delete window.AwesomeStringHelper;
delete window.MyGreatFunction;
ThirdPartyLib.AwesomeStringHelper(haveFun);
You can wrap the entire script in a function and return an object with the "public" functions you want, it can be tedious and hard to maintain.
var myLib = function() {
//entire script
return {
functionA : functionA,
functionB : functionB,
//rest of functions
}
}
Or like this (inmediately invoked function)
(function(global) {
//entire script
myLib.functionA = functionA;
myLib.functionB = functionB;
//rest of fn
global.myLib = myLib;
})(window);
You could automate this using gulp, i'm not sure if there's a good plugin for this.
Not sure if jQuery is an option or if you care for it but I don't know how to write native JS AJAX calls so bear with me:
$(document).ready(function(){
$.ajax({
url: 'www.example.com/awesome_script.js', // get the contents of the external script
type: 'GET',
crossDomain: true,
dataType: 'html',
success: function(data){
// build our script tag and wrap the contents inside of a function call
var script = "<script>"
script+= "var callMe = function(call_func, var1, var2, var3){";
script+= data;
script+= "return typeof call_func === 'function' ? call_func(var1, var2, var3) : 'You trying to dynamically call a variable? idk how to do that.';";
script+= "};";
script+= "<\/script>";
// assuming this is legal then just append the custom script tag to the <body> :-)
$('body').append($(script)[0]);
// profit?
callMe('AwesomeStringHelper', 'some_var'); // this function accepts one parameter
callMe('MyGreatFunction'); // this function accepts no parameters
}
});
});
Related
How do I test the following iife javascript within Jasmine, if I want to mock the parameter y being passed in?
As the script files are referenced before the specs script, the y is immediately invoked before I can mock/spy on the y object.
I can create a dummy mock object on the spec runner html (or even another js file), but this then doesn't allow me to change the mock value on each test, as I cannot reference the variable checkObject, thus I cannot test a pass/fail scenario.
Am I missing something?
Thanks
file 1.js
var y = {
age:25
}
file 2.js
var helper = (function(checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}
})(y);
I see what you're trying to do. I think you should redesign your code. When a peace of code is not testable, it's a hint of a poor design.
An iife is basically used to create a new lexical scope to protect against polluting the global scope. In your case, you are using iife as an object factory. One solution could be to create a helper factory that takes a checkObject and return a helper object.
var helperFactory = {};
helperFactory.createHelper = function (checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}; // this is the helper object returned by the factory
}
Now you can test helperFactory and helper object are testable. To use the y variable, you can just do something like this :
var helper = helperFactory.createHelper(y);
remember, an iife is mainly used to hide variables (create new scope). If you want to hide the helperFactory, you have to use some mechanism to make this object visible in the global scope. you can for example attach the helperFactory to the window global object like this :
(function(){
window.helperFactory = {};
window.helperFactory.createHelper = function (checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}; // this is the helper object returned by the factory
}
})();
var helper = window.helperFactory.createHelper(y);
My previous colleague rewrite window.print method:
function print(data){
var window_print = window.open('', 'my div', 'height=768, width=1024');
window_print.document.write('<!DOCTYPE html><html><head><title>Печать</title></head><body>' + data + '</body></html>');
window_print.print();
window_print.close();
}
My intention was to use default behavior of that function: just print current page, and I added:
if(data) {....} else { window.print() }
And of course I received error: "too much recursion: window.print();"
My question is how to invoke default behavior window.print()?
Edit: Ok, it appears that print is an own property of window in some browsers, and isn't in others. Therefore, just cache the value of window.print:
var printWindow = window.print;
// define your new print function here
var print = function(data) { ... };
// then later:
printWindow.call(window);
NB: If you're doing all this in the global scope, then you'll need to define the new print using a function expression (var print = ...) rather than a function declaration (function print(data) { ... }) because function declarations get hoisted to the top of their scope, and therefore the print redefinition would happen before you had chance to cache it. If you're not doing it in the global scope, then it doesn't matter as the new print won't override window.print regardless of how it's defined.
Original:
Try:
Object.getPrototypeOf(window).print.call(window);
print doesn't appear to be an own property of window, meaning that the newly defined print merely shadows something further up the prototype chain. You can bypass this shadowing by moving up the prototype chain using Object.getPrototypeOf.
You'll also need to use call so that the print method receives the correct value for this.
You need to store the original print method as another property in the window, just before the definition of your own print().
EDIT: You also need to define the new print function specifically as window.print = function(){...} rather than function print(){...} in order to be able to access the original - see answers with nice links here and here. This won't have any impact on how you call the method.
window.originalPrint = window.print;
window.print = function(data)
{
if(data)
{
document.getElementById('foo').innerHTML = data;
}
else
{
window.originalPrint();
}
}
<div id="foo"></div>
<button onclick="window.print('hello')">print('hello')</button>
<button onclick="window.print()">print()</button>
My application is accessing a third party external JavaScript file that I cannot alter.
Within the file is an object defined similarly to as follows:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
}
})(window);
I need to be able access the object_1_var within the object, but I'm struggling to access it.
object_1.v // returns undefined
object_1.obj_1_func() // returns the value via console, but I need to assign it to a var.
I have tried extending the object using as follows: (Using jQuerys $.extend())
object_2 = (function(){
return {
obj_2_func: function() {
return object_1_var;
}
}
})(window);
$.extend(object_1, object_2);
var my_var = object_1.obj_2_func(); // returns 'Uncaught ReferenceError: object_1_var is not defined'
What can I do to be able to access object_1_var?
You will not be able to access the variable. It happens to be a private member. Private members of an object can be accessed only by its member functions.
Read this.
object_1_var is a lexically scoped local variable.
That means that it can't be accessed by extending object_1 outside of its original definition.
The only way it can be accessed is by adding functions within the original lexical scope in which it was declared:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
var_1: function(x) {
if (typeof x !== 'undefined') {
object_1_var = x;
} else {
return object_1_var;
}
}
}
})(window);
but since you can't modify object_1, you're out of luck, I'm afraid!
Make it public, like this:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
},
object_1_var: object_1_var
}
})(window);
EDIT
If unable to edit the javascript (such as in a third party library - sorry for omission) then you will not be able to have access to object_1_var as it's scope is local to the closure object_1.
What you are trying to accomplish is impossible in JS.
With the construction of object_1 the variable goes out of scope of that method. The reason why the logging function can access the variable is what we call 'a closure'.
Sadly, object_1_var isn't accessible in this example. The variable is defined as local to within that particular function - the only reason that the other functions can see it is because they are also defined within that function. This "closure scoping" is an interesting feature in JavaScript that you don't see very often elsewhere, but is the only real way of defining "private" variables in JavaScript Objects.
Hope that helps!
In a worst case scenario, in the past I've worked around this sort of issue by effectively overwriting the definition of an object that was previously defined elsewhere - mainly in Greasemonkey scripts - but I wouldn't condone this for production uses!
The trick here is to just copy the entire piece of script into your own. It's ugly as hell, but it might just work! (YMMV)
How do I properly communicate data betweens two scripts.
In this particular case I have an element in one script, but I want to append it in another script.
The simplest way I could do this is by using the global window object as go-between.
But globals are not best practice.
What is the correct way to pass this element to another script?
Both script are encapsulated in the module pattern.
Script 0 Create Element Point
var element = document.createElement( "div" );
element.innerHTML = response_text;
Script 1 Append Element Point
Vi.sta = {
// implemented in the boot loader - shared global space - appended child already - you don't have to append until now.
// Static data has already been composed.
// Pull off window and append or best practice
};
Both are encapsulated in the module pattern
(function(){
// the code here
})()
All JS scripts are run in the global scope. When the files are downloaded to the client, they are parsed in the global scope.
So if you've got
// File_1.js
var myObj = { colour : "blue" };
There's nothing stopping you from doing this:
// File_2.js
var otherObj = { colour : myObj.colour };
As long as File_1.js is loaded before File_2.js
If you are namespacing, ie: MYNAMESPACE = {}; and each file extends your namespace through modules (rather than "module pattern" referring to returning interfaces from immediately-invoking functions), then check for the module's existence.
If you ARE writing modules for a namespaced app, and you DO need access to variables from one module to another, then you should be providing an interface for that.
If you are SANDBOXING modules, then you need to provide them all a proxy similar to a service-locator or a "registry", or develop a messaging-system based on a mediator-pattern.
window.sharedSpace = {};
sharedSpace.sharedValue = 'foo bar';
That way you only have one global object instead of several.
Why don't you just pass the element to a function in the module?
var module1 = (function () {
return {
func1: function () {
// get your element
var someElement = ...;
// pass it to the other module
module2.func2(someElement);
}
};
}) ();
var module2 = (function () {
return {
func2: function (someElement) {
// do whatever you want with someElement
}
};
}) ();
I'm trying to write 'better' javascript.
Below is one pattern I've found, and am trying to adopt. However, I'm slightly confused about its use.
Say, for example, I've got a page called "Jobs". Any JS functionality on that page would be encapsulated in something like:
window.jobs = (function(jobs, $, undefined){
return {
addNew: function(){
// job-adding code
}
}
})(window.jobs|| {}, jQuery);
$(function(){
$('.add_job').on('click', function(event){
event.preventDefault();
window.jobs.addNew();
});
});
As you can probably deduct, all I've done is replaced all the code that would have sat inside the anonymous event-handler function, with a call to a function in my global jobs object. I'm not sure why that's a good thing, other than it's reduced the possibility of variable collisions and made the whole thing a bit neater, but that's good enough for me.
The - probably fairly obvious - question is: all my event-binding init-type stuff is still sitting outside my shiny new jobs object: where should it be? Inside the jobs object? Inside the return object inside the jobs object? Inside an init() function?
I'm just trying to get a sense of a stable, basic framework for putting simple functionality in. I'm not building JS apps, I'd just like to write code that's a little more robust and maintainable than it is currently. Any and all suggestions are warmly welcomed :)
You can break down your application in whatever number of modules / objects you like too.
For instance, you can have another object / module which caches and defines all your DOM nodes and another one, which just handles any event. So for instance:
(function ( win, doc, $, undef ) {
win.myApp = win.myApp || { };
var eventHandler = {
onJobClick: function( event ) {
event.preventDefault();
myApp.addNew();
}
};
var nodes = (function() {
var rootNode = $( '.myRootNode' ),
addJob = rootNode.find( '.add_job' );
return {
rootNode: rootNode,
addJob: addJob
};
}());
$(function() {
myApp.nodes.addJob.on( 'click', myApp.handler.onJobClick );
});
myApp.nodes = nodes;
myApp.handler = eventHandler;
}( this, this.document, jQuery ));
It doesn't really matter how you create singletons in this (module) pattern, either as literal, constructor, Object.create() or whatnot. It needs to fit your requirements.
But you should try to create as many specific modules/objects as necesarry. Of course, if makes even more sense to separate those singletons / modules / objects into multiple javascript files and load them on demand and before you can say knife, you're in the world of modular programming patterns, dealing with requireJS and AMD or CommonJS modules.
Encapsulation-wise, you're fine: you could even just declare addNew in the jQuery closure and you'd still avoid the global scope. I think what you're getting at is more of implementing something close to an MVC architecture.
Something I like to do is create an object that you instantiate with a DOM element and that takes care of its own bindings/provides methods to access its controls etc.
Example:
// (pretend we're inside a closure already)
var myObj = function(args){
this.el = args.el; // just a selector, e.g. #myId
this.html = args.html;
this.bindings = args.bindings || {};
}
myObj.prototype.appendTo = function(elem){
elem.innerHTML += this.html;
this.bindControls();
};
myObj.prototype.remove = function(){
$(this.el).remove(); // using jQuery
};
myObj.prototype.bindControls = function(){
for(var i in this.bindings){ // event#selector : function
var boundFunc = function(e){ return this.bindings[i].call(this,e); };
$(this.el).on(i,boundFunc);
}
};
The way you are doing it right now is exactly how I do it also, I typically create the window objects inside the anonymous function itself and then declare inside that (in this case: jClass = window.jClass).
(function (jClass, $, undefined) {
/// <param name="$" type="jQuery" />
var VERSION = '1.31';
UPDATED_DATE = '7/20/2012';
// Private Namespace Variables
var _self = jClass; // internal self-reference
jClass = window.jClass; // (fix for intellisense)
$ = jQuery; // save rights to jQuery (also fixes vsdoc Intellisense)
// I init my namespace from inside itself
$(function () {
jClass.init('branchName');
});
jClass.init = function(branch) {
this._branch = branch;
this._globalFunctionality({ globalDatePicker: true });
this._jQueryValidateAdditions();
//put GLOBAL IMAGES to preload in the array
this._preloadImages( [''] );
this._log('*******************************************************');
this._log('jClass Loaded Successfully :: v' + VERSION + ' :: Last Updated: ' + UPDATED_DATE);
this._log('*******************************************************\n');
};
jClass._log = function() {
//NOTE: Global Log (cross browser Console.log - for Testing purposes)
//ENDNOTE
try { console.log.apply(console, arguments); }
catch (e) {
try { opera.postError.apply(opera, arguments); }
catch (e) { /* IE Currently shut OFF : alert(Array.prototype.join.call(arguments, ' '));*/ }
}
};
}(window.jClass= window.jClass|| {}, jQuery));
The reason I leave them completely anonymous like this, is that let's say in another file I want to add much more functionality to this jClass. I simply create another:
(function jClass, $, undefined) {
jClass.newFunction = function (params) {
// new stuff here
};
}(window.jClass = window.jClass || {}, jQuery))
As you can see I prefer the object.object notation, but you can use object literals object : object, it's up to you!
Either way by leaving all of this separate, and encapsulated without actual page logic makes it easier to have this within a globalJS file and every page on your site able to use it. Such as the example below.
jClass._log('log this text for me');
You don't want to intertwine model logic with your business logic, so your on the right path separating the two, and allowing for your global namespace/class/etc to be more flexible!
You can find here a comprehensive study on module pattern here: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html It covers all the aspects of block-scoped module approach. However in practice you gonna have quite a number files encapsulating you code, so the question is how to combine them property. AMD... multiple HTTP requests produced by every module loading will rather harm your page response time. So you can go with CommonJS compiled to a single JavaScript file suitable for in-browser use. Take a look how easy it is http://dsheiko.github.io/cjsc/