0

I'm trying to write a symbol handling calculator in JavaScript. I iterate through a list of symbols. My sample input is 1+2.

for <every element in the list> {
    ...
    // this case handles simple numbers
    var tmp = o.val;
    list[pos] = {type: 'expression', val: +tmp, calc: function (x) {return +tmp} };

    ...
    // this case handles addition
    var v1 = list[pos-1].val, v2 = list[pos+1].val;
    var f1 = list[pos-1].calc, f2 = list[pos+1].calc;
    list[pos-1] = {type: 'expression', val: v1 + ' + ' + v2, calc: function (x) {return f1(x) + f2(x)} };
    ...
}
alert(list[0].val + '=' + list[0].calc(0));

The problem is that this displays 4 rather than 3. The call to calc() for the first operand doesn't return 1 anymore but 2. I wish 'calc' to hold the current values, to make a deep copy of the f1 and f2 functions. How do I achieve this? What is a good programming practice?

David
  • 943
  • 1
  • 10
  • 26
  • It's not about shallow vs. deep copy. The issue is that all those functions share a reference to the same variable "tmp". See the linked duplicate question for more examples. – Pointy Nov 19 '13 at 13:48

2 Answers2

1

Although the correct explanation has already been linked, this is a simple solution to the problem: You'll have to open a new variable scope to make sure that "tmp" is actually a new variable each time, not the same one over and over again:

for <every element in the list> {
  (function() {
    ... loop body ...
  )();
}
Martin Geisse
  • 1,189
  • 1
  • 9
  • 22
0

Your problem can be reduced to this:

var funcs = [];
for (var i=0; i<2; i++) {
  var temp = i;
  funcs.push(function() { console.log(temp) });
}

funcs[0](); // 1  - expected 0
funcs[1](); // 1

The problem is that your functions create a closure over temp. They work on the actual temp variable, not on a copy of the value of the temp variable when they are defined.

The solution is simple: create a copy of the temp variable (by passing it as parameter to a function):

var funcs = [];
for (var i=0; i<2; i++) {
  var temp = i;
  funcs.push(function(copy) { // anonymous function that copies temp
      return function() { console.log(copy); }
    }(temp) // call the anonymous function
  );
}

funcs[0](); // 0
funcs[1](); // 1
Tibos
  • 27,507
  • 4
  • 50
  • 64