Vanilla JS | Invokable object that also contains property - javascript

I was asked in some assignment to do the following:
fun4(): returns a object that can be invoked as a function. this object should also has a ‘k’ property with null value ( so fun4()() should do something)
The first part of the question is easy and understandable.
The second one is the issue for me.
How can I create an object in JS that can be invoked and accessed statically.
To simplify:
Can one create an object that behaves as follows:
> let o = CustomObject;
> o.k
< null
> o()
< //Some value returned here from the function
Thanks!

It looks very straightforward to me...
let CustomObject = function(){ return "hello"; }
CustomObject.k = null;
This will pass your Acceptance criteria

As for vanilla js, this is what you are looking for:
var fun4 = function () {
let val = function () { console.log("hello"); };
val.k = null;
return val;
}
fun4() // returns the function
fun4()() // logs 'hello'
fun4().k // returns null
Given one of your comments above, one thing to watch out for is your use of let and var. In this case, var could be used instead without any difference (when val falls out of scope when the function returns, the variable will be released for garbage collection). However, when you are running this within the console (and when not inside a function or other well-defined and isolated scope), variables created using let will get destroyed after every invocation -- in other words, whenever you press return. The way to demonstrate this is to compare these:
var test = function () {
let x = 1;
let x = 1; // this will cause an immediate syntax error when the script is parsed;
}
On the other hand:
> let x = 1;
< undefined
> let x = 1; // In the console, this will not raise any error/exception
// because the scope is cleared after the line above has
// executed and returned
< undefined

You can see a 'k' property as below.

Related

JavaScript shadowing with initialization

Before I dive into the question I want to clarify that my use case involves patching a trans-compiler to generate a proper equivalent, hence the somewhat awkward question.
I want to shadow an outside variable but initialize it to the same value as outside as well. Here is an example:
var a = 2;
(function(){
var a = a;
a += 3;
// I want `a` to be 5
})();
// I want `a` to be 2
I realize with the above example the internal a will be NaN (undefined + 3), but can I initialize the variable doing the shadowing to the same one that it shadows somehow? Passing it as an argument is not an option as that function will be written by the user, the only thing that will be consistent is the presence of inner scope. I was thinking of changing the name of internal variable a but the compiler isn't currently built in a way to track it easily and this would introduce additional headaches.
You need to pass a as parameter in your IIFE.
(function(parameter){
// «parameter» contains the given value.
// parameter = "Some value".
})("Some value");
Something like this:
var a = 2; // Variable declaration in the global scope.
(function(a) {
a += 3;
// I want `a` to be 5
console.log(a); // Prints the current value in the local scope.
})(a); // The parameter: var a = 2;
console.info(a); // Prints the current value in the global scope.
// I want `a` to be 2
Since that is a immediately invoked function expression it has a completely different scope than the code written outside of it. There's no way to do what you are asking without passing in an argument in some way (whether directly when executing or using bind), or changing the function so the scope is that of the scope where the wanted var a is defined.
With that being said perhaps you can return some methods that will set a to the appropriate value.
http://jsbin.com/vazequhigo/edit?js,console
var a = 2;
w = (function(){
var setA = function(val) {
a = val;
}
var addA = function(val) {
a += val;
return a;
}
var a = 0;
return {
setA: setA,
addA: addA,
};
})();
w.setA(a);
console.log(w.addA(3));

JavaScript: Initializing inner variable after initializing object

Update: rewriting question because original question had false assumptions (I was running code in a console that had already initialized the variables I thought were undefined).
This makes sense:
var obj = { 'whichScope': a };
obj.whichScope; //"ReferenceError: a is not defined"
But then how come this next example doesn't throw an error? And if the second line is getting run before the first line, why doesn't obj.whichScope resolve to "After"?
var obj = { 'whichScope': a };
var a = "After";
obj.whichScope; //"undefined"
If "var a" in the previous example runs before obj is initialized, does any part of 'a = "After";' also run before obj is initialized?
var a = "Before";
var obj = { 'whichScope': a };
a = "After";
obj.whichScope; //"Before"
If whichScope refers to a function that returns 'a' then it does resolve to "After" in the last example.
That is called variable hoisting.
References:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/var#var_hoisting
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/
Variables (declared with var) and functions are hoisted to the top of their scope.
So technically, your script is like this:
var a; // = undefined
var obj = { 'whichScope': a };
a = "After";
Although your first example doesn't do what you say. The value of obj.whichScope is undefined, as expected.
DEMO: http://jsfiddle.net/pRQwK/
As for your last statement, If whichScope refers to a function that returns 'a' then it does resolve to "After" in the second example. - that is because the value of a isn't captured (by a closure). When setting a property, its value is captured immediately (unrelated to a closure).

JS function to return function, shares data which should be private

I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)

Getting rid of eval

