Function Scope with multiple nested IIFE's - javascript

I'm having trouble with this JavaScript exercise. I'm given a function that defines three different variable/value pairs and within that function are multiple nested IIFEs that change those same values. The goal of the exercise is to change the variable's values to a certain value. So here is the code that I was presented:
var scopeExercise = function() {
var a = 1,
b = 2,
c = 3;
result = "a: " + a + ", b: " + b + ", c: " + c;
(function firstFunction() {
var b = 5,
c = 6;
(function secondFunction() {
var b = 8;
(function thirdFunction() {
var a = 7,
c = 9;
(function fourthFunction() {
var a = 1,
c = 8;
})();
})();
})();
})();
return result;
};
console.log(scopeExercise());
And they want the var a = 1, b = 8, and c = 6. I'm still having trouble understanding the function scope because I've tried commenting out the thirdFunction and fourthFunction so that they don't get called before the outer functions and it still won't change the values of var a, b, and c. Also, I don't understand why the nested functions aren't getting executed since they should be immediately invoked.

try this:
var scopeExercise = function () {
var a = 1, b = 2, c = 3;
(function firstFunction() {
b = 5;
c = 6;
console.log('firstFunction()');
(function secondFunction() {
b = 8;
console.log('secondFunction()');
(function thirdFunction() {
a = 7;
c = 8;
console.log('thirdFunction()');
(function fourthFunction() {
a = 1,
c = 6;
console.log('fourthFunction()');
})();
})();
})();
})();
result = "a: " + a + ", b: " + b + ", c: " + c;
return result;
}
console.log(scopeExercise());
added the prints that you will see that all functions were executed

Related

Addition of two or more functions in javascript

I have a function which gives three objects
function myfunc(one, two, three){
this.one = one;
this.two = two;
this.three = three;
}
var a = new myfunc(6,5,7);
var b = new myfunc(10,4,2);
var c = new mufunc(20,1,8);
This gives the three separate objects which are useful. However, i want to create a forth object which is the sum of a,b and c. In effect this would be the same as:
var all = new myfunc(36, 10, 17);
I can do this manually:
aa = a.one + b.one + c.one
bb = a.two + b.two + c.two
cc = a.three + b.three + c.three
var all = new myfunc(aa, bb, cc)
but is there a better way which is less manual.
You could put them into an array and sum their properties in a loop of course:
var list = [a, b, c];
function sum(arr, prop) {
return arr.reduce((acc, x) => acc+x[prop], 0);
}
var all = new myfunc(sum(list, "one"), sum(list, "two"), sum(list, "three"));
Alternatively, mutate an initially empty instance in a loop:
var all = [a, b, c].reduce((obj, x) => {
obj.one += x.one;
obj.two += x.two;
obj.three += x.three;
return obj;
}, new myfunc(0, 0, 0));
The only way to achieve this is to create a function to handle this for you, if you're going to be running it regularly. Just pass in the objects and the function will handle it:
function sum_objects( obj1, obj2, obj3 )
{
return new myfunc(
(obj1.one + obj2.one + obj3.one),
(obj1.two + obj2.two + obj3.two),
(obj1.three + obj2.three + obj3.three)
);
}
function myfunc(one, two, three){
this.one = one;
this.two = two;
this.three = three;
}
myfunc.prototype.add = function(){
return this.one + this.two + this.three
}
var all = new myfunc(36, 10, 17);
all.add()

How to call a function right before object is called

