61

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

1
  • 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? Commented Sep 29, 2009 at 18:20

9 Answers 9

73

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the explanation sth. It's a very simple example that's easy to understand.
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?
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.
"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.
26

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

var arr = [];
arr[0] = arr;

Comments

20

Probably the shortest way to define a cyclic object.

a = {}; a.a = a;

Comments

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.

3 Comments

Thanks Josh, seems like a realistic example of what could happen in an actual app
@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 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 support.microsoft.com/en-gb/kb/830555
4

Or using ES6:

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

circular = new Circular();

Comments

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()

Comments

1
var b = [];
var a = [];
a[0] = b;
b[0] = a;

Printing a or b would return Circular.

2 Comments

That is a circular reference, but what do you mean by "printing"? Your answer implies the JS engine would actually return the string "Circular"...
He means if you console.log(a) then it prints [ [ [Circular] ] ].
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.

Comments

0

A real-world example of circular reference

const person1 = {
  name: "x",
  age: 28,
};

const person2 = {
  name: "y",
  age: 26,
};

person1.spouse = person2;
person2.spouse = person1;

console.log(JSON.stringify(person1)); // will throw the error

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.