Where is my YUI 3 instance living? (plus some YUI event observation) - javascript

This question is mostly just out of academic interest.
I started using YUI 3 today and came across its sandbox concept. After thoroughly trying to find some objects (like my registered event handlers) inside the DOM I had to give up. I just couldn't find any instance objects ending up there.
I think I have closures mostly covered: even if the sandbox heavily relied on them, there would have to be at least one visible reference anywhere at least to parts like event handlers (else nobody could access anything and even closures would be garbage collected at some time in that case), right?
Where can I find the YUI instance inside the DOM (w/out assigning it to a global variable)?
Can I find it: are sandboxed YUI objects or parts of them living inside the DOM at all?
Getting more specific (and slightly off-topic) with an example:
I have a YUI Node object with attached YUI widgets and events. These Node objects are wrapping DOM nodes. Inspecting the DOM the wrapped nodes don't have any observers.
How are the events attached to the Node object triggered if nothing is wired up inside the DOM?

The only visible references with YUI 3 are the YUI global object and occasional element id that is assigned by YUI. Event subscriptions are made with native addEventListener or attachEvent, which don't leave evidence in the markup or innerHTML. There are some developer tools that are capable of showing subscriptions attached in this way, though, and they would show the YUI subscriptions.
You cannot find the YUI instance inside the DOM. That's the point of sandboxing. Instances of YUI exist in the JavaScript layer (technically, ECMAScript layer), not the DOM layer, exclusively inside closures unless you expressly assign them to a globally accessible variable or property or a globally accessible variable.

Related

JS set properties of HTML elements [duplicate]

JavaScript lets you add arbitrary properties and methods to any object, including DOM nodes. Assuming the property was well namespaced (something like _myLib_propertyName) so that it would be unlikely to create a conflict, are there any good reasons not to stash data in DOM nodes?
Are there any good use cases for doing so?
I imagine doing this frequently could contribute to a sloppy coding style or code that is confusing or counter-intuitive, but it seems like there would also be times when tucking "custom" properties into DOM nodes would be an effective and expedient technique.
No, it is usually a bad idea to store your own properties on DOM nodes.
DOM nodes are host objects and host objects can do what they like. Specifically, there is no requirement in the ECMAScript spec for host objects to allow this kind of extension, so browsers are not obliged to allow it. In particular, new browsers may choose not to, and existing code relying on it will break.
Not all host objects in existing browsers allow it. For example, text nodes and all ActiveX objects (such as XMLHttpRequest and XMLDOM objects, used for parsing XML) in IE do not, and failure behaviour varies from throwing errors to silent failure.
In IE, the ability to add properties can be switched off for a whole document, including all nodes within the document, with the line document.expando = false;. Thus if any of the code in your page includes this line, all code relying on adding properties to DOM nodes will fail.
I think more than anything, the best reason not to store data in the DOM is because the DOM is meant to represent content structure and styling for an HTML page. While you could surely add data to the nodes in a well-namespaced manner as to avoid conflicts, you're introducing data state into the visual representation.
I'm trying to find an example for or against storing data in the DOM, and scratching my head to find a convincing case. In the past, I've found that the separation of the data state from the visual state has saved headache during re-designs of sites I've worked on.
If you look at HTML 5 there is the data attribute for fields that will allow you to store information for the field.
Don't be surprised to run into trouble with IE 6 & 7. They are very inconsistent with setAttribute vs set as a property.
And if you're not careful you could set circular references and give yourself a memory leak.
http://www.ibm.com/developerworks/web/library/wa-memleak/
I always set node properties as a last resort, and when I do I'm extra careful with my code and test more heavily than usual.
For what it's worth, the D3.js library makes extensive use of custom properties on DOM nodes. In particular for the following packages that introduce interaction behaviors:
https://github.com/d3/d3-zoom - the __zoom property
https://github.com/d3/d3-brush - the __brush property
D3 also provides a mechanism for putting your own custom properties on DOM elements called d3.local. The point of that utility is to generate property names that are guaranteed not to conflict with the DOM API.
The primary risk I can see associated with custom properties on DOM nodes is accidentally picking a name that already has some meaning in the DOM API. If you use d3.local or maybe prefix everything with __, that should avoid conflicts.

Tool to see object references

Is there a tool for Node.js or the browser whereby I can find out which objects hold a reference to object X?
Right now I am using Backbone for front-end development and even though I remove views there still seem to be references to them afterwards.
The reason I suspect this behavior in the first place is because I am using plugin/addons for Backbone debugging in Chrome and Mozilla.
This does make me wonder if perhaps these programs themselves are the ones holding references to the Backbone objects!
First of all,Sadly there is no way to do that.
You can check who calls a function and object which specific variable holds as reference though.
It's not because of Backbone/Node.js but Javascript itself.
When you substitute object/Array, javascript only passes target memory address to the variable.
But I assume it's highly possible that the reason why you are having memory leak problem is not because of references from another variables but event handlers which is often seen in Backbone uses(also knowns as "zombie view")
Once you set events handler in a View, You need to make sure all events are unset before you actually delete the view(.remove()) unless You are using only listenTo for Backbone events and this.$el for jQuery events.
Because events set via listenTo and this.$el are automatically removed by Backbone Core when you remove a View.
And events set by Model.on or global jQuery$ would not be so.
So Please check your whole code whether You are using .on or global jQuery Object to set events, in the case You have, replace them into listenTo or this.$el.on or manually unset them Before You remove them.

