How to prevent JS code from being parsed/evaluated by compiler? - javascript

I have a module with a lot of JS code in it. Module is created like so:
(function (root, factory) {
// root === window
root.MyModuleName = factory();
})(this, function () {
'use strict';
var MyModuleName = function() {
// A lot of code here that I don't want to be parsed or evaluated
// until MyModuleName constructor is executed.
//
// For example:
// var a = { d: 123 };
// var b = function() { return 45; };
// this.someMethod = function() { b() + a.d };
// ...
};
return MyModuleName;
});
All methods & properties are inside MyModuleName closure and (I thought) they should be parsed only after MyModuleName() is executed.
After user clicks on some button I create an instance of MyModuleName and execute some method:
someButton.onclick = function() {
// I want compiler to parse and evaluate JS code only here
var myModule = new MyModuleName();
console.log(myModule.someMethod());
};
Even though MyModuleName constructor is executed() only after click, code inside it is evaluated when JS file loads (I can see it in Chrome dev tools timeline).
How to make sure compiler evaluates code only after click, not after JS file loads?

You can't. The JS engine has to evaluate the code to create the function before it can assign the function anywhere.

Related

Understanding function self-referencing mechanics

I had (approximately) this situation: I was trying to declare func1() function and add couple of static methods to it in one go by running it through _init() function providing a hash with properties to attach to it, not being aware of that it didn't remain declared in current scope after _init() function did it's job. It just got temporarily defined, and gc-ed after _init() run (as far as I'm aware). Here's the code sample:
//
// #_init
// merge src{} into target{}, overwriting
//
var _init = (function (target, src) {
var _ = this;
if (_.isobj(target))
_.keys(src).forEach(_.pass, {
src : src,
target : target
});
return target;
}).bind({
isobj : (function (node) {
return node === this(node);
}).bind(Object),
keys : (function (node) {
return this.keys(this(node));
}).bind(Object),
pass : function (field) {
this.target[field] = this.src[field];
}
});
, and I was hoping to 'batch-init' it here adding static methods at the same time:
_init(function func1 (e) {
var _api = func1.pop(arguments);
var node = this;
// and stuff...
}, {
pop: Function.prototype.call.bind(Array.prototype.pop),
// ...etc
});
When trying to reference it later I got error:
x = func1();
// ReferenceError: func1 is not defined
// x = func1()
Assigning the output of _init() to var func2 does the job, I can reference and use the function. Thing that confuses me is when console.log()-ing the func2 logs 'func1()', but trying to refernce func1 directly throws ReferenceError:
//
// #func2
//
var func2 = _init(function func1 () {
return func1.pop(arguments);
}, {
pop: Function.prototype.call.bind(Array.prototype.pop)
});
console.log(typeof func2, func2, func2(1,2,3));
// function func1() 3
console.log(func1(1,2,3));
// ReferenceError: func1 is not defined
// console.log(func1(1,2,3));
//
Could someone explain to me why the func1 reference didn't get created, but (strangely) was availble to func2 (it obviously was able to use it...)?
Could someone explain to me why the func1 reference didn't get created, but (strangely) was availble to func2 (it obviously was able to use it...)?
That's just how named function expressions work. Their name (func1) is available inside the function body as an identifier, but not outside. What happens to the result of the expression (passing the created function to _init, then assigning it to func2) is a different and completely unrelated thing.
I was trying to declare func1() function and add couple of static methods to it
You shouldn't, unless those static methods are really supposed to be public and not just simple helper methods. Use the revealing XY pattern (IEFE) and just get your utility functions in the closure scope:
var _init = (function() {
function isobj(node) {
return node === Object(node);
}
function pass(field) {
this.target[field] = this.src[field];
}
var keys = Object.keys;
return function (target, src) {
if (isobj(target))
keys(src).forEach(_.pass, {
src : src,
target : target
});
return target;
};
});
var func2 = (function() {
var pop = Function.prototype.call.bind(Array.prototype.pop);
return function func1 () {
return pop(arguments);
};
});

Call function so this not needed

I have an application that uses the v8 javascript engine, and within that I add functions to namespace objects that execute lines of code from the database. The contents of these functions need to not have this added before every function call. Following is some example code of my problem
var obj = {};
obj.method = function(a) { return a; }
obj.executor = function() { return method(5); }
obj.executor()
ReferenceError: method is not defined
var caller = function() { return method(5); }
caller.call(obj)
ReferenceError: method is not defined
As you can see, neither way allows me to call method without first adding this. Is there some way of executing a function so that it's context is set in such a way that this does not need to be added?
EDIT
This did work in a previous version of the v8 engine, but it seems the most recent one is not allowing it now.
"The client's write rules which are the strings loaded from the database, and it was a requirement (who knows why) that they only need to write the function names and the application sorts out the scoping."
If you're not running in strict mode, you can use a with statement.
var obj = {};
obj.method = function(a) { return a; };
obj.executor = function() {
with (this) {
return method(5);
}
};
obj.executor();
var caller = function() {
with (this) {
return method(5);
}
};
caller.call(obj);
Not saying this is a great solution, but it'll work if those are the requirements given.
I don't know your other requirements, but you can achieve this via a closure as well.
var obj = {};
(function() {
var method = obj.method = function(a) { return a; };
obj.executor = function() {
return method(5);
};
}();
obj.executor();

Isolate this referance in javascript

I am trying to work out a style of javascript modular coding.
Currently I have a "mount variable" declared in a global.js that is linked to all the pages in a web application. It contains global site stuff, like javascript language switching:
var app = window.app || {};
app.Lang = (function ($) {
"use strict";
var init = function () {
...
};
};
return { init: init };
})($);
app.GlobalLogic = (function ($) {
"use strict";
var ready = $(function () {
app.Lang.init();
});
}($));
As you can see, I am using immediately executed functions to initialize the logic for a loaded code in the file.
Now I am trying to write an isolated javascript file that can have a similar variable names with other files loaded on the same page. Here is an example:
app.GalleriesAdminLogic = (function ($) {
"use strict";
var MountDivID = "#galleries-management-view";
...
var constructorFunction = function () {
var initialDataObject = $.parseJSON($(MountDivID + " #initial-galleries-list").val());
...
}($));
Notice the variable MountDivID. If there was a similar js file loaded at the same time, that would also contain a definition for variable named MountDivID, would it create a resolution conflict? What is the correct pattern to address a same name variables only from local function?
Variables declared inside functions are local to that function. They are not visible to other functions, and they "hide" variables with the same name declared outside the function.

Strict Mode Scope Error on SetInterval call

Below is an pseudo example of what I am trying to doing
In non strict mode this works, but in strict mode I get a not defined error when the setInterval fires. This script is called from another jquery script as a plugin which then makes the call to the init section.
From reading here it appears to be a global scope / context problem but I don't know how to proceed
(function($, window, document) {
'use strict'; // remove and things work
var opts,test;
test = function(options) {
opts = $.extend(test.prototype.opts, test.prototype.defaults, options);
};
test.prototype.Save = function () {
console.log('hi');
};
test.prototype.defaults = {
_interval_id: null
};
test.prototype.opts = {};
$.bla.plugins.foobar = function() {
var base = this,
bar;
base.init = function() {
bar = new test();
opts = test.prototype.opts;
bar.Save(); // works
opts._interval_id = setInterval('bar.Save();', 10000); // called but bar is not defined
};
};
})(jQuery, window, document);
When a string is interpreted by setInterval, it's in the global scope, not the scope of the function that called it. Pass the actual function to call, not a string:
setInterval(bar.Save, 10000);
And if you allow bar or bar.Save to be modified, and you want the change to be picked up automatically, you should pass a function that re-evaluates it each time:
setInterval(function() { bar.Save(); }, 10000);

Javascript : Best way to declare functions to be used globally?

My javascript file is getting pretty big (3000+ lines) and I'm getting confused as to how to layout my file and delare functions so that they can called anywhere in the file.
To summarise my JS file looks a little like this at the moment:
//ALL GLOBAL VARIABLES FIRST DECLARED HERE
var var1 , var2 ,var3
$(document).ready(function(){
//JQUERY STUFF
});
//ALL FUNCTIONS THAT NEED TO BE GLOBAL DECLARED HERE
function myFunction(){
//do some stuff here
}
I am running into problems with this as some functions I call in places don't seem to be declared at the time of calling or aren't available globaly. It's all very confusing now!
Could someone suggest the best way to layout a big js/jquery file with certain JS Functions, Objects and Variables available to be referenced anywhere in the file.
UPDATE:
So to simplify it this correct (see my comments)?
window.MainModule = (function($, win, doc, undefined) {//WHAT IS BEING PASSED IN HERE?
var foo, bar, modules; //VARIABLES ACCESSIBLE ANYWHERE
var modules["foobar"] = (function() {//WHAT IS A MODULE? WHEN WOULD I USE A SEPERATE MODULE?
var someFunction = function() { ... };//DECLARING MY FUNCTIONS?
...
return {
init: someFunction,//IS THIS WHERE I USE/BIND MY FUNCTIONS TO EVENTS AND ELEMENTS?
...
};
}());
// hoist a variable into global scope
window.Global = someLocal;
return {
init: function() {//FUNCTION TO INIT ALL MODULES?
for (var key in modules) {
modules[key].init();
}
}
};
}(jQuery, this, document));
The modules section isn't properly defined ... here's a slightly tidied up example.
window.MainModule = (function($, win, doc, undefined) {
var modules = {};
// -- Create as many modules as you need ...
modules["alerter"] = (function(){
var someFunction = function(){ alert('I alert first'); };
return {
init: someFunction
};
}());
modules["alerter2"] = (function(){
var someFunction = function(){ alert('I alert second'); };
return {
init: someFunction
};
}());
return {
init: function(){
for (var key in modules){
modules[key].init();
}
}
};
}(jQuery, this, document));
$(window.MainModule.init);
// We always use closures don't we?
window.MainModule = (function($, win, doc, undefined) {
var foo, bar, modules; // List of local variables.
var modules["foobar"] = (function() {
var someFunction = function() { ... };
...
return {
init: someFunction,
...
};
}());
// hoist a variable into global scope
window.Global = someLocal;
return {
init: function() {
for (var key in modules) {
modules[key].init();
}
}
};
}(jQuery, this, document));
// Let's kick off the MainModule on $.ready
// I recommend you do this in your `html` with page specific data.
$(window.MainModule.init);
[[Disclaimer]]: This is a pseudo-code module with some standard code excluded for brevity.
Anything declared with var x inside your main closure is available throughout the entire function. Of course it won't be set to what you expect it to be set unless you set it.
To control loading and flow split code into what's automatically executed in your self executing closure and what needs to manually inited by your controller with page/user specific parameters.
You can either declare them in Window scope:
window.variableName = myVariable;
or you can omit the var, which is the same as declaring something in window scope:
variableName = myVariable;

Categories

Resources