How to test when passing a variable into a javascript iffe - javascript

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);

Related

Can not bind 'this' to imported function - javascript

I am a beginner and I want to bind 'this' to helper-functions which I have imported from an other file (that I can use the variables, I created in the current lexical environment ).
But I have recognized, that I can bind these imported functions to any object - which I have created in the file, into I have imported them - but not to the current this.
node.js example:
https://github.com/sdeli/issues-with-this
// random-js-file.js
// this is the function I import in app.js
function logOutThis() {
console.log(this);
}
module.exports = logOutThis;
// --------------------------------
// app.js
const importedFn = require('./random-js-file.js');
// this is now the global of random-js-file.js
importedFn();
console.log('--------------------------------');
var monkey = {
chimp : 'chimp'
}
// this is now the object 'monkey'
var myfunction = importedFn.bind(monkey);
myfunction();
console.log('---------------------------------');
//this should be now the current this
var myfunction2 = importedFn.bind(this);
myfunction2();
// console.log displays '{}' and i can not refer to the variable in this lexical environment
So I dont understand why I can not bind 'this' into a function which I have imported but I can bind it to any object.
Thank you for your suggestions
There isn't a way to access the inner context of an imported module unless it explicitly exports the bits you want (or provides functions that expose them). Javascript this doesn't behave like Java this, and modules aren't classes.
There's some other cases, but basically, if the function you're currently is called someFunc and it was called with syntax a.someFunc(), then this will be the same as a. Here's a worked example:
const someObject = {
someFunc() {
//Here, "this" will be the same as someObject, because of the way we called it
someOtherFunction();
}
};
someObject.someFunc();
function someOtherFunc() {
//"this" is still someObject because it's inherited from the calling function which was not called using a method syntax
x.y(); //inside the y() function, "this" will now be x
}
Basically, IMHO, this is a broken mess in Javascript and is one of the key reasons I avoided using it whereever possible. That means I don't use classes, and I get everything I need from closures.

Isolating External Javascript from defining methods in the global (window) scope

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
}
});
});

Why wrap your angular controller code with anonymous function?

I've been seeing a number of people wrapping their controllers with:
function(){
//CODE
}();
What's the benefit / goal there?
That isn't really anything related directly to Angular, it is a JS pattern know as an Immediately Invoked Function Expression.
It is one of the most useful patterns in JavaScript, primarily because of:
Code Encapsulation
Since functions have closures in JS, we can use this pattern to create private data very easily:
var index = (function iife() {
var counter = 0; // <-- this is private, only available inside the closure of inner()
return function inner() {
return counter++;
};
})();
console.log(index()); // 0
console.log(index()); // 1
console.log(index()); // 2
console.log(index.counter) // undefined
We can also pass arguments into an IIFE, which allows us to control how we access the outside context of our IIFE. For example, to make sure $ is actually the jQuery object within your code:
(function ($) {
// here have access to the global jQuery as $ regardless of what window.$ is and
// how many libraries are trying to use $
})(jQuery);
By combining the two ideas above, IIFEs can also be used to implement the module pattern, which is the basis for how RequireJS and NodeJS separate code:
var myModule = (function iife(initData) {
// we can use initData here to initialize our module if necessary
var module = {};
// private vars ...
var privateVar1, privateVar2;
// private methods ...
function privateMethod() {
console.log('yeeey');
}
module.somePublicProperty = 1;
module.somePublicMethod = function() {
privateMethod();
};
return module;
})(/* pass initialization data */);
myModule.somePublicMethod(); // 'yeeey'
The reason you'll find a lot of JavaScript code wrapped in an anonymous function is to isolate it from other code on the page.
The following code will declare a variable called name on a global scope:
var name = "Hello World";
By using that code, any other script on the page attempting to use a variable called name could potentially get an unexpected value of "Hello World" because your script declared it as "Hello World".
By wrapping that code in an anonymous function, you keep the code from conflicting with other variables called name:
(function() {
var name = "Hello World";
})();
In the example above, name is now only available inside of the scope of the anonymous function. It is not global, and therefore cannot conflict with other code on the page.
By wrapping your Angular module in an anonymous function, you prevent your code from conflicting with other code.
Additionally, other people who may use your code won't have to worry about it changing their global scope.
IMHO it's not necessary, even superfluous, as most controllers are already functions:
'use strict';
angular.module('MyApp').controller('AboutController', ['$scope'
function ($scope) {
$scope.title = 'About Us';
}
]);
Everything #nem035 and #tcasey said is correct but it also have another side effect.
If you use tools like Grunt or Gulp it also allow you to have working dists to put in production.
If you do not use Immediate Invoke Pattern you'll most likely have minification problems like:
State X is already defined!
Unknown provider
. . .
I suggest you to wrap all your js modules with this pattern.
I hope I've been helpful.
There are many benefits of you immediately-invoked-function-expression short IIFE and it's best practice to use it. This way each of angular service or controller become isolate and you don't run into global variables as it can happened if you don't use IIFE.
read more about it