I have a name of a method as a string in javascript variable and I would like to get a result of its call to variable:
var myMethod = "methodToBeCalled";
var result;
eval("result = "+myMethod+"();")
This works and there are no problems. But this code is inacceptable for Google Closure Compiler. How can I modify it to work with it? Thanks!
EDIT:
It seems the proposed solutions does not work when the name of the method is inside of some object, for instance:
var myFunction = function () { return "foo!" }
var myObject = {
itsMethod: function() { return "foo!" }
};
...
var fstMethodToCall = "myFunction"
var sndMethodToCall = "myObject.itsMethod";
...
window[fstMethodToCall](); // foo!
window[sndMethodToCall](); // undefined
Assuming you are not in a nested scope of some kind, try:
var result = window['methodToBeCalled']();
or
var myMethod = 'methodToBeCalled';
var result = window[myMethod]();
To execute an arbitrary function of arbitrary depth based on a string specification, while not executing eval:
var SomeObject = {
level1: {
level2: {
someFunc: function () {
console.log('hello');
}
}
}
};
var target = 'SomeObject.level1.level2.someFunc';
var obj;
var split = target.split('.');
for (var i = 0; i < split.length; i++) {
obj = (obj || window)[split[i]];
}
obj();
You can use indexer notation:
result = window[myMethod]();
The Closure Compiler doesn't prohibit 'eval', you can continue to use it if you find it convenient but you have to understand that the compiler doesn't try to understand what is going on in your eval statement and assumes your eval is "safe":
function f(x, y) {
alert(eval("y")); // fails: hidden reference to "y"
alert(eval('"'+x+'"')); // might be valid
}
f('me', 'you');
When the compiler optimizes this function it tries to remove "y" and renamed the remain parameter. This will the first eval to fail as "y" no longer exists. The second eval would correct display the alert "me".
So with SIMPLE optimizations, you can use eval to reference global variables and object properties as these are not renamed or removed (but not local ones).
With ADVANCED optimizations, it is a little trickier, as the compiler tries to remove and rename global as well as local variables. So you need to export the values you need to have preserved. This is also true if you use a string to try to reference a name by other means:
var methodName = "myMethod";
(window[methodName])()
or
var methodName = "myMethod";
eval(methodName+"()")
the compiler simply doesn't try to determine if "methodName" is a reference to a function. Here is a simply example of an ADVANCED mode export:
window['myMethod'] = myMethod;
The assignment does two things: it preserves the myMethod function if it would otherwise be removed and it gives it a fixed name by assigning it to a property using a string. If you do need to reference local values, you need to be a little trickier and use a Function constructor. A definition of "f" from my first example, that can eval locals:
var f = new Function("x", "y", "alert(eval('y')); alert(eval('\"' + x + '\"'));");
You may find this page useful:
https://developers.google.com/closure/compiler/docs/limitations

Binding "this" when passing an object's member function

I had a "class" defined and was making only one instance of it. The instance possessed a member function that would end up being passed around (it's a mouse handler, but that's not important). Since I would only ever make one instance of my "class", I decided to rewrite it as a singleton by using an object literal.
So I have
var mySingleton = {
theObjects : [];
}
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}());
mySingleton.addObject = function(newObj) {
this.theObjects.push(newObj);
}
However, when I try to use this code (after adding a few objects), I keep getting an that.theObjects is undefined error. It's referring to the line in the for loop.
Update for 2015 – Use Function.bind() to specify the value of this within the function. Then, instead of using that, you can use this.
mySingleton.mouseHandler = function (e) {
for (var indx = 0; indx < this.theObjects.length; indx++) {
// do something to this.theObjects[indx];
}
}.bind(mySingleton);
This doesn't work if you want mouseHandler to have the context of the 'moused' element. For that, use my original answer below.
If you need to support IE8 or (heaven forbid) earlier, you'll need to use a polyfill.
Since you are calling the function that creates mouseHandler immediately, it is run in the context of window, not mySingleton. So that refers to window. Instead of calling it immediately, just change it to a method so that it runs in the context of mySingleton:
mySingleton.getMouseHandler = function() {
var that = this;
return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();
Of course, since you are already using a singleton, you can just reference it directly. In your click handler, instead of checking that.theObjects, check mySingleton.theObjects. Or, in mouseHandler change var that = this to var that = mySingleton.
Edit: Or, pass the context to your anonymous function when you call it:
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}).call(mySingleton);
There are a few popular ways to do this. First, super-simple solution is just reference mySingleton directly and bypass the confusion associated with this. Instead of that.theObjects just do mySingleton.theObjects and move on with your life and things will work fine.
However, there is a common pattern to do this binding. Here's how underscore.js does it
Check out the annoted source to underscore, where you will find this
_.bind = function(func, obj) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
};
};
The other answers here so far are also correct. Providing my viewpoint here in case it helps.
The key to understanding why the code doesn't behave as you expect requires understanding how this works in JavaScript. The problem is that this depends on how the function is called.
First, if you call the function in the method style, this is what you'd expect:
mySingleton.mouseHandler(); // this === mySingleton
If you attach the function to something esle, that works too.
var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton
If you detach the function, this becomes the global scope object (window)
var foo = mySingleton.mouseHandler;
foo(); // this === window
And finally, you can force this to be something else using call or apply:
var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy
The takeaway is that this is determined at runtime based on the context of how the function was called. Often, frameworks that allow you to make "classes" abstract these details from you by implicitly applying the bind pattern on your behalf. This is why it used to work, and no longer does.
As others have mentioned, you can change your handler to reference the variable by its scoped name (mySingleton) or otherwise bind it as discussed.
Here's an article I wrote on the subject a few years ago that goes into more detail: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword
Hope this helps!

Categories

Resources