I am trying to get my head around the javascript AMD pattern using requireJS. I'm very new to object oriented programming and also new to requireJS. I'd hope some one can help me here.
I defined a test module called 'module3' with object literal:
define([], function () {
var _name = 'this is a test3';
var returnedModule3={
name:'test',
getName:getName
}
function getName() {
return _name;
}
return returnedModule3;
});
However in the main.js file when I call this module after the file is loaded, I get an error in firebug saying "TypeError: module3ref is not a constructor". Below you will see the code in my main file:
// Load modules and use them
require(['myModule/module3'], function(module3ref){
// do something with the loaded modules
var module3 = new module3ref();
console.log("module3.getName:"+module3.getName());
});
Does this mean we cannot use object literal to create module?
Interpreting your question's title literally: RequireJS can turn object literals directly into AMD modules, e.g.:
define({
getName: function() {
return 'this is a test3';
},
name: 'test'
})
However, with this pattern it's not possible for one property to refer to its "neighbours" (but that's a limitation of JS object literal syntax, not RequireJS itself).
I have tried on my side. Try this:
define([], function () {
var _name = 'this is a test3';
var returnedModule3 = function(){
this.name = 'test';
this.getName = getName
}
function getName() {
return _name;
}
return returnedModule3;
});
Note: this should be attached to each property within returnedModule3 so as to be accessed from outside/other JS file otherwise it become private to function and you will get error object has no method getName.
Issue with your code: In your code, you were trying to create an instance of object.
Another Approach (EXPORTING OBJECT) :
If you want to export Object as it is like your returnedModule3 then must define main.js as:
require(['module3'], function(module3ref){
// do something with the loaded modules
console.log("module3.getName:"+module3ref.getName());
});
Related
I've got a school assignment of creating an app and one of the restrictions were that only one global variable was allowed, named "App". So I need to put the whole JS inside this variable.
I thought of doing something like:
App = function() { this.test = () => alert("test"); }
And then calling it with App.test().
But this doesn't work, the error I'm getting is:
Uncaught TypeError: App.test is not a function at HTMLLIElement.onclick (index.html:25)
Am I doing something wrong?
You need to define your app in a variable as an object and then you can use those members of the object such as:
// Object creation
window.App = {};
Then you add more properties like functions or variables and use it later inside of that variable.
window.App.test = function() {
alert('test');
}
window.App.variable = 'my variable';
console.log( App.test() );
console.log( App.variable );
Another thing is you can omit the word window from App.
Keeping most of your approach as it is, you could return an object that has functions as properties.
App = function() {
return {
test: () => alert("test"),
test2: () => alert("test2")
};
}
App().test();
App().test2();
To be able to use your function that contains this.test..., you'll need to use it as a "constructor function" because this in a function declaration means "instance property", meaning you will need to explicitly create an instance of App with the new keyword:
// Declare the global
var App = function() { this.test = () => alert("test"); }
// Make an instance of an object via the constructor function
var myApp = new App();
// Invoke the functionality via the instance
myApp.test()
Or, set up App as an object, connect that object to the Global window object and set test as a property of App all without any instance properties (this references), which avoids having to make the explicit instance:
// Declare the global property as an Object
// and set up a "test" property that stores
// a function in that object:
window.App = { test: function(){alert("test");}}
// Invoke the functionality directly
App.test()
The test function is not defined before executing the App function:
App = () => test = () => alert("test")
<button onclick='App(); test()'>:</button>
App can be defined as object instead:
App = { test: () => alert("test") }
<button onclick='App.test()'>:</button>
Just to piggyback on to what the other answers have suggested, this technique is most commonly used (in browser developement) when you "namespace" some JS code. Namespacing is useful because it helps the developer reduce global variable pollution by adding all their code under one namespace under window.
This also helps the developer modularise the code.
Here's a pattern you might see from time to time that adds code to a single namespace under window.
// This first function is what's known as an Immediately-invoked
// function expression (IIFE). `window` is passed in as an argument
// to the function - all other declared variables are local to the scope
// of the function so you don't get anything leaking to global.
// PS, the semi-colon is there before the opening parenthesis to prevent
// any errors appearing if you minimise your code
;(function (window) {
// Here we declare something new to add to the namespace
// It could be another object, a string, an array, or a constructor
// like Model
function Model() {...}
// If namespace `window.app` doesn't exist instantiate it with
// an empty object. If it _does_ exist it probably means that another
// module like this one has already been added to the namespace.
window.app = window.app || {};
// Then assign your new module as a property under that namespace
window.app.Model = Model;
})(window);
You can then use it later something like this:
var model = new app.Model();
var game = new Game(model);
For further reading: Addy Osmani on namespacing.
I'm trying to clean my code a bit, so I create some small objects or libraries (call it how you want) like:
function myLib() {
this.get = function() { ... };
...
};
Problem is when I try to call it like myLib.get(). It throws the following error:
Uncaught TypeError: myLib.get is not a function
I've tried to encapsulate the call into $(document).ready(), but it did not help.
Can you help me, please?
Thanks!
myLib is used for "libary", and you want to call this "get" method of libary.
Static instance is better in your case.
const myLib = {
get:function(){},
get2:function(){}
...
};
myLib.get();
myLib.get2();
so I create some small objects or libraries (call it how you want)
In your case you are creating a constructor, myLib is a constructor and not just a function, and you can't access a function's properties and methods directly that's why you got the Exception.
So you need to get an instance of myLib in order to call the get method or to access any of its members(methods).
function myLib() {
this.get = function() { console.log("get called!!"); };
};
let lib = new myLib();
lib.get();
Note:
And from the MDN Reference for Functions you can see that:
The this keyword does not refer to the currently executing function, so you must refer to Function objects by name, even within the function body.
You should use myLib as a constructor ie:
var lib = new myLib();
lib.get();
Can someone explain how to do this properly
foo(); //outputs 'foo'
function foo(){
console.log('foo');
}
but this gives 'function is undefined' error
MY_NAME_SPACE ={};
MY_NAME_SPACE.foo(); //undefined
MY_NAME_SPACE.foo = function(){
console.log('foo');
}
I can see that in the second example, the call was made before the function was added to the My_NAME_SPACE object, but if this is the case,how would one use this type of "name space" if the ordering is important?
Yes, if you are going to use this namespace pattern, you will need to create and populate the namespace before trying to invoke methods or access property values that have not yet been assigned to the namespace.
Instead of defining the namespace object and then defining each consecutive method in the namespace, eg:
var MY_NAME_SPACE = {};
MY_NAME_SPACE.foo = function() {
console.log('foo');
}
I prefer to use what is referred to as the module pattern, as the methods that I want contained in MY_NAME_SPACE are visually wrapped in the module:
var MY_NAME_SPACE = (function () {
var foo = function () {
console.log('foo');
};
return { foo: foo };
})();
MY_NAME_SPACE.foo()
Also, if the methods you wish to wrap in a namespace or module are independent and reusable, it would makes sense to create a separate file, maybe my_name_space.js, and include this file in projects that need access to the methods in MY_NAME_SPACE (the MY_NAME_SPACE API).
Is it possible to do achieve this behavior using node modules?
module.js:
module.exports = function () {
var test.fn = function () {
console.log("$test.fn()");
}
var test.fn2 = function () {
console.log("$test.fn2()");
}
var variable = "test";
};
app.js:
require("./module.js")();
test.fn();
test.fn2();
otherFunction(variable);
I dont want to do anything like this $ = require("./module.js")(); $.test.fn();
I want to inject this variables into app.js scope without wrapper variable.
Edit:
I have ended up using this:
module.js:
module.exports = function () {
eval(String(inject));
var inject = (
this.$module1 = {},
this.$module1.fn = function () {
console.log("$module1.fn()");
}
);
};
app.js:
require("./module.js")();
$module1.fn();
The top level scope in a module is actually a function scope (the node.js loader wraps each module in a function that it then calls to execute the code in the module). Thus, there is no publicly available "root" object that we can programmatically assign properties to.
So, that means that it is not possible in a module to programmatically add new variables at the top level of the module scope without using eval() in a fairly big hack. Function scopes just don't work that way in Javascript.
You could also have the module assign things to the global object where they can be used without a prefix, but this is NOT a recommended practice in any way. The whole point of node.js modules is to avoid using any globals and to make code entirely self contained with little chance of global collisions.
Or, you could have your module export a giant string of Javascript and then eval() it inside of app.js in order to define new variables in the module scope. Again - not recommended.
Your best best is do things the "node.js way" and put everything on an object and export that object. Here's one variation:
app.js
var test = require("./module.js")();
test.fn(); // "executing fn"
test.fn2(); // "executing fn2"
console.log(test.myVar); // "test"
module.js
module.exports = function () {
return {
fn: function () {
console.log("executing fn");
},
fn2: function() {
console.loog("executing fn2");
},
myVar: "test"
}
};
You can do something like that using an IIFE. The method, when required, will automatically run and return an object, which you can then use in your application.
module.js:
global.test = (function() {
return {
fn: function() {
console.log("executing fn");
},
fn2: function() {
console.log("executing fn2");
},
variable: "test"
}
})();
app.js
require("./module.js"); //just include, no need for a wrapper variable
test.fn();
test.fn2();
console.log(test.variable);
Note that this technique will overwrite the global variable test if it already exists.
This answer should not encourage you to use global within your node.js modules (see jfriend00's comments above) as it makes your code vulnerable for name collisions with other modules and thus your modules less portable.
Within module.js you have access to the global object of node.js runtime environment.
module.js:
global.test = {
fn: function() {
//Do something
},
fn2: function() {
//Do something different
},
variable: "test variable"
}
app.js
require("./module.js"); //just include, no need for a wrapper variable
test.fn();
test.fn2();
console.log(test.variable);
Note that this technique will overwrite the global variable test if it already exists.
I've been interested in prototypical programming with JavaScript, and I'm trying to figure out an efficient way of doing it with Node modules.
For example, I'd like to use a prototype to quickly create a debug object in each of my modules, which has a name property and a log method, constructed via:
custom_modules/debug.js
var settings = require('custom_modules/settings');
exports = function debug(name){
this.name = name;
this.log = function(message){
if (settings.debug == 'true'){
console.log("[Debug][" + name + "]: " + message);
}
}
}
So I'd like to know if I can use that module as a constructor, like so:
do_something.js
var debug = new require('custom_modules/debug')("Something Doer");
debug.log("Initialized"); // -> [Debug][Something Doer] : Initialized
Will it work? If not, what's the correct equivalent?
new doesn't care where the function comes from. So yes, the function can be the result of requireing a module.
However, the module must directly export the function. In your current code you are merely assigning a new value to the local exports variable, and we all know that assigning to a local variable doesn't have any effect outside of its scope.
The module will still export an empty object. You have to override the exports property of the module:
module.exports = function() {...};
As pointed out, there will be problems with precedence, so you would have to do
var debug = new (require('custom_modules/debug'))("Something Doer");