Global functions in javascript

I'm new to js and trying to understand global and private functions. I understand global and local variables. But if I have an html named test.html and a 2 js files named test1.js and test2.js. Now I include the test1.js and test2.js in test.html and call the functions written in test2.js inside test1.js and test.html.
The functions that I have written in test2.js are in this form
function abc(){...}
function pqr(){...} etc.
are these above functions global? If they are , how can I not make them global and still access them in test1.js and test.html?
As I have read global functions or global variables are bad right?
Everything in JS is bound to containing scope. Therefore, if you define a function directly in file, it will be bound to window object, i.e. it will be global.
To make it "private", you have to create an object, which will contain these functions. You are correct that littering global scope is bad, but you have to put something in global scope to be able to access it, JS libraries do the same and there is no other workaround. But think about what you put in global scope, a single object should be more than enough for your "library".
Example:
MyObject = {
abc: function(...) {...},
pqr: function(...) {...}
// other functions...
}
To call abc for somewhere, be it same file or another file:
MyObject.abc(...);
in test2.js you can write this to make the function global
window.abc = function(){...}
and then in test1.js yo can access it like this
window.parent.abc();
I hope it will help you
Anything defined in a file without any sort of wrapper will be bound to the window object. Anything bound to the window object is global.
Example:
//these are global variables
foo = 123;
var ABC = 'school';
//these are "private" variables
test = function(){
var foo = 123
}
(function(){
var ABC = 'school';
}).call(this);
Since global variables in every file will be part of the window object, you can access them between files. It is important when creating "private" variables you add var. This says override any global variables in the current "wrapper". If I have a global variable foo and I define it again in a function with var they will be separate.
var foo = 123;
(function(){
var foo = 987; //this foo is separate from the above foo
}).call(this);
If you do have a "wrapper" and you want to define a global function you can do it like this:
window.foo = 123;
(function(){
window.foo = 123;
}).call(this);
Both functions will do the same thing.
Personally, I prefer to put everything in a wrapper and only define global variables when I need them using window.
(function(){
//all code goes here
//define global variable
window.foo = 123;
})call(this);
var SomeName = function() {
var function1 = function (number) {
return number+1;
}
var anotherFunction = function (number) {
return number+2;
}
return {
function1: function (number) {
return function1(number);
},
function2: function (number) {
return anotherFunction(number);
}
}
}();
calling
console.log(SomeName.function1(1)); //logs 2
console.log(SomeName.function2(1)); //logs 3
A modern approach (2020) to using global data is by using a global object literal and define there all your needed logic.
const Website = {
foo () {
console.log('foo')
},
bar () {
console.log('bar')
}
}
document.addEventListener('DOMContentLoaded', () => {
Website.foo()
Website.bar()
})
If your code is more complex than a couple of lines you will need to separate your code into multiple files and with webpack you merge them together into one file.
import Foo from './js/Foo.js'
import Bar from './js/Bar.js'
// define here another object literal or setup document events.
// webpack will merge all this together into one file
<script src="js/webpack-merged.js"></script>
The reasons you don't want to import individual js files with html is described here. The jist of it is you will have poor performance, so you must bundle all your js.
If you don't understand why global variables are bad, then why are you trying to avoid them?
Global functions aren't necessarily bad. What's bad is state that anyone and anything and change.
In general since you're new to Javascript, it's fine to start out with global functions spread out across multiple javascript files that you include in your html file via script tags.
As you transition from beginner to intermediate, you will have to look into some "module" solution (I personally recommend RequireJS).
For now though, you can make do with a simpler module pattern:
var T1 = function() {
return < some module object >
})(); // notice the parenthesis
Google "Javascript module pattern".
Also see this answer.
vos fonctions ne sont pas global si vous faite l'erreur de les incorporé dans par exemple :
$( document ).ready(function() {
function myglobalfunction() { ... }
});
vous devez retirer
$( document ).ready(function() {
your functions are not global if you make the mistake of embedded them in for example:
$(document) .ready (function () {
function myglobalfunction () {
...
}
});
you must remove
$ (document) .ready (function () {

Accessing a variable within a JavaScript object constructed via a function

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)

Categories