Given this function call:
var funcs = obj.getClosures([2, 4, 6, 8], function(x) {
return x*x;
});
I have the following function:
getClosures : function(arr, fn) {
var funcs = [];
var array = arr;
var i = 0;
var l = array.length;
(function(i, array) {
for (; i < l; i++) {
funcs[i] = function(i, array) {
return fn(array[i]);
};
}
}(i, array));
return funcs;
},
I'd like to be able to loop through the returned array and get the square root values of each item in the array exactly like this:
for (var i = 0; i < arr.length; i++) {
funcs[i]();
}
results each time through loop : 4, 16, 36, 64
Shouldn't my funcs array have a function reference in each index that can be readily invoked with the relevant argument values? Where did I go wrong?
There multiple "issues":
The IIFE (function(i, array) { ... }(i, array)); has no benefit at all here. If you remove it the code will have the exact same behavior. If you want to capture the current value of i and array, you would have to move it inside the for loop.
Your function definition is incorrect.
funcs[i] = function(i, array) {
return fn(array[i]);
};
Inside the function, array[i] will refer to the arguments you pass to the function. If you don't pass any, they will be undefined and the code will throw an error. That is, with that definition, you would have to execute the functions like so:
for (var i = 0; i < arr.length; i++) {
funcs[i](i, array);
}
which kind of defeats the purpose of generating the functions in the first place.
If you want to create a closure which has access to i and array of the scope where the function was defined in, don't define parameters with the same name.
Possible solution:
for (var i = 0, l = array.length; i < l; i++) {
(function(i) {
funcs[i] = function() {
return fn(array[i]);
};
}(i));
}
Or simpler, if your code runs in environments which support .map:
getClosures: function(arr, fn) {
return arr.map(function(v) {
return function() {
fn(v);
};
});
},
Related questions:
JavaScript closure inside loops – simple practical example
How do JavaScript closures work?
Read about Function.prototype.bind:
var obj = {
getClosures : function(arr, fn) {
var funcs = [];
var array = arr;
var i = 0;
var l = array.length;
(function(i, array) {
for (; i < l; i++) {
funcs[i] = function(i, array) {
return fn(array[i]);
}.bind(this,i,array);
}
}(i, array));
return funcs;
}
}
var funcs = obj.getClosures([2, 4, 6, 8], function(x) {
return x*x;
});
for (var i = 0; i < funcs.length; i++) {
console.log(funcs[i]());
}
Outputs:
4
16
36
64
It works since javascript 1.8.5 (firefox 4). I have no idea for other browsers but there is implementation for older versions (should work on older browsers as well)
Related
Here is sth interesting on CoffeeScript I observed.
TLDR: {
We know that the fat arrow (=>) generates a closure saving the reference to this and every reference of # would be substituted with the original value of this. Thus the following coffeescript code:
=>
#sth
would produce the following:
(function(_this) {
return (function() {
return _this.sth;
});
})(this);
Notice the _this.sth.
}
But here is the corner case I found:
=>
for a in sth
for b in #sth
sth
Which evaluates to:
(function(_this) {
return (function() {
var a, b, i, len, results;
results = [];
for (i = 0, len = sth.length; i < len; i++) {
a = sth[i];
results.push((function() {
var j, len1, ref, results1;
ref = this.sth;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
b = ref[j];
results1.push(sth);
}
return results1;
}).call(_this));
}
return results;
});
})(this);
This is a bit long, but the problem is that the inner loop itterates through this.sth instead of _this.sth.
The exact lines of the inner loop are:
ref = this.sth;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
b = ref[j];
Is this the normal behaviour, or is it a bug?
Look at the inner loop more closely:
results.push((function() {
var j, len1, ref, results1;
ref = this.sth;
// Loop stuff goes here...
}).call(_this));
The inner loop is wrapped in a function (as part of the loop comprehension code) which is evaluated using Function.prototype.call:
The call() method calls a function with a given this value and arguments provided individually.
call is called with _this (the stashed/bound # from the =>) so this inside that function is actually _this and all is well.
If you suppress the comprehension code by explicitly returning nothing:
=>
for a in sth
for b in #sth
sth
return
then you'll see the ref = _this.sth that you were originally expecting:
(function(_this) {
return (function() {
var a, b, i, j, len, len1, ref;
for (i = 0, len = sth.length; i < len; i++) {
a = sth[i];
ref = _this.sth; # <---------------------------
for (j = 0, len1 = ref.length; j < len1; j++) {
b = ref[j];
sth;
}
}
});
})(this);
I'd like to know how to make the second script return same value as the first one (by modifying MyObject), so I don't have to extend native objects.
Basically, I want to be able to call the function as parameter.functionName() instead of functionName(parameter).
Object.prototype.arraySum = function()
{
var sum = 0;
for(var i = 0; i < this.length; i++)
{
sum += this[i];
}
return sum;
}
console.log([1,2,3,4].arraySum()); /* return: 10 */
function MyObject()
{}
MyObject.prototype.arraySum = function()
{
var sum = 0;
for(var i = 0; i < this.length; i++)
{
sum += this[i];
}
return sum;
}
console.log([1,2,3,4].arraySum()); /* return: [1,2,3,4].arraySum is not a function */
I've looked through many questions here, but I haven't found the answer.
The array literal constructs an Array instance, there's no way how you could affect that. So to make this work with MyObject, you will need to change the code that invokes it.
A simple solution would a wrapper:
function MyObject(arr) {
this.array = arr;
}
MyObject.prototype.sum = function() {
var sum = 0;
for(var i = 0; i < this.array.length; i++) {
sum += this.array[i];
}
return sum;
};
console.log(new MyObject([1,2,3,4]).sum());
There are many other conveivable approaches, but none can change the fact that [1,2,3,4].arraySum() is not going to work.
console.log([1,2,3,4].arraySum())
[1,2,3,4] is an array and thus inherits from Array.prototype, not from MyObject. So if you really want to have parameter.functionName(), then you have to add arraySum function to Array.prototype.
But extending native prototypes is not a very good practice.
So you can write your own Array class:
function MyArray() {}
MyArray.prototype = new Array(); // all Array method will be available
MyArray.prototype.sum = function() {
console.log("My sum function")
}
var arr = new MyArray(); // it will produce an array-like object
arr.push(1,2,3);
arr.sum() // will log "My sum function"
https://jsfiddle.net/nuwanniroshana/2qoko562/
Array.prototype.sum = function() {
var sum = 0;
this.forEach(function(value) {
sum += value;
});
return sum;
};
var numbers = [1, 2, 3, 4, 5, 6, 7, 8];
var sum = numbers.sum();
console.log(sum);
var floats = [1.3, 85.2, 9.33, 1.24, 24.5, 6];
sum = floats.sum();
console.log(sum);
I've created a simple forEach function and I'm trying to understand why, when I run it with myArray, it doesn't mutate the array even though I run element*2.
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i],i,array)
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){element*2})
console.log(myArray)///[1,2,3]
You have to modify the array in the for loop, like this:
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i],i,array)
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){return element*2})
console.log(myArray)
As we were discussing in the comments, the best way would be to implement something like the map function: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
function map(array, callback) {
var out = [];
for (var i = 0; i < array.length; i++) {
out.push(callback(array[i],i,array))
};
return out;
}
var myArray = [1,2,3]
var myOtherArray = map(myArray,function(element){return element*2})
console.log(myArray)
console.log(myOtherArray)
This way myArray is not touched, you create a new one. This is usually the best option, but sometimes you may want to modify it in place, because it is huge or for some other (good?) reason. In that case you can use the first option.
You should assign new array element value, because primitive types (like numbers in your case) are immutable, so element * 2 does not modify element.
To do the job, you should not touch you current forEach implementation, because forEach is not supposed to return values from callback (this is not map). In this case you should do something like this:
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i], i, array);
}
}
var myArray = [1,2,3];
forEach(myArray, function(element, i, arr) {
arr[i] = element * 2;
});
document.write(JSON.stringify( myArray ));
This should work, explicitly assigning the variable.
function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i])
};
}
var myArray = [1,2,3]
forEach(myArray,function(element){return element*2})
console.log(myArray)///[1,2,3]
Yep, bad answer. This [snippet] would do it though.
Anyway, in modern browsers we have Array.forEach to availability
function foreach(array, callback) {
for (var i = 0; i < array.length; i++) {
array[i] = callback(array[i]);
// ^ assign the new value (from the callback function)
};
}
var myArray = [1,2,3]
foreach( myArray, function (element){ return element * 2 } );
// ^ return the new value
document.querySelector('#result').textContent = myArray;
<pre id="result"></pre>
I am using this snippet to extend Array's functionality with getUnique function:
Array.prototype.getUnique = function() {
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
};
The slight drawback is that the attribute getUnique is printed together with array elements if I console.log an array:
["126", "125", getUnique: function]
It is slightly disturbing even if I know that getUnique is not considered as an array element. This happens in Chrome. Not in Firefox as far as I know. Is there a way to prevent it?
Use Object.defineProperty and enumerable:
Object.defineProperty(Array.prototype, 'getUnique',
{ enumerable: false
, configurable: true
, writable: true
, value: function ()
{ var u = {};
var a = [];
for (var i = 0, l = this.length; i < l; ++i)
{ if (u.hasOwnProperty(this[i])) continue;
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
});
By default, a property is enumerable (even if it's not a direct property of an instance), which means that it is visible through any for-in loop (or Object.keys) of the array. Chrome does something similar to a for-in loop on an array, which is why it is represented as it is.
Note that this function only works in (relatively) modernish browsers which support ES5, which means that IE < 9, some older Android browsers and other mobile devices may not be able to use this code. This is one of the reasons why some people recommend against extending the prototypes of native constructors, as this issue can break code that isn't well constructed against these issues. What people who believe this usually recommend is to not place the function on the prototype:
function getUnique(arr)
{ var u = {};
var a = [];
for (var i = 0, l = array.length; i < l; ++i)
{ if (u.hasOwnProperty(arr[i])) continue;
a.push(arr[i]);
u[arr[i]] = 1;
}
return a;
}
I'd also like to point out that only one object will be ever printed by the getUnique function, as it is technically incorrect in its implementation. While rather similar, objects are not dictionaries and can only have string keys, which means that any object (no matter the contents) will have a string value of '[object Object]', which means that when your function encounters another object (even if it has different values), it will not be added to the returned array. While inefficient, the only way I can think of is to iterate through the array each time:
function getUnique(arg)
{ 'use strict';
var O = Object(arg);
var len = O.length >>> 0;
var A = [];
var indexOf = function (arg, searchElement)
{ for (var i = 0, l = arg.length; i < l; ++i) if (arg[i] === searchElement) return i;
return -1;
};
for (var k = 0; k < len; ++k)
{ var elementK = O[k];
var kPresent = k in O;
if (!kPresent || indexOf(A, elementK) !== -1) continue;
A[A.length - 1] = elementK;
}
return array;
}
I have an array:
deleteIds= ["User1"];
and try to iterate over it as like:
first one:
for (var index = 0; index < len; index++) {
alert(deleteIds[index]);
}
second one:
for (var index in deleteIds) {
alert(deleteIds[index]);
}
What is the performance comparison of them?
This is a micro optimisation at best. Unless you have hundreds of thousands of elements in your deleteIds array, you should not be looking at optimising this.
It turns out that the for(;;;) version is quicker than for in: http://jsperf.com/iterating-over-a-loop/2 (thanks to #Jamiec for the jsperf).
However, what's more important than the performance comparison of these, is that both code snippets are functionally different to each other (live example).
var array = new Array(100);
for (var i = 0; i < array.length; i++) {
console.log(i); // shows 0 - 99
}
for (var x in array) {
console.log(x); // shows nothing
}
Furthermore, if you add methods to either the array, or to an object in the arrays' prototype chain, they will show up in the for (var x in y) loop, but not in for (;;;); (live example):
Array.prototype.foo = function() {};
var array = [];
for (var i = 0; i < array.length; i++) {
console.log(i); // nothing
}
for (var x in array) {
console.log(x); // `foo`
}
You can use hasOwnProperty to eliminate the attributes inherited from the prototype chain, but that won't stop you receiving methods directly on the object (live example):
Array.prototype.foo = function() {};
var array = [];
array.bar = function () {};
for (var i = 0; i < array.length; i++) {
console.log(i); // nothing
}
for (var x in array) {
if (array.hasOwnProperty(x)) {
console.log(x); // `bar`
}
}
It is because of these reasons that using for in for iterating over an array is discouraged, and the for(;;;) version should always be used instead. for in should be used for iterating over an object.