Precise explanation of JavaScript <-> DOM circular reference issue

One of the touted advantages of jQuery.data versus raw expando properties (arbitrary attributes you can assign to DOM nodes) is that jQuery.data is "safe from circular references and therefore free from memory leaks". An article from Google titled "Optimizing JavaScript code" goes into more detail:
The most common memory leaks for web applications involve circular
references between the JavaScript script engine and the browsers' C++
objects' implementing the DOM (e.g. between the JavaScript script
engine and Internet Explorer's COM infrastructure, or between the
JavaScript engine and Firefox XPCOM infrastructure).
It lists two examples of circular reference patterns:
DOM element → event handler → closure scope → DOM
DOM element → via expando → intermediary object → DOM element
However, if a reference cycle between a DOM node and a JavaScript object produces a memory leak, doesn't this mean that any non-trivial event handler (e.g. onclick) will produce such a leak? I don't see how it's even possible for an event handler to avoid a reference cycle, because the way I see it:
The DOM element references the event handler.
The event handler references the DOM (either directly or indirectly). In any case, it's almost impossible to avoid referencing window in any interesting event handler, short of writing a setInterval loop that reads actions from a global queue.
Can someone provide a precise explanation of the JavaScript ↔ DOM circular reference problem? Things I'd like clarified:
What browsers are effected? A comment in the jQuery source specifically mentions IE6-7, but the Google article suggests Firefox is also affected.
Are expando properties and event handlers somehow different concerning memory leaks? Or are both of these code snippets susceptible to the same kind of memory leak?
// Create an expando that references to its own element.
var elem = document.getElementById('foo');
elem.myself = elem;
// Create an event handler that references its own element.
var elem = document.getElementById('foo');
elem.onclick = function() {
elem.style.display = 'none';
};
If a page leaks memory due to a circular reference, does the leak persist until the entire browser application is closed, or is the memory freed when the window/tab is closed?
It's probably not worth reproducing all the content in these links, so I'd suggest you do some reading and a look at the other Google search hits:
javascript, circular references and memory leaks
Do you know what may cause memory leaks in JavaScript?
http://www.ibm.com/developerworks/web/library/wa-memleak/
http://www.ibm.com/developerworks/web/library/wa-sieve/index.html?ca=drs-
http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks
The worst memory leaks are in IE6 where the leaks are permanent (even after you leave the affected web page). The other leaks are generally only while you're on that specific page and get cleaned up when you leave the page.
The fact is that the browser is supposed to be able to handle circular references. It's supposed to be able to see that even though a DOM element is still being referred to by a JavaScript element, that the JavaScript element itself only exists because the DOM element is still alive and thus there is no true outside reference left to the DOM element. It is this recognition that older versions of IE were bad at. Thus in your code references that involve event handlers, the garbage collector needs to be smart enough to know that the only references left to the DOM element in JavaScript are references that themselves would go away when the DOM element and it's event handlers were removed - thus there are no true outside references so it's safe to remove both the DOM element and the event handler. This is a more complicated version of the general circular reference problem that all garbage collectors have to handle where object A refers to object B and object B refers to object A, but no other object refers to either A or B, thus both can be freed.
jQuery's .data() makes things more reliable because the older versions of IE had a particular problem with properties that were added to a DOM element and did not handle circular references properly involving the data in those properties and thus would not free things when it should have (a leak). .data() works around that by only using one added property on the DOM element which is a safe, non-leaking string. That string is then a key into a JavaScript object that can contain all the properties that you would like to associate with the DOM element. Because these properties are all stored in plain JavaScript, where the browsers don't have the circular reference bugs, doing it this way doesn't cause leaks.
It's important to realize that there may still be some circular references and that's OK. The work-around is to move the circular references to a place that the browsers handle them appropriately rather than putting them in the place that the browsers have bugs.

What does backbone.js do with models that are not used anymore