Is it possible to call a function at the second an object is being called?
I have following object:
var z;
var a = {
b: function(){
return z * 2;
}
c: function(){
return z * 3;
}
d: function(){
return z * 4;
},
e: function(){
return z * 5;
}
}
var onObjectInvoke = function(){
z = (new Date()).getTime();
}
I want to reset the value of z before a.d() is being called, here's the flow I mean, when a.d() is called:
call onObjectInvoke.
call a.d();
I'm looking for some kind of constructor, is there any way?
Why can't you just call onObjectInvoke() before calling a.d()? Your life will be a lot easier if you allow your functions to be stateless and operate on given parameters rather than using a global z variable.
var z;
var a = {
d: function(x){
return x * 4;
}
}
var onObjectInvoke = function(){
return (new Date()).getTime();
}
z = a.d(onObjectInvoke());
var z;
var a = {
b: 2,
c: 3,
d: function(){
onObjectInvoke();
return z * 4;
}
}
var onObjectInvoke = function() {
z = (new Date()).getTime();
}
console.log(a.d());
One simple way is to add one more layer and then access onObjectInvoke() in that layer first and return the functions from that layer as object and call the desired function on return value
a.defaultFunc().d()
var z;
var a = {
defaultFunc: function() {
onObjectInvoke()
return {
b: function() {
return z * 2;
},
c: function() {
return z * 3;
},
d: function() {
return z * 4;
},
e: function() {
return z * 5;
}
}
}
}
var onObjectInvoke = function() {
console.log('reseting z')
z = (new Date()).getTime();
}
console.log(a.defaultFunc().d())
console.log(a.defaultFunc().e())
In such a case you most certainly don't want to use z directly but invoke a function that returns the value for you. And in that function, you will either reset z or return it depending on your current use case.
Having external code that heavily modifies the behavior/outcome of a function is always a bad idea with regard to maintainability and readability.
var z;
var resetZ = true;
var a = {
getZ: function() {
if (resetZ) {
z = (new Date()).getTime();
}
return z;
},
b: function() {
return this.getZ() * 2;
}
c: function() {
return this.getZ() * 3;
}
d: function() {
return this.getZ() * 4;
},
e: function() {
return this.getZ() * 5;
}
}
a.d()
You for sure should not use a global resetZ, but as it is not clear how exactly you use your code it is not clear how to structure the code. Or maybe getZ should be a free function, instead of a function belonging to the object.

Javascript: making functions at runtime

update
solution works in foreach loop but not in for loop
function x(number){
return number - 10;
}
var i = 0
var runtimefunctions = {};
var allLevels = {"1":"State","2":"Educational_Services","3":"Principal_Networks","4":"Schools"}
for (var key in allLevels) {
runtimefunctions[i] = function() { return x(i); };
i++;
};
console.log(runtimefunctions[1]()); // -6
console.log(runtimefunctions[2]()); // -6
console.log(runtimefunctions[3]()); // -6
tried hard to make functions but it's first time to create such thing so cant understand the proper way...
I have a function..
function x(number){
return number - 10;
}
runtimefunctions = {};
now I have a loop to run
[1,2,3].forEach(function(y){
//here I want to create a function.. which will make a function x(y) -- like this
runtimefunctions[x] = new Function("return function x_" + levelIterator + "(levelIterator){ console.log(levelIterator); x(" + y + ") }")();
});
so basically..want to make functions like this.
runtimefunctions= {
"1": x(1),
"2": x(2),
and so on
}
Is this what you need?
function x(number){
return number - 10;
}
var runtimefunctions = {};
[1,2,3].forEach(function(y){
runtimefunctions[y] = function() { return x(y); };
});
console.log(runtimefunctions[1]()); // -9
console.log(runtimefunctions[2]()); // -8
console.log(runtimefunctions[3]()); // -7
To satisfy your next (for-in) requirement, you need to closure the index variable with additional function call:
var runtimefunctions = {}, i = 0;
var allLevels = {"1":"State","2":"Educational_Services","3":"Principal_Networks","4":"Schools"}
for (var key in allLevels) {
runtimefunctions[i] = function(index){ return function() { return x(index); } }(i++);
};
It is much easier.
For example:
const createFunctionWith = (x) => {
return (param) => console.log(x, param)
}
let a = [1,2,3].map(x => createFunctionWith(x));
console.log(a[1]("bebe")); // 2, "bebe"
https://jsfiddle.net/muLxoxLd/
You could do something like this
// Found in your code
var x = (a) => {
console.log(a)
};
var runtimefunctions = {};
[1, 2, 3].forEach(function(y) {
//Create a function with a parameter named "levelIterator"
runtimefunctions[y] = Function("levelIterator", "{ console.log(levelIterator); x(" + y + ") }");
});
runtimefunctions[1]('test')

