JSON and scope. Using a closure - javascript

I am trying to understand why the variable index is being updated ( added and subtracted ) when my function returns an object.
var init = (function() {
var index = 0;
return function() {
return {
subtract: index -= 1,
add: index = index + 1,
getIndex: index
}
}
})();
console.log(init().getIndex); // 1
console.log(init().add); // 2
console.log(init().getIndex); //2
Instead 0 is returned. This is because when the object is returned all of the properties in that returned object are execute. SO my question is how do i prevent that from happening.

I highly doubt it returns 0. It should return undefined:
var f = init();
// f is now the returned function. Therefore:
f.getIndex; // should be undefined
f().getIndex; // should be 1
Therefore, the to get the expected output, change your code to:
console.log(init()().getIndex); // 1
console.log(init()().add); // 2
console.log(init()().getIndex); //2

var init = (function() {
var index = 0;
return function() {
return {
subtract: function() { return --index; },
add: function() { return ++index; },
getIndex: function() { return index; }
}
}
})();
console.log(init().getIndex()); // 0
console.log(init().add()); // 1
console.log(init().getIndex()); // 1

subtract, add and getIndex aren't being initiated as functions. They are receiving the values -1, 0 and 0.
To return operations set
var init = (function() {
var index = 0;
return {
subtract: function () { index -= 1 },
add: function () { index + 1 }, // Should probably be += here
getIndex: function () { return index; }
}
}();

Related

How to limit function calls in JS?

I need a function limitCalls (fn, maxCalls) that takes a function fn and returns a new function that can be called no more than the number of times specified in maxCalls. Test example:
it('limitCalls', () => {
const makeIncrement = () => {
let count = 0;
return () => {
count += 1;
return count;
};
};
const limitedIncrementA = limitCalls(makeIncrement(), 3);
expect(limitedIncrementA()).toBe(1);
expect(limitedIncrementA()).toBe(2);
expect(limitedIncrementA()).toBe(3);
expect(limitedIncrementA()).toBe(undefined);
expect(limitedIncrementA()).toBe(undefined);
const limitedIncrementB = limitCalls(makeIncrement(), 1);
expect(limitedIncrementB()).toBe(1);
expect(limitedIncrementB()).toBe(undefined);
expect(limitedIncrementB()).toBe(undefined);
});
I have:
var calls = 0;
export default function limitCalls(fn, maxCalls) {
if (calls >= maxCalls) {
return undefined;
}
calls += 1;
return fn();
}
And error is limitedIncrementA is not a function. Help me please to realise it.
Instead of conditionally returning a function, always return a function that conditionally executes the fn callback:
function limitCalls(fn, maxCalls) {
let count = 0;
return function(...args) {
return count++ < maxCalls ? fn(...args) : undefined;
}
}
const limited = limitCalls(console.log, 3);
limited('one');
limited('two');
limited('three');
limited('four');
In this snippet, limitedIncrementA isn't indeed a function. See this:
/* You're calling makeIncrement,
so you're passing its return to 'limitCalls'
*/
const limitedIncrementA = limitCalls(makeIncrement(), 3);
/* Here, considering that makeIncrement exists,
you're passing a reference to this functions,
which can be called inside 'limitCalls'
*/
const limitedIncrementB = limitCalls(makeIncrement, 3);
So, supposing that makeIncrement returns 1, 2, 3, ..., your current code is equivalent to:
limitCalls(1, 3);

Is it possible to use module pattern for more objects

var modularpattern = (function () {
var sum = 0;
return {
add: function () {
sum = sum + 1;
return sum;
},
}
} ());
var c = modularpattern;
c.add(); // 1
var d = modularpattern;
d.add(); // 2 but I want to be 1
console.log(modularpattern.add()); // alerts: 3
Is it possible to have more objects not only one? I want to have private fields but at the same time also having more that just one object?
Yes, that's easily possible by dropping the IIFE invocation to get a normal function instead. Only it's called factory pattern then, no longer module.
function factory() {
var sum = 0;
return {
add: function () {
sum = sum + 1;
return sum;
}
}
}
var c = factory();
c.add(); // 1
var d = factory();
d.add(); // 1
console.log(c.add()); // logs: 2
You can use the module pattern to create a factory which uses the module pattern to create more objects. Using your original example, it would look something like this:
var moduleFactory = (function() {
return {
create: function() {
return (function() {
var sum = 0;
return {
add: function() {
sum = sum + 1;
return sum;
}
}
})();
}
}
}
)();
var c = moduleFactory.create();
console.log(c.add()); //1
var d = moduleFactory.create();
console.log(d.add()); //1

How to do lazy evaluation of an array being processed by a for loop

window.onload = function () {
x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:x, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = myArray[i].a + myArray[i].b;
}
alert(x); // alerts '';
}
Hi, the above is an example of what I'm trying to do. Basically, I would like for x to be evaluated after the 2nd array element computes it. I think this is called lazy evaluation, but not sure... I'm somewhat new.
How can I process my array in the loop and x be evaluated each time such that when I get to the third iteration, x = 'cd' and will alert as 'cd'?
I think I figured out the answer with your help and the other thread I mentioned in the comment. Just need to wrap x in a function and then define a get function to apply to all elements:
window.onload = function () {
function get(e) {return (typeof e === 'function') ? e () : e; }
var x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:function() {return x; }, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = get(myArray[i].a) + get(myArray[i].b);
}
alert(x); // alerts 'cd';
}
x can be anything then. For example (x + 'xyz') will alert 'cdxyz'. So this way I can have any variable that I want evaluated later (when needed) be evaluated correctly (based on state at that point).
That's what I needed. :)
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "ace"
You can use a closure so you can't access the values, like:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function make_getter(list) {
return {
get: function (num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
};
}
var getter = make_getter(elements);
console.log(getter.get(0).a); // "a"
console.log(getter.get(1).a); // "ac"
console.log(getter.get(2).a); // "ace"
You can make different implementations of the aggregation function.
With recursion:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = list[num];
if (num > 0) {
agg.a = getter(list, num-1).a + agg.a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "aace" <-- note, elements are actually modified!
console.log(getter(elements, 2).a); // "aaacaace" <-- note, elements are actually modified!
old answer
Since x is not an object it's value will be copied, rather than passed as a reference.
If you change your code to:
var element = { a: '', b:'' };
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, element ];
for (i = 0; i < myArray.length; i += 1) {
element.a = myArray[i].a + myArray[i].b;
}
alert(el.a); // alerts 'cd';
You will get "cd".
This is not called lazy evaluation by the way. It's just an aggregate or something.