Lately I am diving into the whole client-side MVC/MVVM design paterns, and the one I am particularly interested in is backbone.js.
One thing I do not fully understand is what happend to the models when they are not really needed anymore.
Let's say we have an app that has users and products. We have user models/views and product models/views
NOTE: for simplicity sake we are not a user. We can just CRUD users / products.
When I enter the products page, I assume we load the model and the view corresponding to this.
What happens when we leave the page and enter the users page. A user model/view is loaded, but the products are also still loaded.
Do we keep them loaded, does backbone take care of that for you, or do you explicitly need to end certain objects.
Backbone does not explicitly handle cleaning up objects for you. It's 50/50 you and the JavaScript runtime.
JavaScript is a garbage collected language like Java, C#, Ruby, and others. The basic of a garbage collected languages is that an object that is still referenced by your application will not be cleaned up. The counter to that is when an object is no longer referenced by your application, will be cleaned up.
JavaScript in general:
When you create a variable, you can either scope that variable to a local function or as a global variable.
Global variables are never cleaned up by the garbage collector, during the life of the page. The only time they are cleaned up is when you leave the HTML page behind entirely - navigate to a different page and force the browser to load the new page from the server (doing a complete server refresh) or closing the browser or browser tab.
Function scoped variables are cleaned up when the variable falls out of scope - that is, when the function has exited and there are no more references to it. There are a few exceptions to this: return values and closures.
A return value is held in your app by assigning the return value to another variable. A return value falls under the same general rules, but the variable is now in a different function. Once that variable goes out of scope, it can be cleaned up.
A closure allows a parent scope to provide values that a descendant scope can access. When the descendant scope is cleaned up, the parent's closured variable may be allowed to be cleaned up (assuming nothing else is holding on to it).
Objects with attributes and functions fall under the same rules. An object can reference another object or function by having an attribute assigned to it: myObj.foo = thatObj.
The DOM (Document Object Model - the HTML in your app) is a JavaScript object. Events and other references to your DOM work the same as any other reference. If you have an object handling a DOM event, it has a reference in your app and it won't be cleaned up by the garbage collector. If you want it cleaned up, you have to remove all references to it - including the DOM reference from the event handler.
Cleaning Up Memory
The general rule is that if you are loading data in to a backbone collection or object and you want that object to be cleaned up so it's not using anymore memory, you must remove all references to that object. This is just the standard JavaScript garbage collection rule.
You cannot force garbage collection, but you can force a variable to de-reference the thing it points to using the delete keyword in JavaScript: delete myVar
Backbone
Backbone is JavaScript so it falls under the same rules. There are some interesting uses of closures and references in Backbone that you need to be aware of, which will help you to know when you need to manually clean up some objects.
For example: events. An even handler / callback method works by having a reference between the object that triggers the event and the callback that handles the event. This is one of the easiest places to cause memory leaks in a Backbone app and I discuss it in detail, here: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
Other than being aware of how events work in terms of references, just follow the standard rules for manage memory in JavaScript and you'll be fine. Once you remove all references to that collection of User objects, they'll be cleaned up.
Note that if you console.log() your model (at least in Chrome) it will remain in memory because the console has a reference to it.
I got mad because of this. If you see memory leak, try clearing your console and run your profile again. If you had the same problem i had, it's gone :)

What's wrong with adding properties to DOM Element objects?

I've been looking for a straight answer for this (I can think of lots of possiblities, but I'd like to know the true reason):
jQuery provides a .data() method for associating data with DOM Element objects. What makes this necessary? Is there a problem adding properties (or methods) directly to DOM Element Objects? What is it?
Is there a problem adding properties (or methods) directly to DOM Element Objects?
Potentially.
There is no web standard that says you can add arbitrary properties to DOM nodes. They are ‘host objects’ with browser-specific implementations, not ‘native JavaScript objects’ which according to ECMA-262 you can do what you like with. Other host objects will not allow you to add arbitrary properties.
In reality since the earliest browsers did allow you to do it, it's a de facto standard that you can anyway... unless you deliberately tell IE to disallow it by setting document.expando= false. You probably wouldn't do that yourself, but if you're writing a script to be deployed elsewhere it might concern you.
There is a practical problem with arbitrary-properties in that you don't really know that the arbitrary name you have chosen doesn't have an existing meaning in some browser you haven't tested yet, or in a future version of a browser or standard that doesn't exist yet. Add a property element.sausage= true, and you can't be sure that no browser anywhere in space and time will use that as a signal to engage the exciting DOM Sausage Make The Browser Crash feature. So if you do add an arbitrary property, make sure to give it an unlikely name, for example element._mylibraryname_sausage= true. This also helps prevent namespace conflicts with other script components that might add arbitrary properties.
There is a further problem in IE in that properties you add are incorrectly treated as attributes. If you serialise the element with innerHTML you'll get an unexpected attribute in the output, eg. <p _mylibraryname_sausage="true">. Should you then assign that HTML string to another element, you'll get a property in the new element, potentially confusing your script.
(Note this only happens for properties whose values are simple types; Objects, Arrays and Functions do not show up in serialised HTML. I wish jQuery knew about this, because the way it works around it to implement the data method is absolutely terrible, results in bugs, and slows down many simple DOM operations.)
I think you can add all the properties you want, as long as you only have to use them yourself and the property is not a method or some object containing methods. What's wrong with that is that methods can create memory leaks in browsers. Especially when you use closures in such methods, the browser may not be able to complete garbage cleaning which causing scattered peaces of memory to stay occupied.
This link explains it nicely.
here you'll find a description of several common memory leak patterns
It has to do with the fact that DOM in IE is not managed by JScript, which makes it completely different environment to access. This leads to the memory leaks http://www.crockford.com/javascript/memory/leak.html. Another reason is that, when people use innerHTML to copy nodes, all those added properties are not transfered.

Categories