Lazy.js based fibonacci to map Bacon.js interval?

I have a code to generate fib sequences with lazy.js
var fibF = function()
{
var seq = []; //build sequence array in this closure
var f = function(n)
{
var val;
if (n <= 1)
{
val = 1; // as the Fib definition in Math
}
else
{
val = seq[n - 2] + seq[n - 1]; // as the Fib definition in Math
}
seq[n] = val;
return val;
};
return f;
}();
var fibSequence = _.generate(fibF);
/* just for test
var fib_1000 =
fibSequence
.take(1000) 
.toArray(); 
console.log(fib_1000);
//[ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,...........,4.346655768693743e+208 ]
*/
At the same time, I have a code of timer with Bacon.js
var B = require('baconjs');
var timeSequence = B
.interval(1000); //every second
timeSequence
.onValue(function()
{
console.log(require('moment')().format('MMMM Do YYYY, h:mm:ss'));
// print timestamps every second
});
Then,
I want to map the the fibSequence onto timeSequence such as
var mySequence = fibSequence.map(timeSequence);
or
var mySequence = timeSequence.map(fibSequence);
Is it possible?
If so, please show me the way.
Any workaround solution is welcome.
Thanks.
EDIT working code:
//to simplify use Natrual, instead of Fib
var _ = require('lazy.js');
var __ = require('baconjs');
var natural = function(n)
{
return n;
};
var _natural = _.generate(natural); //natural numbers
var __timer = __.interval(1000); //every second
var map_to__ = function(_seq, __seq)
{
var it = _seq.getIterator();
var sequence =
__seq
.map(function()
{
it.moveNext();
return it.current();
});
return sequence;
};
var __mappedTimer = map_to__(_natural, __timer);
__mappedTimer
.onValue(function(x)
{
console.log(x); // print every second
});
I'm not sure whether this is the intended use of iterators, but it should work:
var it = fibSequence.getIterator()
var mySequence = timeSequence.map(function() {
return it.moveNext() && it.current();
});

Named parameter in Javascript without overriding the existing values

Here is the code that I got from this Named parameters in javascript:
var parameterfy = (function () {
var pattern = /function[^(]*\(([^)]*)\)/;
return function (func) {
// fails horribly for parameterless functions ;)
var args = func.toString().match(pattern)[1].split(/,\s*/);
return function () {
var named_params = arguments[arguments.length - 1];
if (typeof named_params === 'object') {
var params = [].slice.call(arguments, 0, -1);
if (params.length < args.length) {
for (var i = params.length, l = args.length; i < l; i++) {
params.push(named_params[args[i]]);
}
return func.apply(this, params);
}
}
return func.apply(null, arguments);
};
};
}());
var myObject = {
first: "",
second: "",
third: ""
};
var foo = parameterfy(function (a, b, c) {
//console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
myObject.first = a;
myObject.second = b;
myObject.third = c;
console.log("first " + myObject.first + " second " + myObject.second + " third " + myObject.third);
});
foo(1, 2, 3); // gives 1, 2, 3
foo({a: 11, c: 13}); // gives 11, undefined, 13
foo({ a: 11, b:myObject.second, c: 13 }); // in order to avoid undefined, this is
Note that, in second instance of foo, I got undefined as I didn't pass b so I had to work around using third instance where I passed the current value of b.
Is there anyway to make it so that if I don't have to pass a value, for example, value of b in this case so that it still updates the given values of a and c but retains the value of b?
Something like the below may work:
var foo = parameterfy(function (a, b, c) {
//console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
if(typeof a != 'undefined'){myObject.first = a;}
if(typeof b != 'undefined'){myObject.second = b;}
if(typeof c != 'undefined'){myObject.third = c;}
console.log("first " + myObject.first + " second " + myObject.second + " third " + myObject.third);
});
Here's the named parameter standard that has been successfully used for years and you should stick with it:
function myFunction(options) {
console.log(options.first);
console.log(options.second);
console.log(options.third);
}
myFunction({
first: 1,
second: 2,
third: 3
});

Categories