I have JavaScript class which have huge functions which are very difficult to maintain.
The 2 public functions are called at start and then on click. I want to create private functions inside these public functions say break into into some private functions scope to these public methods.
var searchResultView;
var SearchResultView = function () {
me = this;
this.init = function () {
// huge code
}
this.Search = function () {
// huge code
}
}
jQuery(function () {
searchResultView = new SearchResultView();
searchResultView.init();
searchResultView.Search();
}
What will best way to achieve this. I tried to use below approach but i think this nested function will not work well.
var searchResultView;
function searchResultView() {
me = this;
this.init = function () {
var declareControls = function () {}
var addEvents = function () {}
var fillControls = function () {}
declareControls();
addEvents();
fillControls();
}
this.Search = function () {
var validateAndCreateCriteria = function () {
if (!validateAandGetLocation()) {
alert("invalid location");
return false;
}
if (!validateAandGetCategory()) {
alert("choose search type");
return false;
}
var validateAandGetLocation = function () {}
var validateAandGetCategory = function () {}
}
validateAndCreateCriteria();
}
}
jQuery(function () {
searchResultView = new searchResultView();
searchResultView.init();
});
If I understood correctly, you should have the functions something like this:
var foo = (function() {
var privateBar = function() { // private function
},
privatefooBar = function() { // private function
};
return {
publicFoo : function() { //public function
/* use privateBar and privatefooBar functions here */
}
};
})();
Later you can access publicFoo function by using
foo.publicFoo();
But you can't access the inside functions which are privateBar() and privatefooBar() directly because they are private functions.
Updated Fiddle
Breaking up the function is easy:
function f(..) {
// many lines here
return ret_f;
}
if equivalent to
function f {
function f1(..) {
// not so many lines here
}
function f2(..) {
// not so many lines here
}
var ret_f1 = f1(..);
var ret_f2 = f2(..);
// calculate ret_f from ret_f1 and ret_f2
return ret_f;
}
or if you prefer this style using anonymous functions
function f {
var f1 = function(..) {
// not so many lines here
};
var f2 = function(..) {
// not so many lines here
};
var ret_f1 = f1(..);
var ret_f2 = f2(..);
// calculate ret_f from ret_f1 and ret_f2
return ret_f;
}
I fear however your real question is specific to your existing code and is about what useful smaller functions to extract there and how to combine them.
For this one would need to have your full code and understand it. That might be a bit much for this QA format.
Related
I am trying to do a small JavaScript Lab. In the lab, first I created an Animal object :
function Animal(species, nature) {
this.species = species;
this.nature = nature;
var preys = new Array();
Animal.prototype.getSpecies = function() {
return species;
}
Animal.prototype.getNature = function() {
return nature;
}
Animal.prototype.getPreys = function () {
return preys;
}
Animal.prototype.setNature = function (newNature) {
nature = newNature;
}
Animal.prototype.setSpecies = function (newSpecies) {
species = newSpecies;
}
Animal.prototype.setPrey = function (newPreys) {
preys = newPreys;
}
}
Then, I created a World object which will basically store a number of animal object and separate them according to their nature.
/// <reference path="Animal.js" />
function World() {
var animals = new Array();
animals.push(new Animal("Wolf", "Carnivore"));
animals.push(new Animal("Crocodile", "Carnivore"));
animals.push(new Animal("Sheep", "Omnivore"));
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores());
}
function getOmnivores(animal) {
}
}
In my getOmnivors function, I can not use the Animal class as a variable. It is a little bit complicated for me cause I am new in JavaScript and regardless of their types we are using var keyword (Or not using in some places such as parameters in functions).
What did I do wrong and how can I fix it? I could not reach the Animal class in my private function getOmnivores. I think program does not understand that it is the class called Animal
I hope I explained well. Have a nice day!
EDIT
Error picture :
Animal is the Class name, you don't need it there. When using filter each element of the array is automatically passed on to the callback function as the first parameter of that function.
Since each element of the array is an instance of the class Animal you can use it straight away.
Also, the syntax {ClassName}.Prototype.{functionName} should not be used within that same Class, because by the time the interpreter reaches that line the Animal Class has not yet been defined. That syntax is used on already existing and defined classes. Use this.{functionName} instead.
function Animal(species, nature) {
this.species = species;
this.nature = nature;
this.preys = new Array();
this.getSpecies = function() {
return this.species;
}
this.getNature = function() {
return this.nature;
}
this.getPreys = function () {
return this.preys;
}
this.setNature = function (newNature) {
this.nature = newNature;
}
this.setSpecies = function (newSpecies) {
this.species = newSpecies;
}
this.setPrey = function (newPreys) {
this.preys = newPreys;
}
}
function World() {
var animals = new Array();
animals.push(new Animal("Wolf", "Carnivore"));
animals.push(new Animal("Crocodile", "Carnivore"));
animals.push(new Animal("Sheep", "Omnivore"));
this.getOmnivores = function () {
return animals.filter(this.filterOmnivores);
}
this.filterOmnivores= function(animal) {
return animal.getNature()=='Omnivore';
}
}
myworld = new World();
console.log(myworld.getOmnivores());
A working fiddle at https://jsfiddle.net/47dyg1q9/
The filter method takes a function as a paramter.
You must provide the function but in your code you are instantly calling the function:
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores());
}
Remove the parentheses to provide just the function without calling it, or insert an anonymous function:
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores);
}
// or
World.prototype.getOmnivores = function () {
return animals.filter(function (animal) {
return animal.nature === "omnivore";
});
}
You need to pass the function as an argument, not what it returns.
return animals.filter(isOmnivore);
And isOmnivore becomes
function isOmnivore(animal) {
animal.nature == 'Omnivore';
}
Can I get a little advice on my js modules? I'm good with js, but not quite guru status :) Am I refactoring my modules right?
I've been using the js module pattern like this (rough example, I'm just worried about the structure):
sloppy way?
/* Module Code */
var MapModule = (function ($) {
var $_address;
var $_mapContainer;
function loadApi() {
// do something. maybe load an API?
}
function someInternalMethod() {
// do other things
}
var pub = {};
pub.setAddress = function (address) {
$_address = address;
};
pub.getAddress = function () {
return $_address;
};
pub.initialize = function () {
loadApi();
}
})(jQuery);
// usage
MapModule.initialize();
But that usage seems a little sloppy. I like constructors.
I refactored some modules like this:
Better way?
(function ($) {
this.MapModule = function () {
var $_address;
var $_mapSelector;
var $_mapContainer;
function loadApi() {
// do something. maybe load an API?
}
function someInternalMethod() {
$_mapContainer = $($_mapSelector);
// do stuff with the jQ object.
}
var pub = {};
pub.setAddress = function (address) {
$_address = address;
};
pub.getAddress = function () {
return $_address;
};
pub.initialize = function (selector) {
$_mapSelector = selector;
loadApi();
}
}
})(jQuery);
var map = new MapModule();
map.initialize('#mapcontainer');
That usage seems a lot cleaner to me, and it works just fine, but am I going about it properly?
Taking it another step
Say this module does some stuff with a div that wraps Google Maps and jQuery functionality: Any tips on turning that into a jQ plugin so I can use it with a signature like var map = $('mapcontainer').mapModule();
Thanks!
I have modified your snippet and have actually implemented javascript revealing module pattern which gives the opportunity to implement public & private functions using closure.
Hope this will be helpful:
/* Module Code */
var MapModule = (function (module, $, global) {
var $_address;
var $_mapContainer;
// Public functions
function _loadApi() {
// Do something, maybe load an API?
}
function _someInternalMethod() {
// Do other things.
}
function _initialize = function () {
_loadApi();
}
// Private functions
function _setAddress = function (address) {
$_address = address;
};
function _getAddress = function () {
return $_address;
};
$.extend(module, {
loadApi: _loadApi,
someInternalMethod: _someInternalMethod,
initialize: _initialize
});
return module;
})(MapModule || {},this.jQuery, this);
// Usage
MapModule.initialize();
JSFiddle
Just came across this and thought I'd share my approach...
///////////////////////////////
// Module Code
///////////////////////////////
var ExampleModule = (function()
{
////////////////////////////
// Private Properties
////////////////////////////
var whatever = {
data: 'somedata';
};
////////////////////////////
// Private functions
////////////////////////////
function _init()
{
_loadApi();
_bindToUIEvents();
}
function _loadApi()
{
// load an api
}
function _bindToUIEvents()
{
$('#something').on('click', function(){
// Do something cool
});
}
function _getWhatever()
{
return whatever;
}
//////////////////////
// Public API
//////////////////////
return{
init: _init(),
getWhatever: function()
{
return _getWhatever();
}
};
})();
// Usage
ExampleModule.init;
I am trying to write a small javascript library as shown below. What I really want is when I call
console.log(tnd().pv);
it should output same number and not generate new number everytime. I know the issue is it calls Math.random everytime I console log. But how can I do so that it outputs same number?
(function () {
var tnd = function() {
return new tnlib();
};
var tnlib = function() {
this.version = function(){
console.log('1.0');
};
this.pv = Math.random()*10000000000000000;
};
if(!window.tnd) {
window.tnd = tnd;
}
})();
Don't execute Math.random() on each invocation of tnlib, but as a static variable:
(function () {
function tnd() {
return new tnlib();
}
function tnlib() {
}
tnlib.prototype.version = function(){
console.log('1.0');
};
tnlib.prototype.pv = Math.random()*10000000000000000;
if (!window.tnd) {
window.tnd = tnd;
}
}());
(or, if you really need to make pv an instance property):
var staticPv = Math.random()*10000000000000000;
function tnlib() {
this.pv = staticPv;
…
}
Can you please help answering this. Please not the contraints.
var myLib = {
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
async_call_one(callback_one);
async_call_two(callback_two);
};
var doStuff = function () {
console.log('doStuff is called');
};
};
// User of my library
myLib.init();
myLib.doStuff();
// output
initializing...
doStuff is called
callback_one
callback_two
// What i need:
initializing...
callback_one
callback_two
doStuff is called
Constraint:
calling myLib.init shall not end up calling myLib.doStuff. i.e. myLib.init should be independent of myLib.doStuff
myLib.doStuff() should be called after myLib.init() and its callbacks are returned.
Thanks,
//You must change your API so init is async
//There is no way to have it wait until all initialization is done before it retuns
var init = function (initDone) {
console.log('initializing...');
var n = 0;
function serviceDone(){
n++;
if(n >= 2){ initDone() }
}
async_call_one(function(x){ callback_one(x); serviceDone() });
async_call_two(function(x){ callback_two(x); serviceDone() });
};
// User of my library
myLib.init(function(){
myLib.doStuff();
})
The way I parallelized those calls is very ad-hoc s not the most maintainable (there I need to keep the calls to serviceDone and the value of N in sync).. In the long run I would recommend using one of the many JS async programming libs out there.
hugomg has a good answer.
Yet I think it is really specific and could benefit a sort of workflow implementation, like this (approximately...):
function void() {}
var myLib = {
var g_flow = [];
g_flow[this.init] = [];
g_flow[this.init]["whendone"] = this.callback_one;
g_flow[this.init]["done"] = false;
g_flow[this.callback_one] = [];
g_flow[this.callback_one]["whendone"] = this.callback_two;
g_flow[this.callback_one]["done"] = false;
g_flow[this.callback_two] = [];
g_flow[this.callback_two]["whendone"] = this.doStuff;
g_flow[this.callback_two]["done"] = false;
g_flow[this.doStuff] = [];
g_flow[this.doStuff]["whendone"] = void;
g_flow[this.doStuff]["done"] = false;
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
};
var doStuff = function () {
console.log('doStuff is called');
};
var flow_onward(hwnd) {
async_call(function(){ hwnd(); myLib.flow_onward(g_flow[hwnd]["whendone"]); });
}
flow_onward(this.init);
};
// User of my library
myLib.init();
myLib.doStuff();
Doing this way you can ensure the sequentiality and expand the numbers of callback as much as you want.
ps: this code has not been tested
I am learning how to use jsMockito to write perfect code. So, could you give me any idea on how to run callback which is provided to service?
Here is my class:
function MyClass(service) {
this.service = service;
}
MyClass.prototype.doSomething = function() {
this.service.doIt(function() {
console.log("How to run this function while running tests?");
})
}
And here is my test:
var MyClassTest = TestCase("MyClassTest");
MyClassTest.prototype.testMyClass = function() {
this.service = mock(Service);
this.myClass = new MyClass(this.service);
this.myClass.doSomething();
}
So, I need to see the log message:
"How to run this function while running tests?"
Any ideas are welcome.
Finally I have found the solution.
We need to create doItCallback:
function MyClass(service) {
this.service = service;
}
MyClass.prototype.doItCallback = function() {
console.log("How to run this function while running tests?");
}
MyClass.prototype.doSomething = function() {
this.service.doIt(this.doItCallback);
}
Also we need to update the mock:
var MyClassTest = TestCase("MyClassTest");
MyClassTest.prototype.testMyClass = function() {
this.service = mock(Service);
this.myClass = new MyClass(this.service);
var myClass = this.myClass;
when(this.service).doIt().then(function() {
myClass.doItCallback();
});
this.myClass.doSomething();
}