I am using what I understand to be the Javascript module pattern, and jQuery.
I have an app which has a public and an admin side. Each has its own JS file, though some functionality is shared so I have extracted it to a common file. Gulp combines the common + public files into a single file for use on the public side, and the common + admin files into a single file for use on the admin side.
The public JS file includes something like:
var PublicFoo = (function () {
var bar = function() {
// ..
};
var init = function() {
$button.on('click', Common.someCommonThing);
};
return {
init: init
};
})();
The HTML page where this code is needed fires it off like so:
<script>
PublicFoo.init();
</script>
The admin JS file includes something very similar, also defining a bar() function, and calling the same Common module function.
var AdminFoo = (function () {
var bar = function() {
// ..
};
var init = function() {
$button.on('click', Common.someCommonThing);
};
return {
init: init
};
})();
The common JS file (shared and combined with both public and admin JS) includes something like:
var Common = (function () {
var someCommonThing = function() {
// Do stuff.
// When done, I want to call bar() in the calling module.
// This does not work, throws 'Uncaught ReferenceError: bar is not defined'
bar();
};
return {
someCommonThing: someCommonThing,
// ...
};
})();
From the Common module, how can I reference a function in the calling module?
I know about .caller, but apparently that is non-standard and should not be used.
I could maybe somehow pass in the name of the calling module as a parameter to Common, and reference it, but that seems ugly:
// In PublicFoo
var init = function() {
$button.on('click', function() {
Common.someCommonThing(PublicFoo)
});
};
// In Common
var someCommonThing = function(callingModule) {
// ...
callingModule.bar();
I could also of course extract the bar() call out and do it back in the calling module, but that doesn't seem so neat either:
// In PublicFoo
var init = function() {
$button.on('click', function() {
Common.someCommonThing();
bar();
});
};
// ... and the same thing in AdminFoo
I feel like this must be JS modules 101, a basic requirement, and yet I can't seem to find anything about it, though I may be searching using the wrong terminology. Or is the reason I can't find how to do this because it should not be done this way?
How can I reference the appropriate bar() from the Common module?
I know about .caller, but apparently that is non-standard and should not be used.
Also it doesn't work in your case, as the caller is the event handler and neither PublicFoo nor AdminFoo.
I could maybe somehow pass in the name of the calling module as a parameter to Common, and reference it
Yes, passing references to the thing that you want to be called is the way to go if you want someCommonThing to do different things after it has finished. Notice you really should only use such a callback when the thing is asynchronous, otherwise just returning and calling bar afterwards (like in your last snippet) is much easier.
How can I reference the appropriate bar() from the Common module?
If both bars might be loaded at once into the page, then there's no way around a parameter that references the callback.
However, that doesn't seem to be the case in your example - on one page, AdminFoo takes the role of Foo and on the other page PublicFoo takes the role of Foo.
So just reference only Foo.bar from Common! Let the respective pages fill it with the appropriate value, i.e.
var Foo = AdminFoo
on the admin page and
var Foo = PublicFoo
on the public page.
Passing functions to other functions is very common and perfectly idiomatic JavaScript, so you could do it like this:
// In PublicFoo
var bar = function() {
// ..
};
var init = function() {
$button.on('click', function() {
Common.someCommonThing(bar)
});
};
// In Common
var someCommonThing = function(bar) {
// ...
bar();
};
Related
I have a js file looking like this:
$(document).ready() {
// Mostly DOM manipulation operations
functionOne();
functionTwo();
functionThree();
...
}
What I want to achieve is to encapsulate and organize all functions in one object, and create logic so they get called only on specific pages. I started writing something like this:
(function( window, document, $, undefined) {
var MyNamespace = {};
// Cache these to local scope
MyNamespace.$window = $(window);
MyNamespace.$document = $(document);
// Functions
MyNamespace.functionOne = function() {
...
};
MyNamespace.functionTwo = function() {
...
};
})( window, window.document, window.jQuery );
I wonder if I am going in the right direction, and if there are any better ways of doing this with the page specific logic that I have not started implementing yet (I have a page identifier already available). I have looked at this book written by Addy Osmani and only thing that looked similar to what I want to achieve was the Command pattern, but I am still not convinced if it would be the right choice.
What you are looking for is kind of a class in the normal OOP sense. I personally use the following schema:
var OPTIONS = {
selectorA: '.js-xxx'
};
var MyClass = function(el) {
this.el = el;
this._init() // private function called
};
MyClass.prototype._init = function () {
// Do some stuff
};
MyClass.prototype.publicFunction = function() {
// Do exposable stuff here
}
window.instance = new MyClass(document.querySelect('#myElement'));
In order to load these modules you should need requireJS or another AMD loader.
I have 3 .js files. The main Home.js and two other .js files, for instance Page1.js, Page2.js
Home.js:
var Home= {
Sample: function (pageId,data) {
pageId.MergePageData(data);
}
}
Page1.js:
var Page1 = {
MergePageData: function (data) {
// do something
}
}
Page2.js:
var Page2 = {
MergePageData: function (data) {
// do something
}
}
I tried calling like this by passing it as a string:
Home.Sample('Page1', 'data');
Home.Sample('Page2', 'data');
But I guess as it is being passed as a string am getting an error
"Object doesn't support property or method 'MergePageData' "
I need to differentiate the call between the two functions in two different js files. How to achieve that?
The pageId in your function is just a variable, and when you call it you specify the type of this variable, and as you pass "Page1", which is a String it is just a string without having anything to do with your real Page1 object. but there are some options that can help you out.
The Other point that you have to consider is, it doesn't matter you have 2 or 3 different js files. the important point is if all your javascript codes are injected to single html page, then you can have all your code in all the JavaScript files in single window context. So if you define something in a global scope which is window, you can have access to it all over your JavaScript code.
It seems your Page1 and Page2 are global objects, so you can do it like:
var Home= {
Sample: function (pageId,data) {
window[pageId].MergePageData(data);
}
}
but I guess data vriable is not a global, you have 2 options, first make it global or just store it in a storage like:
localStorage.setItem("data", JSON.stringify(data));
then change your Sample function like this:
var Home= {
Sample: function (pageId,data) {
window[pageId].MergePageData(JSON.parse(localStorage.getItem("data")));
}
}
Although you have to be very careful about the size of your data object, if it is a big object with a lot of properties and inner objects, you should reconsider and change your solution.
Basically you want to create your object Home inside an IIFE. That way you can pass in any library or object namespace throughout several files.
For your Home object, declare and run an anonymous function, assign your object directly to the the window object. This will be your "namespace" and accessible throughout your files by window.Home. Use this file to initialize your Page objects. As for now the DOM ready event is used from jQuery.
// #param ($): jquery library 1.10.2
(function ($) {
// 1. ECMA-262/5
'use strict';
// 2. PRIVATE CONFIGURATION
var cfg = {
// an object literal to store config
page2: {
page: 'page2',
data: 'data'
}
};
// 3. GLOBAL OBJECT NAMESPACE
window.Home = {
init: function(){
// initialize your other files
this.cache = {
page1: new Home.Sample();
page2: new Home.Sample(cfg.page2);
}
}
};
// 4. ONCE THE DOM IS READY
$(function () {
Home.init();
});
}(window.jQuery));
Then for your other files, a slightly different approach can be used.
// #param ($): jquery library 1.10.2
// #param (home): Home namespace
window.Home = (function ($, home) {
// 1. ECMA-262/5
'use strict';
// 2. CONFIGURATION
var cfg = {
page: 'page1'
data: 'data'
};
// 3. CONSTRUCTOR FUNCTION
home.Sample = function (options) {
this.settings = $.extend({}, cfg, options);
this.init();
};
// 4. PROTOTYPE OBJECT
home.Sample.prototype = {
init: function(){
this.cacheItems();
this.mergePageData(settings.data);
},
cacheItems: function(){
this.page = settings.page;
},
mergePageData: function (data) {
// do something with this.page and data
// consider caching data instead of passing it along
}
};
// 5. GLOBALIZE OBJECT
return home;
}(window.jQuery, window.Home || {}));
This approach is modular and better to maintain. Since the whole configuration is extracted from the logic you will find it easier to create different instances of an object. Simply by passing in options into your Sample object you can change the whole data/structure but keeping behavior as intended. You can fill in options from your server language and use the sizzle selector engine inside jQuery todo powerful DOM traversing and so on ...
Im designing an API that requires my users to download a javascript file from my server and then load it on their pages. Inside this file there is a function call generic(), if my users include this js and for some reason they have a piece of js on their page where there is another function call generic() this will represent an issue. Im not a front end dev, I know that with php you can solve this creating a class and putting all your functions inside, so you can call them like $myclass->myfunction();, but how can i solve this on js? Is this even a good approach on js? (no jquery please.)
You will obviously always have to expose at least one identifier globally, but a common approach is to wrap everything in an immediately-invoked function expression:
var YourNamespace = (function () {
var privateData = 10; // Not accessible outside the IIFE
// Expose public properties (these functions can access the private data)
return {
someMethod: function () {
// Do stuff
},
anotherMethod: function () {
// More stuff
}
};
}());
This will expose a single identifier, YourNamespace, as an object with properties that can be used as methods. You can use it like this:
YourNamespace.someMethod();
Wrap your code inside a wrapper object/ or function.
var MyLibrary = {
global1: 123,
global2: 'abc',
doSomething: function(a){
// ...
},
somethingElse: function(b){}
};
If u are looking for Encapsulation in Javascript, then u are looking for Closures
Hi,
I have my main file in which I include my javascript file.
In my javascript file I have this
$(document).ready(function(){
//some functions here
});
I want all the functions just available to this page and I know you can kinda conceal them to outside world of javascript by doing something like
(function(){
$document.ready(function(){
//my functions
)};
}).init();
but I am not 100% sure how would it be called or whether its even the right way.
Anyone shedding light on this would be a great help!
In javascript everything declared inside a function is only available inside that function (except for when you declare a variable without the keyword var).
So everything inside the function that you pass to $().ready() is only available inside that function.
$(document).ready(function () {
//all code here is scoped inside this function, so it can't be accessed
// outside of this function
});
Like the first comment says you can't hide them from the user, if they really want to see it, they will see it.
You can clean them up in a way if you really wanted to, something like
var mySpace = {};
mySpace.init = function() {
// your init functions here
};
in doc ready you just call
mySpace.init();
I am not sure if this is what you wanted but it is the way I understood the question
(function(){
var secret1 = function(msg) {console.log("Secret Message:" + msg);}
$document.ready(function(){
secret1("this can only be called from within");
)};
})();
secret1("this will cause a script error");
It sounds like the thing you are looking for is a 'javascript obfuscator'. Here is an example one. It makes the code much harder to read and copy. But as others have said, you can't actually fully hide javascript.
The problem here is that JavaScript is intrinsically a client-side scripting language unless using a server-side javascript application such as node.js.
As long as JavaScript is being used in this way, the entirety of your code will be downloaded much like downloading a .txt file from a website. The only real difference is that the ".js" extension and its inclusion in html <script> tags or in an AJAX call will force the user's browser to render it as JavaScript.
If you want to make the script a little harder for the user to find, however, this is doable. I recommend having your website retrieve the script via AJAX and appending it to the DOM. You can do this with require.js or by using Kickstrap and making your script into an "app." The script won't appear as a link in the DOM and the user would really have to search for it. You can make it even more difficult (without compromising the integrity of your site) by minifying the script. This will make it run faster while inadvertently making it less human-readable on the front end.
In JavaScript there is only function scope (the exception argument in try-catch being an exception). ES5 will let you use let (no pun intended) to achieve block scope but it wont be usefull untill majority of UAs implement it.
So your functions are concealed from the outside world, if with outside you mean outside the dom ready event.
$( document ).ready( function () {
var myFunc = function () {};
} );
myFunc();// <- ReferenceError: myFunc is not defined
You can't really hide the functions, as it's in the source code of a file downloaded by the client, but you can make it so they can't access your functions from javascript.
(function() {
var doStuff = function() {
// Not Accessible
console.log('You can\'t reach me!');
}
return {
'init': function() {
// Accessible
doStuff();
}
}
})().init();
If you are talking about Access Modifiers like public, private etc. Then check out this article on how Javascript handles this. Here are the key components:
//constructor function (class)
function Maths(x, y) {
//public properties
this.x =x;
this.y = y;
//public methods
this.add = function () { _sum = x + y; return _sum; }
this.mod = function () { _mod = x % y; return _mod; }
//public method calls private method
this.show = function () {
this.add();
this.mod();
showResult();
}
//private variables
var _sum=0;
var _mod=0;
//private methods
function showResult() {
alert( "sum: " + _sum + ", mod: " + _mod );
}
}
//end function
//create instance
var plus = new Maths(3, 4);
plus.show();
//static method multiply, you can use it without instance of Maths
Maths.multiply = function (x,y) { return x * y; }
//call static method by constructor function (class) without instance of Maths
var result = Maths.multiply(5,7);
alert(result);
//output: 35
I'm looking to encapsulate my javascript inside a namespace like this:
MySpace = {
SomeGlobal : 1,
A: function () { ... },
B: function () { ....; MySpace.A(); .... },
C: function () { MySpace.SomeGlobal = 2;.... }
}
Now imagine that instead of a few lines of code, I have about 12K lines of javascript with hundreds of functions and about 60 globals. I already know how to convert my code into a namespace but I'm wondering if there's a quicker way of doing it than going down 12K lines of code and adding MySpace. all over the place.
Please let me know if there's a faster way of doing this.
Thanks for your suggestions.
I like to wrap up the namespace like so. The flexibility is huge, and we can even separate different modules of the MySpace namespace in separate wrappers if we wanted too. You will still have to add some sort of _self. reference infront of everything, but at least this way you can change the entire name of the namespace very quickly if need be.
You can see how with this method you can even call _self.anotherFunc() from the 1st module, and you'll get to the second one.
(function (MySpace, $, undefined) {
var _self = MySpace; // create a self-reference
_self.test = function () {
alert('we got here!');
_self.anotherFunc(); // testing to see if we can get the 2nd module
};
_self = MySpace; // reassign everything just incase
}(window.MySpace = window.MySpace || {}, jQuery));
$(function () {
MySpace.test(); // call module 1
MySpace.callOtherModule(); // call module 2
});
// Here we will create a seperate Module to the MySpace namespace
(function (MySpace, $, undefined) {
var _self = MySpace; // create a self-reference
_self.callOtherModule = function () {
alert('we called the 2nd module!');
};
_self.anotherFunc = function () {
alert('We got to anotherFunc from the first module, even by using _self.anotherFunc()!');
};
_self = MySpace; // reassign everything just incase
}(window.MySpace = window.MySpace || {}, jQuery));
jsFiddle DEMO
Wrap a function body around your existing code to use as scope, hiding everything from global - this will allow you to do internal calls without pasting Namespace. prefix everywhere, neatly hide things you don't want everyone else to see, and will require minimal changes as well.
After that, decide what functions you want to "export" for everyone and assign them to properties of object you want to use as "namespace".