How to create constructor function Count?

Function count() return 1, 2, 3... How to create constructor function Count?
var count = new Count();
count(); // 1
count(); // 2
function Count() {
var c = 1;
return function() {
return c++;
}
};
var count = new Count(); // count is now a function that adds and returns
count(); // 1
count(); // 2
function Count() {
this.x = 0;
this.count = function() {
return (this.x += 1);
};
}
counter = new Count();
counter.count(); //1
counter.count(); //2
Declare a variable globally:
var counter=0;
Create a function to return the value:
function count() {
return ++counter;
}
A constructor needs to return an object for the most part,
so returning a number in the constructor will just return the instance of that function,
not the number. You can explicitly return an object, however, so the closest you can get is:
;(function() {
var count = 0;
window.Count = function() {
return new Number(count += 1);
}
})()
var a = +new Count // 1
var b = +new Count // 2
Of course, you could just do:
window.count = (function(){
var i = 0;
return function() {
return i += 1;
}
})()
var a = count() // 1
var b = count() // 2
Which makes more sense in most cases.

JavaScript dictionary with names

I want to create a dictionary in JavaScript like the following:
myMappings = [
{ "Name": 10%},
{ "Phone": 10%},
{ "Address": 50%},
{ "Zip": 10%},
{ "Comments": 20%}
]
I want to populate an HTML table later and want to set the titles of table to the first column of myMappings and the width of columns to the second. Is there a clean way to do it?
Another approach would be to have an array of objects, with each individual object holding the properties of a column. This slightly changes the structure of "myMappings", but makes it easy to work with:
var myMappings = [
{ title: "Name", width: "10%" },
{ title: "Phone", width: "10%" },
{ title: "Address", width: "50%" },
{ title: "Zip", width: "10%" },
{ title: "Comments", width: "20%" }
];
Then you could easily iterate through all your "columns" with a for loop:
for (var i = 0; i < myMappings.length; i += 1) {
// myMappings[i].title ...
// myMappings[i].width ...
}
The main problem I see with what you have is that it's difficult to loop through, for populating a table.
Simply use an array of arrays:
var myMappings = [
["Name", "10%"], // Note the quotes around "10%"
["Phone", "10%"],
// etc..
];
... which simplifies access:
myMappings[0][0]; // column name
myMappings[0][1]; // column width
Alternatively:
var myMappings = {
names: ["Name", "Phone", etc...],
widths: ["10%", "10%", etc...]
};
And access with:
myMappings.names[0];
myMappings.widths[0];
An object technically is a dictionary.
var myMappings = {
mykey1: 'myValue',
mykey2: 'myValue'
};
var myVal = myMappings['myKey1'];
alert(myVal); // myValue
You can even loop through one.
for(var key in myMappings) {
var myVal = myMappings[key];
alert(myVal);
}
There is no reason whatsoever to reinvent the wheel. And of course, assignment goes like:
myMappings['mykey3'] = 'my value';
And ContainsKey:
if (myMappings.hasOwnProperty('myKey3')) {
alert('key already exists!');
}
I suggest you follow this: http://javascriptissexy.com/how-to-learn-javascript-properly/
You may be trying to use a JSON object:
var myMappings = { "name": "10%", "phone": "10%", "address": "50%", etc.. }
To access:
myMappings.name;
myMappings.phone;
etc..
Try:
var myMappings = {
"Name": "10%",
"Phone": "10%",
"Address": "50%",
"Zip": "10%",
"Comments": "20%"
}
// Access is like this
myMappings["Name"] // Returns "10%"
myMappings.Name // The same thing as above
// To loop through...
for(var title in myMappings) {
// Do whatever with myMappings[title]
}
I suggest not using an array unless you have multiple objects to consider. There isn't anything wrong this statement:
var myMappings = {
"Name": 0.1,
"Phone": 0.1,
"Address": 0.5,
"Zip": 0.1,
"Comments": 0.2
};
for (var col in myMappings) {
alert((myMappings[col] * 100) + "%");
}
An easier, native and more efficient way of emulating a dict in JavaScript than a hash table:
It also exploits that JavaScript is weakly typed. Rather type inference.
Here's how (an excerpt from Google Chrome's console):
var myDict = {};
myDict.one = 1;
1
myDict.two = 2;
2
if (myDict.hasOwnProperty('three'))
{
console.log(myDict.two);
}
else
{
console.log('Key does not exist!');
}
Key does not exist! VM361:8
if (myDict.hasOwnProperty('two'))
{
console.log(myDict.two);
}
else
{
console.log('Key does not exist!');
}
2 VM362:4
Object.keys(myDict);
["one", "two"]
delete(myDict.two);
true
myDict.hasOwnProperty('two');
false
myDict.two
undefined
myDict.one
1
Here's a dictionary that will take any type of key as long as the toString() property returns unique values. The dictionary uses anything as the value for the key value pair.
See Would JavaScript Benefit from a Dictionary Object.
To use the dictionary as is:
var dictFact = new Dict();
var myDict = dictFact.New();
myDict.addOrUpdate("key1", "Value1");
myDict.addOrUpdate("key2", "Value2");
myDict.addOrUpdate("keyN", "ValueN");
The dictionary code is below:
/*
* Dictionary Factory Object
* Holds common object functions. similar to V-Table
* this.New() used to create new dictionary objects
* Uses Object.defineProperties so won't work on older browsers.
* Browser Compatibility (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties)
* Firefox (Gecko) 4.0 (2), Chrome 5, Internet Explorer 9, Opera 11.60, Safari 5
*/
function Dict() {
/*
* Create a new Dictionary
*/
this.New = function () {
return new dict();
};
/*
* Return argument f if it is a function otherwise return undefined
*/
function ensureF(f) {
if (isFunct(f)) {
return f;
}
}
function isFunct(f) {
return (typeof f == "function");
}
/*
* Add a "_" as first character just to be sure valid property name
*/
function makeKey(k) {
return "_" + k;
};
/*
* Key Value Pair object - held in array
*/
function newkvp(key, value) {
return {
key: key,
value: value,
toString: function () { return this.key; },
valueOf: function () { return this.key; }
};
};
/*
* Return the current set of keys.
*/
function keys(a) {
// remove the leading "-" character from the keys
return a.map(function (e) { return e.key.substr(1); });
// Alternative: Requires Opera 12 vs. 11.60
// -- Must pass the internal object instead of the array
// -- Still need to remove the leading "-" to return user key values
// Object.keys(o).map(function (e) { return e.key.substr(1); });
};
/*
* Return the current set of values.
*/
function values(a) {
return a.map(function(e) { return e.value; } );
};
/*
* Return the current set of key value pairs.
*/
function kvPs(a) {
// Remove the leading "-" character from the keys
return a.map(function (e) { return newkvp(e.key.substr(1), e.value); });
}
/*
* Returns true if key exists in the dictionary.
* k - Key to check (with the leading "_" character)
*/
function exists(k, o) {
return o.hasOwnProperty(k);
}
/*
* Array Map implementation
*/
function map(a, f) {
if (!isFunct(f)) { return; }
return a.map(function (e, i) { return f(e.value, i); });
}
/*
* Array Every implementation
*/
function every(a, f) {
if (!isFunct(f)) { return; }
return a.every(function (e, i) { return f(e.value, i) });
}
/*
* Returns subset of "values" where function "f" returns true for the "value"
*/
function filter(a, f) {
if (!isFunct(f)) {return; }
var ret = a.filter(function (e, i) { return f(e.value, i); });
// if anything returned by array.filter, then get the "values" from the key value pairs
if (ret && ret.length > 0) {
ret = values(ret);
}
return ret;
}
/*
* Array Reverse implementation
*/
function reverse(a, o) {
a.reverse();
reindex(a, o, 0);
}
/**
* Randomize array element order in-place.
* Using Fisher-Yates shuffle algorithm.
*/
function shuffle(a, o) {
var j, t;
for (var i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
t = a[i];
a[i] = a[j];
a[j] = t;
}
reindex(a, o, 0);
return a;
}
/*
* Array Some implementation
*/
function some(a, f) {
if (!isFunct(f)) { return; }
return a.some(function (e, i) { return f(e.value, i) });
}
/*
* Sort the dictionary. Sorts the array and reindexes the object.
* a - dictionary array
* o - dictionary object
* sf - dictionary default sort function (can be undefined)
* f - sort method sort function argument (can be undefined)
*/
function sort(a, o, sf, f) {
var sf1 = f || sf; // sort function method used if not undefined
// if there is a customer sort function, use it
if (isFunct(sf1)) {
a.sort(function (e1, e2) { return sf1(e1.value, e2.value); });
}
else {
// sort by key values
a.sort();
}
// reindex - adds O(n) to perf
reindex(a, o, 0);
// return sorted values (not entire array)
// adds O(n) to perf
return values(a);
};
/*
* forEach iteration of "values"
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEach(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for(var i = 0; i < a.length; i++) {
if (f(a[i].value, i)) { break; }
}
};
/*
* forEachR iteration of "values" in reverse order
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEachR(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for (var i = a.length - 1; i > -1; i--) {
if (f(a[i].value, i)) { break; }
}
}
/*
* Add a new Key Value Pair, or update the value of an existing key value pair
*/
function add(key, value, a, o, resort, sf) {
var k = makeKey(key);
// Update value if key exists
if (exists(k, o)) {
a[o[k]].value = value;
}
else {
// Add a new Key value Pair
var kvp = newkvp(k, value);
o[kvp.key] = a.length;
a.push(kvp);
}
// resort if requested
if (resort) { sort(a, o, sf); }
};
/*
* Removes an existing key value pair and returns the "value" If the key does not exists, returns undefined
*/
function remove(key, a, o) {
var k = makeKey(key);
// return undefined if the key does not exist
if (!exists(k, o)) { return; }
// get the array index
var i = o[k];
// get the key value pair
var ret = a[i];
// remove the array element
a.splice(i, 1);
// remove the object property
delete o[k];
// reindex the object properties from the remove element to end of the array
reindex(a, o, i);
// return the removed value
return ret.value;
};
/*
* Returns true if key exists in the dictionary.
* k - Key to check (without the leading "_" character)
*/
function keyExists(k, o) {
return exists(makeKey(k), o);
};
/*
* Returns value assocated with "key". Returns undefined if key not found
*/
function item(key, a, o) {
var k = makeKey(key);
if (exists(k, o)) {
return a[o[k]].value;
}
}
/*
* changes index values held by object properties to match the array index location
* Called after sorting or removing
*/
function reindex(a, o, i){
for (var j = i; j < a.length; j++) {
o[a[j].key] = j;
}
}
/*
* The "real dictionary"
*/
function dict() {
var _a = [];
var _o = {};
var _sortF;
Object.defineProperties(this, {
"length": { get: function () { return _a.length; }, enumerable: true },
"keys": { get: function() { return keys(_a); }, enumerable: true },
"values": { get: function() { return values(_a); }, enumerable: true },
"keyValuePairs": { get: function() { return kvPs(_a); }, enumerable: true},
"sortFunction": { get: function() { return _sortF; }, set: function(funct) { _sortF = ensureF(funct); }, enumerable: true }
});
// Array Methods - Only modification to not pass the actual array to the callback function
this.map = function(funct) { return map(_a, funct); };
this.every = function(funct) { return every(_a, funct); };
this.filter = function(funct) { return filter(_a, funct); };
this.reverse = function() { reverse(_a, _o); };
this.shuffle = function () { return shuffle(_a, _o); };
this.some = function(funct) { return some(_a, funct); };
this.sort = function(funct) { return sort(_a, _o, _sortF, funct); };
// Array Methods - Modified aborts when funct returns true.
this.forEach = function (funct) { forEach(_a, funct) };
// forEach in reverse order
this.forEachRev = function (funct) { forEachR(_a, funct) };
// Dictionary Methods
this.addOrUpdate = function(key, value, resort) { return add(key, value, _a, _o, resort, _sortF); };
this.remove = function(key) { return remove(key, _a, _o); };
this.exists = function(key) { return keyExists(key, _o); };
this.item = function(key) { return item(key, _a, _o); };
this.get = function (index) { if (index > -1 && index < _a.length) { return _a[index].value; } } ,
this.clear = function() { _a = []; _o = {}; };
return this;
}
return this;
}

Categories

Resources