59

I was wondering if anyone has a good, working example of a circular reference in javascript? I know this is incredibly easy to do with closures, but have had a hard time wrapping my brain around this. An example that I can dissect in Firebug would be most appreciated.

Thanks

MatthewJ
  • 3,127
  • 2
  • 27
  • 34
  • Thanks for the answers; I can see Josh's example happening in a production app where I might have many bound events. I would love to prevent this leakage from happening on my clients that are running IE6. Correct me if I'm wrong but this is only an issue with browsers IE6 and below? Most modern browsers implement a garbage collector capable of finding these type of references? – MatthewJ Sep 29 '09 at 18:20

8 Answers8

70

A simple way to create a circular reference is to have an object that refers to itself in a property:

function Foo() {
  this.abc = "Hello";
  this.circular = this;
}

var foo = new Foo();
alert(foo.circular.circular.circular.circular.circular.abc);

Here the foo object contains a reference to itself.

With closures this is usually more implicit, by just having the circular reference in scope, not as an explicit property of some object:

var circular;

circular = function(arg) {
  if (arg) {
    alert(arg);
  }
  else {
    // refers to the |circular| variable, and by that to itself.
    circular("No argument");
  }
}

circular("hello");
circular();

Here the function saved in circular refers to the circular variable, and thereby to itself. It implicitly holds a reference to itself, creating a circular reference. Even if circular now goes out of scope, it is still referenced from the functions scope. Simple garbage collectors won't recognize this loop and won't collect the function.

sth
  • 222,467
  • 53
  • 283
  • 367
  • Thanks for the explanation sth. It's a very simple example that's easy to understand. – MatthewJ Sep 29 '09 at 18:40
  • What is the benefit of "Simple garbage collectors won't recognize this loop and won't collect the function"? In your example here, does it mean `circular` is always accessible and can be updated? I ask because my API has a circular object that carries database transaction context, so it seems like it would be updated all the time with new context info, so avoiding garbage collection seems like a good plan. Am I close? – agm1984 Jun 24 '17 at 18:57
  • And just for some extra complete information, I noticed that it throws a circular reference error if I try to snapshot it with `console.log(JSON.stringify(circular))`. I'm curious why I can't view it in this manner to see the current context. – agm1984 Jun 24 '17 at 18:59
  • 1
    "*Simple garbage collectors won't recognize this loop and won't collect the function.*" - C'mon, something that isn't able to deal with reference circles can't earn the name *garbage collector*. – Bergi Oct 13 '17 at 13:42
24

Or even simpler, an array "containing" itself. See example:

var arr = [];
arr[0] = arr;
Alan Kis
  • 1,790
  • 4
  • 24
  • 47
19

Probably the shortest way to define a cyclic object.

a = {}; a.a = a;
cychoi
  • 2,890
  • 3
  • 22
  • 31
11
window.onload = function() {
  hookup(document.getElementById('menu'));

  function hookup(elem) {
    elem.attachEvent( "onmouseover", mouse);

    function mouse() {
    }
  }
}

As you can see, the handler is nested within the attacher, which means it is closed over the scope of the caller.

Josh Stodola
  • 81,538
  • 47
  • 180
  • 227
  • Thanks Josh, seems like a realistic example of what could happen in an actual app – MatthewJ Sep 29 '09 at 18:40
  • 1
    @Josh Stodola can you explain why this is a problem? I'm tried to dissect this code and understand why this would cause a memory leak. Thanks. – Amir May 28 '16 at 13:30
  • @Amir in order to attach mouse() function DOM object must refer to whole hookup function and attachEvent is inside this hookup function and this makes circular reference more details here https://support.microsoft.com/en-gb/kb/830555 – Zgr3doo Oct 27 '16 at 12:50
4

Or using ES6:

class Circular {
  constructor() {
    this.value = "Hello World";
    this.self = this;
  }
}

circular = new Circular();
codeepic
  • 3,723
  • 7
  • 36
  • 57
4

You can do:

  • window.window...window
  • var circle = {}; circle.circle = circle;
  • var circle = []; circle[0] = circle; or circle.push(circle)
  • function Circle(){this.self = this}; var circle = new Circle()
1
var b = [];
var a = [];
a[0] = b;
b[0] = a;

Printing a or b would return Circular.

Prasad Jadhav
  • 5,090
  • 16
  • 62
  • 80
Psi
  • 21
  • 1
  • 7
    That is a circular reference, but what do you mean by "printing"? Your answer implies the JS engine would actually return the string "Circular"... – nnnnnn Oct 21 '12 at 07:17
  • 1
    He means if you `console.log(a)` then it prints `[ [ [Circular] ] ]`. – Miguel Mota Jan 15 '16 at 21:51
1
function circular(arg){
    var count = 0;

    function next(arg){
        count++;
        if(count > 10) return;
        if(arg){
            console.log('hava arg: ' + arg);
            next();
        }else{
            console.log('no arg');
            next('add');
        }
    }
    next();
}
circular();

Circular and with a closures.

Liucw
  • 13
  • 3