This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
You have an array of arbitrary values. Write a transform function in the global scope that will transform the array to an array of functions that return the original values, so instead of calling a[3], we will call a3.
For example I want:
var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
a[1]; // 24
b[1](); // 24
However I am getting:
b
[function () {
return this.temp;
}, function () {
return this.temp;
}, function () {
return this.temp;
}]
Here is my code:
var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
document.writeln(a[1]); // 24
document.writeln(b[0]()); // 24
document.writeln(b[1]()); // 24
function transform(array) {
b = [];
var i;
for (i = 0; i < array.length; i += 1) {
b[i] = function () {
return temp;
};
}
return b;
}
function transform(array) {
b = [];
var i;
for (i = 0; i < array.length; i += 1) {
b[i] = (function (x) {
return function () { return x; };
})(array[i]);
}
return b;
}
demo http://jsbin.com/udasoj/1/edit
Your code is correct if you call b[0](), then you should get the value returned.
Note that none of the existing answers will quite work; they will all produce functions that return that last value in a. var is function-scoped and interacts poorly with closures.
See this other answer I just wrote, which explains a similar problem: https://stackoverflow.com/a/14330595/17875
What is temp? It looks like the way to accomplish this is to generate the function via another function:
function transform(array) {
b = [];
var i;
for (i = 0; i < array.length; i += 1) {
b[i] = createReturnFunction(a[i]);
};
}
return b;
}
function createReturnFunction(value){
return function(){ return value; };
}
Working Fiddle: http://jsfiddle.net/eFWyf/
Related
I have a function 'sometimes' and want to return a function object from it.
Below is my code:
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes (inputFunc){
return function (a, b){
return inputFunc()
}
}
outputArr
I expect my outputArr variable to equal:
[5, 7, 9]
Instead mine equals:
[ NaN, NaN, NaN ]
What am I doing wrong?
You are not passing the parameters to the wrapped function. The add function tries to sum two undefined values and the result is NaN (not a number).
You have to pass the parameters to the wrapped function:
return function(a, b) {
return inputFunc(a, b); // <<<
}
Since sometimes is a higher order function, that needs to wrap different functions, with a changing number of parameters, it can't know the implementation of the wrapped function. To ensure that, you should use rest parameters (to collect the parameters to an array) and spread (to convert the array back to parameters) to pass the arguments to the wrapped function.
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes(inputFunc) {
return function(...args) {
return inputFunc(...args)
}
}
console.log(outputArr);
you can use your code as
function sometimes(a,b){
return a + b;
}
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(sometimes(2 + i, 3 + i));
}
console.log(outputArr);
now the output is
[ 5, 7, 9 ]
I am very new to arguments object concept in JavaScript and trying to understand the concept from this tutorial.
The below example from the doc explains as follows.
function func1(a, b, c) {
console.log(arguments[0]);
// expected output: 1
console.logr(arguments[1]);
// expected output: 2
console.log(arguments[2]);
// expected output: 3
}
func1(1, 2, 3);
But I am unable to understand the importance of this concept and in which scenario we mostly use such concept?
Excuse me for question format issues as I am typing in mobile.
The arguments object is useful in cases you have possibly a variable number of arguments:
function sumAll() {
var res = 0;
for (var i = 0; i < arguments.length; i++) {
res += arguments[i];
}
return res;
}
console.log(sumAll(3)); // 3
console.log(sumAll(3, 4, 5)); // 12
However, since ES6, a version of javascript standardized in 2015 ( also called ES2015), you can use the rest parameter syntax to simplify this operation:
function sumAll(...args) {
var res = 0;
for (var i = 0; i < args.length; i++) {
res += args[i];
}
return res;
}
console.log(sumAll(3)); // 3
console.log(sumAll(3, 4, 5)); // 12
The arguments object is therefore no longer necessary.
function summIt(){
let result = 0;
let args = [...arguments];
args.forEach(el => result += el);
return result;
}
console.log(summIt(4,7,4,8,9,3,3)); //38
OR
const summ = (...args) => {
let result = 0;
Array.from(args).forEach(el => result += el);
return result;
}
console.log(summ(4,7,4,8,9,3,3)); //38
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
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.
Let's say I have a JavaScript object:
function a(){
var A = [];
this.length = function(){
return A.length;
};
this.add = function(x){
A.push(x);
};
this.remove = function(){
return A.pop();
};
};
I can use it like so:
var x = new a();
x.add(3);
x.add(4);
alert(x.length()); // 2
alert(x.remove()); // 4
alert(x.length()); // 1
I was trying to make .length not a function, so I could access it like this: x.length, but I've had no luck in getting this to work.
I tried this, but it outputs 0, because that's the length of A at the time:
function a(){
var A = [];
this.length = A.length;
//rest of the function...
};
I also tried this, and it also outputs 0:
function a(){
var A = [];
this.length = function(){
return A.length;
}();
//rest of the function...
};
How do I get x.length to output the correct length of the array inside in the object?
You could use the valueOf hack:
this.length = {
'valueOf': function (){
return A.length;
},
'toString': function (){
return A.length;
}
};
Now you can access the length as x.length. (Although, maybe it's just me, but to me, something about this method feels very roundabout, and it's easy enough to go with a sturdier solution and, for example, update the length property after every modification.)
If you want A to stay 'private', you need to update the public length property on every operation which modifies A's length so that you don't need a method which checks when asked. I would do so via 'private' method.
Code:
var a = function(){
var instance, A, updateLength;
instance = this;
A = [];
this.length = 0;
updateLength = function()
{
instance.length = A.length;
}
this.add = function(x){
A.push(x);
updateLength();
};
this.remove = function(){
var popped = A.pop();
updateLength();
return popped;
};
};
Demo:
http://jsfiddle.net/JAAulde/VT4bb/
Because when you call a.length, you're returning a function. In order to return the output you have to actually invoke the function, i.e.: a.length().
As an aside, if you don't want to have the length property be a function but the actual value, you will need to modify your object to return the property.
function a() {
var A = [];
this.length = 0;
this.add = function(x) {
A.push(x);
this.length = A.length;
};
this.remove = function() {
var removed = A.pop();
this.length = A.length;
return removed;
};
};
While what everyone has said is true about ES3, that length must be a function (otherwise it's value will remain static, unless you hack it to be otherwise), you can have what you want in ES5 (try this in chrome for example):
function a(){
var A = [],
newA = {
get length(){ return A.length;}
};
newA.add = function(x){
A.push(x);
};
newA.remove = function(){
return A.pop();
};
return newA;
}
var x = a();
x.add(3);
x.add(4);
alert(x.length); // 2
alert(x.remove()); // 4
alert(x.length); // 1
You should probably use Object.create instead of the function a, although I've left it as a function to look like your original.
I don't think you can access it as a variable as a variable to my knoledge cannot return the value of a method, unless you will hijack the array object and start hacking in an update of your variable when the push/pop methods are called (ugly!). In order to make your method version work I think you should do the following:
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};
These days you can use defineProperty:
let x = {}
Object.defineProperty(x, 'length', {
get() {
return Object.keys(this).length
},
})
x.length // 0
x.foo = 'bar'
x.length // 1
Or in your specific case:
Object.defineProperty(x, 'length', {
get() {
return A.length
}
})
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};