I'm new to JavaScript programming.I wrote an IIFE that will help me improve my understand. My intention is to define a $ function that when called will call itself as a constructor. When the code is run, it generates an error 'Too much recursion'. I don't know what the problem is.
(function() {
//check the global host object
var root = this;
var inside = "inside";
var $ = function () {
return new $(); //this line generates an error 'Too much recursion.'
}
$.check = function(obj) {
console.log(inside);
}
//add the $ to global object
root.$ = $;
}).call(this);
var ins = $();
console.log(ins);
var $ = function () {
return new $(); //this line generates an error 'Too much recursion.'
}
This function is repeatedly calling itself, which is why you see the Too much recursion. error. You aren't distinguishing between a regular function call and a new call.
My intention is to define a $ function that when called will call itself as a constructor.
The simplest way is to explicitly check this:
var $ = function $() {
if(!(this instanceof $)) return new $();
// ... from this point on, behave as if called via new
}
this line generates an error 'Too much recursion.'
Right. You have a function assigned to the $ symbol which calls the function assigned to the $ symbol. So each call (whether direct or via new) will run the code in that function, which makes another call to it, and so on until you exceed the engine's willingness to recurse. To avoid that, have $ do something else.
It's because you have created an infinite loop. By using parenthesis when returning your new var instance you are recursively calling that function with no parameters. I'm not sure what you are trying to accomplish, but you might want to have $ create a new object "{}" instead and then you can extend methods off of that reference. Look into singleton patterns, that will allow you to create a new instance of something.
*edit, and just to be clear your problem doesn't have anything to do with it being an IIFE, you would encounter this same error anywhere you tried to call a new function on itself in this manner.
Related
Learning the JS Module Pattern.
I am trying to figure out the differences between an IIFE and a regular function in this use case. It looks like the ONLY advantage of an IIFE is that my module becomes an object (since the IIFE returns immediately) so I don't need to use the call invocation/param () syntax on my module.
Is there any additional benefit? Consider the following code:
//IIFE
var HelloWorldEncapsulated = (function(){
return {
privateMethodEncapsulated: function () {
console.log('ayy lmao encapsulated');
}
}
})();
HelloWorldEncapsulated.privateMethodEncapsulated();
//Just a function
var HelloWorld= function(){
return {
privateMethod: function () {
console.log('ayy lmao');
}
}
};
HelloWorld().privateMethod();
Again, is there any difference between the two besides using () and not using it? It seems that privateMethod is just as hidden in the non-IIFE function as it is in the IIFE. Code here in case you don't want to type 2 words
The only difference in those is when you're executing your function. In your second example, if you want to call another method (or call privateMethod more than once), you'd have to execute it again (not ideal):
HelloWorld().privateMethod();
HelloWorld().privateMethod(); // Calls HelloWorld a second time, creating a
// whole new object to make the call on (bad idea)
...or remember the result of the first execution an dreuse it:
var hw = HelloWorld();
hw.privateMethod();
hw.privateMethod();
...at which point you're back to your first example.
Are there any (non-trivial/ugly hack) ways to call a 'private' method from outside a class/module itself?
Please don't ask why I need this.
Just my personal curiosity and trust in power of doing anything in JS :)
function Module () {
var privateMethod = function () {
alert('1');
};
var publicMethod = function () {
privateMethod();
alert(2);
};
return {
pm: publicMethod
};
}
var m = new Module();
m.pm(); // can i call m.privateMethod() here somehow?
DON'T TRY THIS AT HOME
So you've been warned, now look at this (fiddle; I've replaced all the alerts with console.log() for the greater good):
var methodName = 'privateMethod';
var mSource = Module.toString();
var methodPattern = new RegExp(methodName + '\\s*=\\s*function[^]*?\\{([^]+?)\\};');
var privateMethodSource = mSource.match(methodPattern);
var privateMethodRebuilt = new Function([], privateMethodSource[1]);
privateMethodRebuilt();
It's one possible way to do this with HUGE number of restrictions. First, this particular snippet doesn't even try to parse the arguments, assuming that the method in question doesn't need any. Second, you won't be able to access the other private variables defined within the Module (as the code rebuilds only the particular function, not the environment).
Surely, you can take it further - and rebuild the Module itself, making the target method public (for example, by adding the private function into the exposed methods' list). The question is, WHY on Earth do you even think about needing such thing as using the private method outside the module?
Let's start from the code:
function say(name) {
var ghost=function () {
function ghost() {
alert('!');
};
return body;
};
eval("var body=''+"+name+';');
eval(name+('=('+ghost).replace('body', body)+')();');
eval(name+'();');
}
function Baal() {
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
say('Baal'); // or just Baal();
Looks like that saying the devil's name invoke his presence (well, maybe he needs somebody for spiritual possession) ..
As you can see the ghost doesn't exist along with Baal, but we can invoke it since there're evals in say(name).
say(name) reassigns Baal to its code body as a closure and makes it captured a ghost method, that's how things work. But I'm trying to avoid eval ..
So .. let me reword the question:
How do I make a nonexistent(and not a member or global) method invocable without using eval?
Let me rephrase your question, just to make sure I’ve got it. Given a function, you want to put a new variable in its scope, without that scope being the global scope or a scope shared between the caller and the subject, without using eval (or the equivalent new Function and other hacks depending on the environment).
You can’t.
In the case you just mentioned, you could define one function, base(), that uses arguments.callee.caller.
Don’t do that.
The short answer: You don't.
That scope is not available. If you were to attach the scope then it would be available inside of the scope used. You could then access the method handles. I assume this is not what you were looking for, but here is what that would look like. demo
function say(name){
var methods = {};
methods.Baal = function(){
alert("!");
};
return methods[name];//this could invoke as well: methods[name]()
}
var handle = say('Baal');
handle();
What your evals break down to is something along these lines (although with dynamic content from string building - this is the end result)
function say(name) {
var Baal = (function () {
function ghost() {
alert('!');
};
return function(){
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
})();
Baal();
}
say('Baal'); // or just Baal();
Note that the meat of what happens here is from the function Baal, namely that it calls a hardcoded ghost() which in turn calls a hardcoded alert. Why go through all of this trouble to access a hardcoded function?
A better way would be to inject this function as a callback which expects some parameters to be injected.
jsFiddle Demo
function say(callback){
var params = "!";
if( typeof callback == "function" ){
callback(params);
}
}
say(function(params){
alert(params);
});
It's very difficult for me to read through your code and figure out what you are trying to accomplish with it, but it appears that you are trying to introduce a variable into the current scope so that you can call it. You cannot do this in javascript with the method that you demonstrated. Scoping only ever "flows down". By that I mean that a variable or function defined within a function will only be available to that function and any other functions defined therein. Your function named ghost will only ever be available within the function where it is defined, regardless of when that function is evaluated.
What you can do, however, is write a function that returns a function. You can then call that function and assign the result to a variable in the scope where you want to expose functionality. Doing that would look something like this.
function defineSpecialAlert() {
return function(name) {
alert(name + "!");
};
}
var newlyDefinedMethod = defineSpecialAlert();
newlyDefinedMethod("Baal");
So if I understand, it seems like you want to create an alias of eval: Something like
#Note this code is not intended as a solution, but demonstrates
#an attempt that is guaranteed to fail.
#
function myAlias(ctx) {
eval.call(ctx, 'var ghost = 42');
}
myAlias(this);
alert(ghost);
Javascript allows many funky sleight-of-hand tricks especially with closures, but this is maybe the one impossible thing that javascript cannot do. I've tried at length to do this exact same thing, and I can tell you that you'll run into nothing but complaints from the browser, saying that eval cannot be re-contexted or aliased in any way.
For some strange reason, when calling a function that assigns this to thisObj, I get an error:
TypeError: thisObj is undefined
Here's what I've got:
function templateObject()
{
"use strict";
var thisObj = this;
function _loadBackgroundImages()
{
"use strict";
// something happens here
}
thisObj.initialise = function()
{
"use strict";
_loadBackgroundImages();
};
}
The function is then called using the instantiation like so:
var templateObj = templateObject();
templateObj.initialise();
Can't figure out why I get the error - any idea?
Use new:
var templateObj = new templateObject();
Calling function with new will pass newly created empty object as this to the function and then return it to templateObj.
When you call a function like you've done (i.e. not as a method of an object):
templateObject()
Then, if you’re in strict mode, this will be undefined inside that function.
As #mishik pointed out, it looks like you wanted templateObject to be a constructor function, and to use it as such, you need to call it with the new keyword before it:
var templateObj = new templateObject();
One style note: it's conventional in JavaScript to name functions intended to be used as constructors with an initial capital.
That might make mistakes like this marginally less likely, as it'd look odd to see a function with an initial capital called without new:
function TemplateObject() {
...
}
var templateObj = new TemplateObject();
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.