I'm using the introjs library.
See the original code here.
I want to be able to write var = new IntroJs() rather than call the start() method.
How can I achieve that?
Why not simply wrap up the factory that introJs provides and call start on it in your wrapper?
You can do it externally with something like this (untested):
var introJsWrapper = function(targetElm) {
var ijs = introJs(targetElm);
ijs.start();
return ijs;
};
Or you can do that inside a fork of the introJs code by exposing it as a property of the main function, e.g.:
var introJs = function (targetElm) {
if (typeof (targetElm) === 'object') {
// ...
}
introJs.autoStart = function(targetElm) {
var ijs = introJs(targetElm);
ijs.start();
return ijs;
};
Note that in introJs, the main function is just a very thin parameter-testing/changing wrapper already around the internal constructor. Calling it indirectly invokes the constructor. So there is really no need to access this internal constructor function directly, as far as I can see.
Well, this should be it. I assume these are enclosed in a closure since the code seems to imply that there is some internal functions going on. Here's what I gathered. It's not a complete implementation since I don't know how the this when calling new IntroJS gets used in the constructor. All I know is that your prototype functions are operating on some properties.
//internal functions
function _mergeOptions(target){/*implementation*/}
function _introForElement(el){/*implementation*/}
function _goToStep(step){/*implementation*/}
function _exitIntro(target){/*implementation*/}
function _setHelperLayerPosition(nodeList){/*implementation*/}
//constructor
function IntroJs(first){
this._options = {};
this._introChangeCallback;
this._introCompleteCallback;
this._introExitCallback;
}
Just an empty constructor will suffice. As Jan said, it's pretty useless, but if you like the notation...
http://plnkr.co/edit/eFzkKJ14TeaMY44GDxR2
Ok, so basically this solved my problem:
introJs.fn = IntroJs.prototype = {
...
initialize: function() {
return this;
}
...
}
Now, calling introJs().initialize() gives me the library without calling the start() method.
Related
I wrote a JS "plugin" using the following clever structure (which was not an idea of mine, I found googling around).
It basically functions as a class, with private methods, but don't have to use the private keyword notation (which is not fully supported even in modern browsers), or the class notation.
var myPlugin = (function () {
'use strict';
/**/
var Constructor = function (options) {
var publicAPIs = {};
//private method
function myFunction() { }
//public method
publicAPIs.myFunction2 = function () {
return 1;
}
return publicAPIs;
};
return Constructor;
})();
Now the problem is I don't fully get how this works.
It is used in the following way :
var myPlugin = new myPlugin({
selector: '#selector-test'
});
myPlugin.myFunction2();
I guess the "new" keyword will use the outer function which returns the Constructor (which returns the publicApi object). Is that correct ?
Also, I guess instantiating an Object in this way will "copy" all the Constructor's functions within each instantiated Object (which is not a problem in my case, since I'm using just a single Object).
Another question I have is the following :
Would it be possible in JS to call this plugin directly on an element instead of passing the selector (or directly the element) in the arguments ? Basically something like JQuery plugins work. I'd like to do something like the following :
var myElement = document.querySelector('#id');
myElement.myPlugin(options);
Thanks.
Right now I have about 3 seperate javascript "classes". I call them like this;
ajax(parameters).success().error()
getElement('selector').height(400).width(400).remove();
customAlert({object with arguments});
This not only feels like random function calling, but will be likely to give me some naming issues.
Then I thought: How does jQuery do it?
Well, I have no idea. I've tried googling the subject but so far I haven't found any results of how to make this happen. Only results of how to add prototypes and such...
The basic idea of my classes is like this:
var getElement = function(selector){
if(!(this instanceof getElement))
{
return new getElement(selector);
}
//Do element getting
return this;
};
getElement.prototype = {
name: function,
name: function,
//etc.
};
Now, this is kind of working perfectly fine, but I'd like to "prefix" and scope my functions within a wrapper class. I want to call my functions like this:
wrap.getElement(selector);
wrap.ajax(parameters).success().error();
wrap.customAlert({object with arguments});
However, whenever I try it, I bump into at least one kind of error or issue, like;
losing the this scope within my classes
Being unable to add prototype functions to my classes
The entire wrapper class reinstantiating with every function call
being unable to create new object() because the scope isn't right anymore
Also, if at all possible I would like to not re-initialize the wrapper class every time. (seems wildly inefficient, right?)
So I'd want 1 instance of the wrapper class, while the classes within it get their new instances and just do their thing. Is this even possible or am I just dreaming here?
This is the way I've tried it so far;
//It would probably be easier to use an object here, but I want it to
//default to wrap.getElement(selector) when it's just wrap(selector),
//without re-instantiating the old class
var wrap = function(selector){
//Check if docready and such here
}
wrap.prototype.getElement = function(){
// the getElement class here
}
//This is where it starts going wrong. I can't seem to re-add the prototypes
//back to the getElement class this way.
wrap.getElement.prototype = {
name:function,
name:function,
//etc
}
//I can call wrap().getElement(); now, but not the prototypes of
getElement().
//Also, I still have wrap() while I'd want wrap.getElement.
Then I "solved" the issue of having to do wrap() by putting it into a variable first.
var test = new wrap();
test.getElement(selector); // works and only inits once!
//However, I did have to remove the 'new instance' from my getElement()
I have also tried it this way, but this just gave me errors on top of errors of which I didn't really know why;
(function(wrap) {
console.log("init")
this.getElement = function() {
return "test";
};
})(wrap);
// Gives me "wrap is undefined" etc.
And last but not least, I have tried it this way;
var wrap = new function() {
return this;
};
wrap.getElement = function(){};
//This works perfectly fine
wrap.getElement.prototype.css = function(){};
//This will cause "getElement.css is not a function"
So yeah, I'm kind of stuck here. There are many ways to get past this in ES6, I've found. I am however not willing to move to ES6 yet (Because I don't use anything that still needs an interpreter). So it has to be ES5.
The easiest way to wrap your modules in a namespace and keep the classes intact is to use the "Revealing module pattern".
It uses an immediately invoked function expression (IIFE) to set up all the functions with a private scope and then returns just the functions in a new object (which works as a namespace for your module)
var wrap = (function() {
function getElement() {...}
function moreFunctions() {...}
function etcFuncitons() {...}
return {
getElement: getElement,
moreFunctions: moreFunctions,
etcFuncitons: etcFuncitons
};
})();
To answer your second question
I would like to be able to call wrap() by itself as well. I want it to forward automatically to getElement(). Is this hard to do? Is this possible with this construction? Because this looks very easy to maintain and I'd love to keep it like your answer. - Right now it will reply wrap() is not a function
I haven't tested this but you should be able to attach the functions directly to a returned wrapper function. This should avoid the issue of shared this by adding them to the prototype
var wrap = (function() {
function getElement() {...}
function moreFunctions() {...}
function etcFuncitons() {...}
function wrap(selector) {
return new getElement(selector);
}
wrap.getElement = getElement;
wrap.moreFunctions = moreFunctions;
wrap.etcFuncitons = etcFuncitons;
return wrap;
};
})();
This works because everything is an object in javascript, even functions haha
I'm trying to understand something in JavaScript which confuses me.
Let's say that I want to create a method Guard.ThrowError() in JavaScript, I have 2 approaches for this:
Using an object:
This is basically the approach which I found on the net.
var Guard2 = {
ThrowIfNull: function() {
throw new Error('sdmflsfk');
}
};
Using a Function:
When you create a class "Guard" in TypeScript and you let it compile to JavaScript, you get something like this:
var Guard = (function() {
function Guard() { }
Guard.ThrowIfNull = function () {
throw new Error('sdmflsfk');
};
return Guard;
})();
Both functions can be called using Guard.ThrowIfNull().
I would like to know what's the difference and when choose I choose approach 1 over approach 2?
Thanks for your valuable feedback.
Kind regards
I would like to know what's the difference and when choose I choose approach 1 over approach 2?
The only difference is that Guard is a function in your second example and not a function in your first. So the second one can be called (in that code it doesn't actually do anything, but it can be called); the first can't.
Functions are objects in JavaScript, proper real objects, and so like all other objects, they can have additional properties added to them like your ThrowIfNull.
jQuery is a widely-used example of this: The main function, jQuery (aka $) is used for its function-ness:
$("#foo").on("click", function() { /*...*/ });
...and also for its object-ness, because it has various properties attached to it exactly as you've done with Guard:
$.ajax(/*...*/);
Both approach works because a function in javascript is also an object. The only difference is that you can call a function myFunction(); and you can't do it with a object.
Function approach is used to create classes in JS. Using this approach, you can have private and public properties. This cannot be achieve in object approach. All properties are public in object.
Sample Example
function testClass(){
// Private member
var a = "abc";
var b = "test";
function print(){
console.log(a,b);
}
return {
print: print
}
}
testClass().print();
// testClass().a This will throw error
Guard2- your approach 1 - is somehow a 'static' definition of an object. You can't instatiate another object of that type.
Guard- your approach 2 - is a mixed: it's a definition of an 'automatic class' Guard which gets instantiated. The result is almost the same as you can't instantiate new objects from that as well.
Usually, you would do it like this:
var Guard = function() {
function Guard() { };
Guard.ThrowIfNull = function () {
throw new Error('sdmflsfk');
};
};
var myGuard = new Guard();
EDIT:
Everything is working as I expected. It was just an error calling the template method. I mistyped a () so I was trying template.method instead of template().method;
Anyway, if somebody would like to explain me if this is a valid design pattern or if I should go in a different way I will be definitively very grateful.
I read about the module pattern and I'm trying to implement it in some of my projects. The problem is that, in my opinion, I'm twisting it too much.
I'm inspired by the google apps script style where many objects returns other objects with methods and so on and they pass arguments.
something like
object.method(var).otherMethod();
What I want to achieve is a method that receives a parameter, sets an internal variable to that parameter and then returns an object with methods that uses that variable. Here is a minified version of the code that does not work:
var H_UI =(function (window) {
var selectedTemplate,
compileTemplate = function(){},
parseTemplateFields = function(){};
//template subModule. Collect: collects the template fields and returns a JSON representation.
var template = function(templateString){
if(templateString) selectedTemplate = templateString;
return {
getHtml:function(){ return compileTemplate( parseTemplateFields( selectedTemplate ) ) } ,
collect:function(){
.. operating over selectedTemplate ...
return JSON.stringify(result)}
} };
return {
template:template
};
})(window);
If I remove the line :
if(templateString) selectedTemplate = templateString;
and replace selectedTemplate with the parameter templateString in the methods of the returned object it works as expected. I know that I cant create a set() method in the returned object and use it like this
H_UI.template().set(var)
But I find it ugly. Anyway I think that I'm messing things up.
What is the best way to construct this?
If you want H_UI.template() creates a new object every time you call template() on it, your solution does not work. Because the variable selectedTemplate is created only once when the immediate function is called.
However if your intent is this your solution works fine. (variable selectedTemplate is shared for all calls to template()).
But if you want to every call to template creates a new object. Please tell me to write my idea
Is this a valid design pattern or if I should go in a different way
Yes, enabling chaining is definitely a valid design pattern.
However, if your template() method returns a new object, that object and its methods should only depend on itself (including the local variables and parameters of the template call), but not on anything else like the parent object that template was called on.
So either remove that "global" selectedTemplate thing:
var H_UI = (function () {
function compileTemplate(){}
function parseTemplateFields(){}
// make a template
function template(templateString) {
return {
getHtml: function(){
return compileTemplate(parseTemplateFields(templateString));
},
collect: function(){
// .. operating over templateString ...
return JSON.stringify(result)
}
}
}
return {template:template};
})();
or make only one module with with a global selectedTemplate, a setter for it, and global methods:
var H_UI = (function () {
var selectedTemplate;
function compileTemplate(){}
function parseTemplateFields(){}
return {
template: function(templateString){
if (templateString)
selectedTemplate = templateString;
return this; // for chaining
},
getHtml: function(){
return compileTemplate(parseTemplateFields(selectedTemplate));
},
collect: function(){
// .. operating over selectedTemplate ...
return JSON.stringify(result)}
}
};
})();
The difference is striking when we make two templates with that method:
var templ1 = H_UI.template("a"),
templ2 = H_UI.template("b");
What would you expect them to do? In a functional design, templ1 must not use "b". With the first snippet we have this, and templ1 != templ2. However, if .template() is a mere setter, and every call affects the whole instance (like in the second snippet), we have templ1 == H_UI and templ2 == H_UI.
I was reading another question, and I saw this:
var basketModule = (function() {
var basket = []; //private
return { //exposed to public
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
},
getTotal: function(){
var q = this.getItemCount(),p=0;
while(q--){
p+= basket[q].price;
}
return p;
}
}
}());
Can you please explain why does he wrap the function in ( and )'s? Also, what is the purpose of that return? Couldn't he just write self.addItem = ... and so on?
When you wrap a function with parantheses, and add () to the end of it, it's a self executing function.
(function() x() {
//do something;
})();
And, by returning he's making basket variable somewhat private. Try getting basketModule.basket from anywhere else, and you'll get undefined.
That is called javascript Module Pattern. Defining a function and calling it immediately to prevent variables to be in the global space or to define a function name.
Note parentheses in the last line: (). The function is defined and immediately called:
(function() { })();
return { ... } returns an object having a method addItem
The intention of the code is to create an object with three methods. addItem,getItemCount and getTotal. They all depend on state represented by basket.
if basket was defined globally that state would be exposed (and there could only ever be one variable basket. both of those can lead to issues so by wrapping the entire declaration the state is encapsulated and only accessible from the created object.
There are other ways of achieving the same and the pro's and con's are related to style and how many objects of that particular type you're going to need.
wrapping the function(){}() is required since function(){}() will not parse