Apologies for the title but I wasn't sure how to phrase this.
In a normal module you have the following format:
var SomeClass = function() {
...
};
module.exports = SomeClass;
You can shorthand that to:
module.exports = function() {
...
};
There is however a problem with the second notation if you need to use the 'class' name inside itself ( to instantiate a new object of itself for example). Using the 1st example this would be easy ...
var SomeClass = function() {
...
function doSomething() {
var x = new SomeClass();
}
...
};
module.exports = SomeClass;
Is this possible using the 2nd notation? I know I can't use 'this' as that is an instance variable, I also can't use any form of the module.
module.exports = function() {
...
function doSomething() {
var x = new ??????
}
...
};
I'm asking purely from an aesthetic and a uniformity point of view.
Any Help appreciated.
Thanks
It is possible to do this in the form that you describe. Theoretically, to you in that scope, there is no difference between SomeClass and module.exports: both are variables and you give them both the same value.
Therefor, if you wanted, you could have something like this (though it looks odd):
module.exports = function() {
// ...
function doSomething() {
var x = new module.exports();
}
// ...
};
Related
I was just not sure how to search this out despite many tries, so forgive me if this has been answered before.
The question is simple: can I create an instance of class window.A.class() as window.B?
To clarify, I have an object literal holding all my data for a browser game:
var gameName = {
environment: function() {
this.place = "...";
// ...
// ...
},
game: function() {
this.player = function() {
// ...
}
}
// and so on...
}
Could I create a window-level gameName.environment() instance with var A = new gameName.environment()? Are there any restrictions to creating an object-bound class's instance outside the class' parent object?
It doesn't really matter in this case how/where a function is defined. Consider these two examples:
function Foo() {}
var obj = {
bar: Foo
};
and
var obj = {
bar: function () { }
};
As far as the function and the object are concerned, those two examples are equivalent. So no, there is no problem calling a function assigned to an object property with new. All you need is a reference to the function, it doesn't matter how you get that reference.
You could do
var Environment = gameName.environment;
var A = new Environment();
if you like that better, but that's totally unnecessary.
I have read a lot of articles about how to create modules in node.js, and you can use module.exports to expose module internals to the file which includes it.. awesome!
How does this work the other way around? I'll use the following as an example :-)
USER.JS
function User() {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
MAIN.JS
var myArray = [];
myArray.push('something');
myArray.push('something else');
mainFileFunction() {
for(thing in myArray) {
return myArray[thing];
}
}
var u = new user();
log(u.property); <--- THIS IS EXPOSED, COOL!
u.doSomething(); <--- This will throw an error because mainFileFunction is not defined in user.js :-(
If I were to move mainFileFunction to the user file, then it still wouldn't work because the myArray array wouldn't be defined... and if I were to move that over too, I wouldn't be able to use it in other functions in main (which I want to) :-)
I'm sorry if I'm missing something really obvious here... What I want is to expose the parts of my choosing from modules I include (module.export works for that) but I also want to expose everything from the main file to all the includes..
or just expose everything to everything? is that totally messy and horrible??
Just to explain what I am trying to do here... I want to have classes defined in separate files, but I want to instantiate a bunch of them as objects in the main file and store them in arrays.. I want the objects to contain methods which can access arrays of the other object types.
Thanks guys! :-)
You can use globals, or have a proper circular dependency (requireing both files), however - this is usually a bad habit which can lead to maintainability problems in the future.
Instead, you can use dependency injection and inject doSomething into your module.
This basically gives you the following for free:
You can test User with a simple mock implementation of doSomething later and verify the correctness of your code
The dependencies of a user are explicit and not implicit, which makes it obvious what a user needs.
I'll provide two implementations, one using constructor dependency injection and one with a module wide setting.
USER.JS
function User(dependentFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = dependentFunction();
// do something with getStuff
}
}
module.exports = User;
MAIN.JS
...
var u = new User(mainFileFunction);
u.doSomething(); // this will now work, using mainFileFunction
What happens here is fairly simple, and we know what's going on.
This can also be a module wide setting
USER.JS
function User(depFunc) {
this.property = 'value';
this.doSomething = function() {
var getStuff = depFunc();
// do something with getStuff
}
}
function UserFactory(depFunc){
return function(){
return new User(depFunc);
}
}
module.exports = UserFactory;
MAIN.JS
var getUser = UserFactory(mainFileFunction);
var u = getUser(); // will return a new user with the right function
+1 to Benjamin answer for dependency injection.
I would like to add another way to inject objects in your modules by passing the dependency in the require like require('./module.js')(dependentFunction);
//MAIN.js
var db = function() {
this.rows = [];
this.insert = function(name) {
this.rows.push(name);
console.log('Db: Inserting user with name ' + name);
}
this.getAll = function(){
return this.rows;
}
}
var fakeDb = new db();
var user = require('./user.js')(fakeDb);
user.add('Jhon');
user.add('Rose');
user.list();
and
//users.js
module.exports = function(db) {
return {
add: function(name) {
db.insert(name);
},
list: function() {
var users = db.getAll();
var i = users.length;
console.log('listing users\n-------');
while(i--) {
console.log(users[i]);
}
}
}
}
You should pass mainFileFunction as a parameter to the constructor of user.
USER.JS
function User(mainFileFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
In your main.js use the following
var u = new user(mainFileFunction);
How about moving mainFileFunction to user.js, and have the function accept an array as an argument:
mainFileFunction(array) {
for(thing in array) {
return array[thing];
}
}
And then when you call it from main.js, pass the function your array:
u.doSomething(myArray);
If I have a javascript class that runs some initialization code, it seems logical to put this code at the top, and any methods at the bottom of the class. The problem is, if the initialization code contains a method call, I get a 'undefined' is not a function error. I imagine because the method is defined after the method call. How do people normally structure javascript classes to avoid this? Do they put all the initialization code at the end of the class? For example:
var class = function() {
this.start();
this.start = function() {
alert('foo');
};
};
var object = new class();
causes an error, while:
var class = function() {
this.start = function() {
alert('foo');
};
this.start();
};
var object = new class();
does not. what would be considered a good structure for a javascript object like this?
Here's what I would do
// create a "namespace"
var com = com || {};
com.domain = com.domain || {};
// add "class" defintion
com.domain.MyClass = function(){
var privateFields = {};
var publicFields = {};
privateFields.myFunction = function(){
// do something
}
publicFields.initialize = function(){
privateFields.myFunction();
}
return publicFields;
}
var myClass = new com.domain.MyClass();
myClass.initialize();
of course, you could just make initialize(); "private" and run it before return publicFields;
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
now you can mess around with start as much as you want in the constructor. but dont use the keyword class as it is a reserved word.
I like http://ejohn.org/blog/simple-javascript-inheritance/ where I feel much more programing OOP.
var myClass = Class.extend({
init: function () {
this.foo();
},
foo: function () {
alert("foo");
}
});
new myClass();
Consider making start a method on class's prototype. This has the bonus of saving memory since all instances of class can share the same start, instead of creating a new start function for each instance.
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
I actually work a lot with structures like this:
var foo = function() {
// Actual construction code...
start(); // <-- doesn't make a difference if functions are
privateMethod(); // public or private!
// ---------------------------------
function start() {
// ... whatever ...
};
function privateMethod() {
// ... whatever ...
};
// ---------------------------------
// Add all public methods to the object (if start() was only used internally
// just don't assign it to the object and it's private)
this.start = start;
};
I am wrapping common javascript functions that will work on elements on a page.
My page has 2 of these elements (textareas), so I will need to create 2 instances and then I want to do this:
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init("ta1");
textArea2.init("ta2");
I tried doing this the module pattern way, but I'm confused how I can create 2 seperate instances of it?
var MYMODULE = function() {
var _init = function(ta) {
// ..
}
return {
init: function(ta) {
_init(ta);
}
};
}();
Use a constructor function:
function SomeClass(id) {
this.id = id;
// ...
}
Usage:
var textArea1 = new SomeClass("ta1");
var textArea2 = new SomeClass("ta2");
You can put methods for the class in the prototype for the function. Example:
SomeClass.prototype = {
getValue: function() { return document.getElementById(this.id).value; }
};
Usage:
var text = testArea1.getValue();
Using your specific example, you could just MYModule twice, but it's a weird pattern that doesn't seem to do a whole lot.
Simple example how instantiation works:
function SomeClass() {
// constructor
}
SomeClass.prototype.init = function(ta) {
// ..
}
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init('ta1');
textArea2.init('ta2');
But regardless, you may like Backbone.js
Your MYMODULE idea will work fine. As above and then
MYMODULE.init("ta1");
MYMODULE.init("ta2");
This line here will not care it is called with two different parameters
var _init = function(ta) {
// ..
}
It is just a place to hold a function. The real question is what is inside that function.
For example if it works with ta in some standard way (attaches event handlers, does some styling.. ) then it will not be a problem. The issue will be if you use MYMODULE local variables and expect to have more than one of them. You only have one MYMODULE so local variables will be shared with this design. This might be what you want. I'm not sure.
This pattern can work fine for a control passed in having special data all itself. The best way to do this -- since you are using jQuery is with the data function... thus the code could look like:
var _init = function(ta) {
jQuery.data(ta,"foo", 10);
// etc
}
How can I hook up an event to a function name I have defined as a string?
I'm using Prototype.js, although this is not Prototype-speficic.
$(inputId).observe('click', formData.fields[x].onclick);
This would result in JavaScript complaining that my handler is not a function. I would prefer not us use eval().
Property accessors can be used to access any object's properties or functions.
If the function is in the global scope, you can get it using the window object:
var myFunc = window[myFuncName];
This also works within the this scope:
var myFunc = this[myFuncName];
I have worked on this problem, as I needed a function like this. Here is my sandbox code, not thoroughly tested, but can be a startpoint for others.
Note that there is one eval() in the code as I couldn't figure out how to bypass that step, maybe a javascript quirk and cannot be done in any other way. Let me know if there is a way to get rid of eval() here!
executeFunctionByName = function(functionName)
{
var args = Array.prototype.slice.call(arguments).splice(1);
//debug
console.log('args:', args);
var namespaces = functionName.split(".");
//debug
console.log('namespaces:', namespaces);
var func = namespaces.pop();
//debug
console.log('func:', func);
ns = namespaces.join('.');
//debug
console.log('namespace:', ns);
if(ns == '')
{
ns = 'window';
}
ns = eval(ns);
//debug
console.log('evaled namespace:', ns);
return ns[func].apply(ns, args);
}
core = {
paragraph: {
titlebar: {
user: "ddd",
getUser: function(name)
{
this.user = name;
return this.user;
}
}
}
}
var testf = function()
{
alert('dkdkdkd');
}
var x = executeFunctionByName('core.paragraph.titlebar.getUser', 'Ikon');
executeFunctionByName('testf');
... or this[myFuncName];
Perhaps?
setTimeout ( "myFunc()", 1 );
Just an eval would do the job
var call = eval("method_name").call(args);
Looks like formData.fields[x].onclick holds the name of a global function? If so try:
$(inputId).observe('click', window[formData.fields[x].onclick]);
window.myFunction === window["myFunction"]
Do you know what the onclick property contains or what type it is? I assume this is prototype specific stuff, as "fields" does not exist in DOM forms.
If you need to call a string function with arguments, do this:
window[stringFunctionName].apply( window, arrayOfArguments )
You can use scope in place of window if preferred
update:---
use ES6 export and import
a.js
const fn = {
aaa: function() {
//code
},
bbb: function() {
//code
},
//codes ....
nnn: function() {
//code
}
}
export default fn
b.js
import someFn from './a'
//eg
const str1='aaa'
const str2 = 'bbb'
someFn[str1]()
eval('str') (obsolete feature https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features )
setTimeout('str') setInterval('str')
window['str'] (but...sometimes,global object is not window)
new Function('str')
These methods above always not be recommend by some reasons, but they are really convenient to use.
These methods below are safe, but really not conveninet to use.
switch...case (or if...else)
switch(str){
case 'str1':
fn1()
break
case 'str2':
fn2
//and so on
}
put functions in a object
const fn={
str1:fn1,
str2:fn2
//and so on
}
fn[str1] //call function