1

So the code explains things pretty well I think. I am using a closure to keep a method called reverseAll private. reverseAll is recursive but you don't need to worry about that I think. I am trying to reference this.head in the reverseAll function but I found it was undefined. So I passed in a reference to this.head and keep passing it through the recursive calls. Alas, it was not meant to be. I can just pull the reverseAll method out of the closure and I will have a reference to this.head. But I would like to know why my this.head reference I pass in is not a "copy of a reference" or "a copy of a pointer" if you will like Javascript does when you pass in an object to a function. this.head is a node object by the way.

Here is the code (reference to jQuery is because the Stackoverflow snippet "IDE" failed when on line var obj = new LinkedList(); so I added a document.ready, jQuery not needed for any reason other than that:

function LinkedList() {
    this.head = null;
};

LinkedList.prototype = (function () {

    function reverseAll(current, prev, theHead) {

        if (!current.next) { //we have the head
            console.log('ending recursion, new head!!');
            console.log('we have a refence to this.head in theHead or so I thought:');
            console.log(theHead);
            theHead = current;
            theHead.next = prev;
            console.log('theHead has a new "pointer":');
            console.log(theHead);
            return;
        }

        var next = current.next;
        current.next = prev;

        //keep passing the theHead reference through recursion
        reverseAll(next, current, theHead);
    };

    return {
        constructor: LinkedList,

        reverse: function () {
            console.log('clone head to iterate and change');
            console.log('but also pass in reference of this.head obj as this.head is a node obj and this.head will be undefined in reverseAll()');
            var headClone = JSON.parse(JSON.stringify(this.head));
            reverseAll(headClone, null, this.head);
        }
    }
})();

LinkedList.prototype.add = function(value) {
    var node = {
        value: value,
        next: null
    };

    var current;

    if (this.head === null) {
        this.head = node;
    } else {
        current = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = node;
    }

    return node;
}

LinkedList.prototype.remove = function(node) {
    var current, value = node.value;

    if (this.head !== null) {
        if (this.head === node) {
            this.head = this.head.next;
            node.next = null;
            return value;
        }
        //find node if node not head
        current = this.head;
        while (current.next) {
            if (current.next === node) {
                current.next = node.next;
                return value;
            }

            current = current.next;
        }
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(function() {
    var obj = new LinkedList();
    
    for (var i = 1; i <= 10; i++) {
        obj.add(i);
    }

    console.log('about to call obj.reverse()!');
    console.log('obj.head:');
    console.log(obj.head);
    obj.reverse();

    console.log('obj instance obj.head after reverse call, it has not changed!!:');
    console.log(obj.head);

});
</script>
Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
  • Dunno why you included jQuery here, it's entirely unnecessary. – RobG Oct 25 '15 at 21:01
  • Because the snippet would not execute. When I defined var obj = new LinkedList, I got an error that LinkedList didn't exist. I do not know the lifecycle loading of the Stackoverflow Snippet code "IDE" so I just added a document.ready. – Brian Ogden Oct 25 '15 at 21:54
  • Can you please explain how `reverse`/`reverseAll` are supposed to work? I believe your problem lies in the misunderstanding that the `theHead` variable would be a "pointer" to the `this.head` property, and assignments to the variable would be reflected in the property. They are not. – Bergi Oct 25 '15 at 22:00
  • Yes that is correct, I think theHead is a pointer to this.head. I passed this.head to revereseAll which has the signature reverseAll(current, prev, theHead), call reverseAll with arguments reverseAll(headClone, null, this.head). reverseAll how it works really doesn't matter. If you look at my console.log you will see that the value theHead had a value and it is the same value as this.head. But when I change theHead which I thought was a copy of a pointer to a new obj. this.head does not change, the obj instance, obj.head did not change. – Brian Ogden Oct 25 '15 at 22:13
  • Removing from a closure did fix my problem – Brian Ogden Oct 25 '15 at 22:13
  • 1
    @BrianOgden: Indeed, there are no pointers in JS. Every function call is by value, if you change one variable it doesn't magically change something else. Objects are "reference values", i.e. if you mutate them (assign to their properties) the object will change, but putting them into variables/call arguments doesn't copy them. – Bergi Oct 25 '15 at 23:29
  • Thanks @Bergi I like the explanation I just read here: http://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference – Brian Ogden Oct 25 '15 at 23:32
  • 1
    A copy of the reference is passed and I can manipulate the object being referenced but the reference the caller holds to the object will remain unchanged. So, could have directly manipulated the object via the copy of a reference I passed to my reverseAl method and the caller would have experienced those changes and in fact did experience those changes but I was trying to change the reference for the caller to a new object and that wasn't going to fly ;) because I didn't hold the reference the caller did. – Brian Ogden Oct 25 '15 at 23:35
  • 1
    Exactly. So you could either pass `this` completely (not only `this.head`), and change its `.head` property in the function; or you just `return` the value that is supposed to be assigned to `.head` and do it at the call site. – Bergi Oct 25 '15 at 23:37

2 Answers2

1

The actual problem of the current implementation lies in the assignment theHead = current. This does not have any effect on the instance of your LinkedList. A simple fix would be to pass this as the last argument to reverseAll(current, prev, instance) and change the assignment in question to:

instance.head = current;
current.next = prev;

However, I do believe that a better code design would help avoiding this issue all together. Put reverseAll inside of reverse. First it belongs there as it does not serve any other purpose, and second you would not even need to pass this to it, use it from the closure provided you use a name substitute var self = this;. The latter is an advisable pattern to use in other places as well as it helps avoiding trivial errors:

function LinkedList() {
    this.head = null;
}

LinkedList.prototype.reverse = function() {
    var self = this;
    var reverseAll = function(current, prev) {
        var exitCondition = !current.next;
        var next = current.next;
        current.next = prev;

        if (exitCondition) {
            self.head = current;
            return;
        }

        reverseAll(next, current);
    };

    // FIXME this is ugly, but unrelated, so keep it
    var headClone = JSON.parse(JSON.stringify(self.head));
    reverseAll(headClone, null);
};

// remove and add prototypes as in the original

node

> a = new LinkedList();
LinkedList { head: null, reverse: [Function] }
> a.add(1);
{ value: 1, next: null }
> a.add(2);
{ value: 2, next: null }
> a
LinkedList {
  head: { value: 1, next: { value: 2, next: null } },
    reverse: [Function] }
> a.reverse();
undefined
> a
LinkedList {
  head: { value: 2, next: { value: 1, next: null } },
    reverse: [Function] }
> 
Oleg Sklyar
  • 9,834
  • 6
  • 39
  • 62
  • 2
    And contrary to your believe your code is not self-explanatory. – Oleg Sklyar Oct 25 '15 at 20:52
  • Code is self explanatory to me, though I don't know what the code is about under the word "node". Is that NodeJs? I don't know, and don't need to know. naming this self, which I do all the time but just didnt and assigning the reverse all Function to a var, which is something I do not often in Javascript allowed this to be what I expected it to be in my reverseAll function. – Brian Ogden Oct 25 '15 at 21:48
  • To sum, really the big key was simply assigning my reverseAll function to a var so I could execute it in my reverse proto. self = this is of course, always a good thang. Sill don't understand why though when I passed this.head as an argument into the function in the closure I lose the reference – Brian Ogden Oct 25 '15 at 21:52
  • @Oleg: I'm sorry to say but this answer is plain wrong. Nowhere you are using `var self = this` it is necessary or does change anything from just using `this` directly. – Bergi Oct 25 '15 at 21:56
  • @Oleg: Or actually, it is necessary in `reverseAll`, I didn't see that before. However, your explanation suggests that you'd need to use `self` everywhere, which you really don't, and your code uses it a lot in *inappropriate* places. You should rather explain how and why *you changed the `reverseAll` implementation*. – Bergi Oct 25 '15 at 22:04
  • @Oleg: Yes, your code is working, but your explanation of the problem and the solution is wrong. – Bergi Oct 25 '15 at 22:16
  • @RobG I have updated the answer correspondingly and am removing all of my comments in this discussion to clean up the mess here. I will delete this comment later as well. – Oleg Sklyar Oct 26 '15 at 00:03
  • @Oleg: Thank you very much! – Bergi Oct 26 '15 at 00:52
0

Your member value this.headis overwritten by the function you defined in the returned object!

head: function() 
{
  return this.head;
}

is added to the prototype.

Then you instanciate LinkedList and the prototype is used to extend this. this.head is then replaced by the function.

The best you can do is rename the function to getHead for example.

Peter Paul Kiefer
  • 2,114
  • 1
  • 11
  • 16
  • That returned object is assigned to the constructor's prototype, not the instance so when every instance is initialised with `this.head = null`, the inherited *head* method is made unreachable, which is lucky because it's never called. – RobG Oct 25 '15 at 21:11
  • Yes I never used that head method I was playing around and forgot to delete it from my code snippet for my Stack Overflow question, I will remove now. – Brian Ogden Oct 25 '15 at 21:51
  • @RobG Yes, you are right. I had overseen the constructor. Damed scroll wheel ;-). I have to be more attentively. – Peter Paul Kiefer Oct 26 '15 at 07:52