How do I properly pass a parameter to a parameterized event handler in a loop?

S

Sean Dockery

I have the following HTML file that I've been using for testing...

<html>
<head>
<script type="text/javascript">
<!--
function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}

var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;
var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}
child.attachEvent('onclick', handler.onclick);
parent.appendChild(child);
}
}
window.onload = handleWindowLoad;

function handleButtonClick() {
alert(this.currentItem);
}
//-->
</script>
</head>
<body>
<div id="parent">
</div>
</body>
</html>

What I want is for the handleButtonClick function to show an alert with the
text "item1", "item2", and so forth for each of the items clicked by the
user. Instead, it shows "item10" for every child of the parent element.
But I don't understand why. I thought that the code would be creating a
closure (am I using this term correctly?) for each child element, but it
apparently is not. Can anyone provide me with a solution that can clear up
my confusion?

I have also read that closures are bad from the point of view that they can
often be the cause of memory leaks. Is there a memory-leak-less way to
achieve the same thing that as the code above?

Thanks for your time.
 
R

RobG

Sean said:
I have the following HTML file that I've been using for testing...

<html>
<head>
<script type="text/javascript">
<!--

Don't use HTML comment delimiters inside script elements, they don't
do anything useful and are potentially harmful.

function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}


This array isn't necessary.

var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;
var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}
child.attachEvent('onclick', handler.onclick);


attachEvent is OK for IE and others that copy it's event model, but
what about the rest? Add a fork for addEventListener for W3C browsers.

But in any case it seems simpler to use:

child.onclick = handler.onclick;

parent.appendChild(child);
}
}
window.onload = handleWindowLoad;

function handleButtonClick() {
alert(this.currentItem);
}
//-->
</script>
</head>
<body>
<div id="parent">
</div>
</body>
</html>

What I want is for the handleButtonClick function to show an alert with the
text "item1", "item2", and so forth for each of the items clicked by the
user. Instead, it shows "item10" for every child of the parent element.

Then set the div id to 'item1', 'item2', etc. and then the
handleButtonClick becomes - alert(this.id).

You need to say what is important here. What do you really want to
store, a reference to the new div or the text 'item1', 'item2', etc?

But I don't understand why. I thought that the code would be creating a
closure (am I using this term correctly?) for each child element, but it
apparently is not. Can anyone provide me with a solution that can clear up
my confusion?

You are creating a closure, hence your reference is always to the last
'item' created, not to the one that was attached at the time each div
was created.

As to how to avoid it, there are many schemes but it depends on what
you are really trying to do.

I have also read that closures are bad from the point of view that they can
often be the cause of memory leaks. Is there a memory-leak-less way to
achieve the same thing that as the code above?

Yes, see above. One solution(?) is below:


function handleWindowLoad()
{
var numOf = 10;
var child, txt;
var parent = document.getElementById("parent");

for (var i=0; i<numOf; ++i) {
child = document.createElement("div");
txt = 'item' + (i + 1);
child.appendChild(document.createTextNode(txt));
child.id = txt;

child.onclick = handleButtonClick;
parent.appendChild(child);
}
}


function handleButtonClick()
{
alert(this.id);
}


window.onload = handleWindowLoad;
 
S

Sean Dockery

Jasen Betts said:
I have the following HTML file that I've been using for testing...

function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}

var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;
var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}

if(child.attachEvent)
child.attachEvent('onclick', handler.onclick);
else
child.onclick=handler.onclick;
parent.appendChild(child);
}
}
window.onload = handleWindowLoad;

function handleButtonClick() {
alert(this.currentItem);
}
What I want is for the handleButtonClick function to show an alert with
the
text "item1", "item2", and so forth for each of the items clicked by the
user. Instead, it shows "item10" for every child of the parent element.
But I don't understand why. I thought that the code would be creating a
closure (am I using this term correctly?) for each child element, but it
apparently is not. Can anyone provide me with a solution that can clear
up
my confusion?

you have a closure but your closure refers to the variables of
handleWindowLoad()


I believe at this point that I can safely say that I don't really understand
what a closure is. I had assumed that they were similar to anonymous inner
classes in Java, but it seems that is not the case.
get the target object from the event....

Can you provide an example?
 
S

Sean Dockery

RobG said:
Don't use HTML comment delimiters inside script elements, they don't do
anything useful and are potentially harmful.

I had always believed that they were a considered a "best practice". Can
you give an example of situation where they would be harmful?
function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}


This array isn't necessary.


The array is necessary in that it is the mechanism by which I will be
delivered a number of JavaScript objects. See below.
var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;
var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}
child.attachEvent('onclick', handler.onclick);


attachEvent is OK for IE and others that copy it's event model, but what
about the rest? Add a fork for addEventListener for W3C browsers.


This is just a proof of concept. In production, I'm using the popular
"addEvent" function that hides the browser details.
But in any case it seems simpler to use:

child.onclick = handler.onclick;



Then set the div id to 'item1', 'item2', etc. and then the
handleButtonClick becomes - alert(this.id).

You need to say what is important here. What do you really want to store,
a reference to the new div or the text 'item1', 'item2', etc?

Okay. In my application, I have a list (array) of JavaScript objects that
are being provided through the DWR remoting library. For the purposes of
testing, I could easily create the objects as follows...

var person0 = { id: 1234, firstName: 'John', lastName: 'Doe' };
var person1 = { id: 2345, firstName: 'Jack', lastName: 'Rabbit' };
var people = [person0, person1];

In the callback function, I want to create HTML elements in the DOM for each
of the items in the result list such that the elements that allow for
addition detail information to be fetched for each person when the user
clicks on them. I want the handleButtonClick function in my example to have
access to the relevant object which was clicked upon by the user.
You are creating a closure, hence your reference is always to the last
'item' created, not to the one that was attached at the time each div was
created.

As I mentioned in the other reply in this thread, I have come to believe
that I don't understand closures yet. I had thought that they worked the
same as Java's anonymous inner classes, but they do not.
As to how to avoid it, there are many schemes but it depends on what you
are really trying to do.



Yes, see above. One solution(?) is below:


function handleWindowLoad()
{
var numOf = 10;
var child, txt;
var parent = document.getElementById("parent");

for (var i=0; i<numOf; ++i) {
child = document.createElement("div");
txt = 'item' + (i + 1);
child.appendChild(document.createTextNode(txt));
child.id = txt;

child.onclick = handleButtonClick;
parent.appendChild(child);
}
}


function handleButtonClick()
{
alert(this.id);
}

I'm confused by this part here. Can you tell me to which JavaScript object
the keyword "this" refers in this function? I am confused because I thought
that the "this" keyword was only value when you did something like this...

function MyObject(id) {
this.id = id;
}
myObject.prototype.whatever = function() {
alert(this.id);
}
var myObj = new MyObject(567);
myObj.whatever();

....and that the "call" method of the Function object was the only way to
bind the "this" keyword to a specific object when dealing with generic
methods. For example...

function MyObject(id) {
this.id = id;
}

function whatever() {
alert(this.id);
}

var myObj = new MyObject(678);
whatever.call(myObject);

Is there always a value for the "this" keyword that is implied somehow?
 
T

Thomas 'PointedEars' Lahn

Sean said:
I have the following HTML file that I've been using for testing...

<html>

That is not Valid HTML. said:
<head>
<script type="text/javascript">
<!--

Trying to comment out `script' element's content is unnecessary and
potentially harmful. In contrast your assumption, it is not at all
best practice, it is rather the practice of the uninitiated and the
incompetent. Since you already asked why, I can refer you here to
previous discussions; Google (Groups) is your friend. [psf 6.1]
function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}

var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;


This is all error-prone.

var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}
child.attachEvent('onclick', handler.onclick);


Frankly, I do not understand why you just not make direct use of the
means the DOM provides in the first place.
parent.appendChild(child);
}
}
window.onload = handleWindowLoad;

That will be called when the window has finished loading. That is not
necessarily when the document it displays has finished loading. Use
the `onload' attribute of the HTML document's `body' element instead;
images and objects may not be loaded by then, but iff there is a DOM
tree, it is then there for you to use it along with its element objects.
function handleButtonClick() {
alert(this.currentItem);
}
//-->
</script>
</head>
<body>
<div id="parent">
</div>
</body>

What are users without enabled client-side script support going to do with
that "document"? Client-side scripting should be used to extend documents,
not to build them, unless those documents can only be accessed through
scripting (which required some search engine and code tweaks). Graceful
degradation is the keyword here.
</html>

What I want is for the handleButtonClick function to show an alert with
the text "item1", "item2", and so forth for each of the items clicked by
the user. Instead, it shows "item10" for every child of the parent
element. But I don't understand why.

I thought that the code would be creating a closure (am I using this term
correctly?) for each child element, but it apparently is not. Can anyone
provide me with a solution that can clear up my confusion?

The closure created by the method assignment is unused because you are
passing a reference to handler's method to the method that assigns the
event listener [currently attachEvent()]. When you do this, the Function
object referred to is not longer called as method of the object `handler'
refers to but as method of the Global Object -- it loses its connection to
its previous owner. `handler' within the method code will then no longer
refer to the value `handler' had when the function object was created and
assigned a method of `handler', but to the last value of the global
`handler' variable; that is, the value it is assigned in the last loop.

A possible workaround is

child.attachEvent(
'onclick',
function()
{
handler.onclick();
});

The same problem occurs and the same workaround is suggested, e.g., when you
use a reference to a named method as first argument of the setTimeout() or
setInterval() methods of Window objects; see previous discussions.


HTH

PointedEars
 
R

RobG

Sean said:
I had always believed that they were a considered a "best practice". Can
you give an example of situation where they would be harmful?

HTML comment delimiters were used inside script elements to cater for
browsers that didn't know what a script element was and to stop them
displaying the element content. Such browsers would be prior to
Netscape 2 (Dec 1995), IE 3 (Jun 1996) or HTML 3.2 (Jan 1997).

Anyone still using one of those browsers on the web today has far more
to worry about than the display of some script element content. If
that is a genuine concern for you, put the script in an external file
so that the script element has no content.

Even if someone visited your site with such an old browser, it should
not display markup appearing inside the head element anyway.

The delimiters are potentially harmful for two reasons:

1. They are not valid JavaScript (or ECMAScript) and browsers are not
required to treat them as comment delimiters (though likely all
current browsers do provided they start before any real script and the
closing tag is commented out).

2. If you decide to use XHTML, the script element is not portable as
the comment delimiters will comment out the script content. XHTML
requires that different delimiters be used:

<URL:http://www.w3.org/TR/xhtml1/#h-4.8>


Again, I think most browsers will ignore them but you can't guarantee
that.

In reality, adding comment delimiters does no real harm in current
browsers, but the fact that they have survived for at least 5 years
(or so) beyond their use-by date hints that their continued use simply
perpetuates their futile existence.

Welcome to 2006 :)

function handleWindowLoad() {
var items = [];
for (var i = 0; i < 11; i++) {
items = "item" + (i + 1);
}


This array isn't necessary.



The array is necessary in that it is the mechanism by which I will be
delivered a number of JavaScript objects. See below.


Ah, a prototype. :)

var parent = document.getElementById("parent");
for (var i = 0; i < items.length - 1; i++) {
var child = document.createElement("div");
child.innerHTML = items;
var currentItem = items;
handler = new Object();
handler.currentItem = currentItem;
handler.onclick = function() {
handleButtonClick.call(handler);
}
child.attachEvent('onclick', handler.onclick);


attachEvent is OK for IE and others that copy it's event model, but what
about the rest? Add a fork for addEventListener for W3C browsers.



This is just a proof of concept. In production, I'm using the popular
"addEvent" function that hides the browser details.


I can guess what that does, but adding the onclick directly as below
seems simplest of all.

But in any case it seems simpler to use:

child.onclick = handler.onclick;
[...]
You need to say what is important here. What do you really want to store,
a reference to the new div or the text 'item1', 'item2', etc?


Okay. In my application, I have a list (array) of JavaScript objects that
are being provided through the DWR remoting library. For the purposes of
testing, I could easily create the objects as follows...

var person0 = { id: 1234, firstName: 'John', lastName: 'Doe' };
var person1 = { id: 2345, firstName: 'Jack', lastName: 'Rabbit' };
var people = [person0, person1];

In the callback function, I want to create HTML elements in the DOM for each
of the items in the result list such that the elements that allow for
addition detail information to be fetched for each person when the user
clicks on them. I want the handleButtonClick function in my example to have
access to the relevant object which was clicked upon by the user.

So you are trying to create a link between the DOM object and the
'record' in the array. I think the easiest way is to pick a unique
identifier for each record (you have an id that might be useful) then
use that as the key to the person record.

When you create each div, give it an id that you use as the key to the
person's record in the people object. IDs can't start with numbers
but can include them. The example below (end of post) does just that.

I've used an array of for the people records then created an index
object that maps the element IDs to the people record indexes. That
may be useful, but you can also just sift through the records in an
object using for..in for up to say, 50 records with no noticeable
affect on performance (depending on the system). If you have a lot of
records, an index will really help.

The people array could be an object too, I'm not really using the
array-ness of it. If the ID is unique, the structure could be:

var people = {
'1234' : {firstName: 'John', lastName: 'Doe' },
'2345' : {firstName: 'Jack', lastName: 'Rabbit'}
};

Using an array means that if you modify the order of records, delete
or add some you'll have to update the index object.

[...]
As I mentioned in the other reply in this thread, I have come to believe
that I don't understand closures yet. I had thought that they worked the
same as Java's anonymous inner classes, but they do not.

I don't know what Java anonymous inner classes are, and while I can
recognise a closure I can't explain them fully. They are powerful and
handy but can also cause problems.

I'm confused by this part here. Can you tell me to which JavaScript object
the keyword "this" refers in this function?

When the function is attached to an element using a reference, 'this'
will refer to the element whose event called the function. In the
case above, 'this' will refer to the div that was clicked on.

I am confused because I thought
that the "this" keyword was only value when you did something like this...

function MyObject(id) {
this.id = id;
}
myObject.prototype.whatever = function() {
alert(this.id);
}
var myObj = new MyObject(567);
myObj.whatever();

In the above case, 'this' refers to the new object being created.

...and that the "call" method of the Function object was the only way to
bind the "this" keyword to a specific object when dealing with generic
methods. For example...

function MyObject(id) {
this.id = id;
}

function whatever() {
alert(this.id);
}

var myObj = new MyObject(678);
whatever.call(myObject);

Is there always a value for the "this" keyword that is implied somehow?

If it doesn't apply to anything else, it will be a reference to the
global object (the window object in most browsers).


Maybe something like this will do the trick (feature detection and
doctype omitted for brevity):


<html>
<head><title>...</title>
<script type="text/javascript">

// A people array that holds records for each person
var people = [
{ id: 1234, firstName: 'John', lastName: 'Doe', age: 35 },
{ id: 2345, firstName: 'Jack', lastName: 'Rabbit', age: 12 }
];

// To hold a reference to each people record by id:
var peopleIndex = {};

function handleWindowLoad()
{
var parent = document.getElementById("parent");
var d, p, pName;
for (var i=0, len=people.length; i<len; ++i){

// Create a div element
d = document.createElement('div');

// Build a text string and add it as div content
pName = people.firstName + ' ' + people.lastName;
d.appendChild(document.createTextNode(pName));

// Use the person ID to give the div element an id
d.id = 'id-' + people.id;

// Add a cross-reference entry in the index file for faster
// retrieval of the person record later
peopleIndex['id-' + people.id] = i;

// Add the onclick
d.onclick = function (){
handleButtonClick(this.id, people, peopleIndex);
}

parent.appendChild(d);
}
}

function handleButtonClick(elId, obj, objIndex)
{
var idxNum = objIndex[elId];
var record = obj[idxNum];
var txt = 'Record ' + idxNum;
for (var prop in record){
txt += '\n' + prop + ' : ' + record[prop];
}
alert(txt);
}

window.onload = handleWindowLoad;

</script>
</head>
<body>
<div id="parent"></div>
</body>
</html>
 
M

Michael Winter

On 01/01/2006 00:19, Sean Dockery wrote:

[snip]
I had always believed that [SGML comment declarations in scripts]
were a considered a "best practice". Can you give an example of
situation where they would be harmful?

To say that they are potentially harmful, whilst theoretically true (as
Rob explained), can be detrimental to the case because it's hard to
prove (at least with HTML). Irrespective of that, they are /not/ best
practice, and haven't been for a long time.

[snip]
[...] I have come to believe that I don't understand closures yet. I
had thought that they worked the same as Java's anonymous inner
classes, but they do not.

Closures in ECMAScript are a fairly simple concept but the deeper
mechanics, which do need to be understood to use closures effectively,
are much more complex.

We begin with nested functions.

function outer(argument) {
var local;

function inner() {
}
}

As you are no doubt aware, nested functions have access to all local
variables (including formal arguments and other nested functions) of the
enclosing function object. Under normal circumstances, returning from
the outer function will cause the inner function to be destroyed (there
will be no references to it, so it will be garbage collected like any
other object).

A closure is formed when a nested function survives after the enclosing
function has returned. This can be achieved by either returning a
reference to the nested function, or by assigning that reference to a
variable.

function outer() {
function inner() {
}

return inner;

/* or:
*
* return function() {
* };
*/
}


var myClosure;

function outer() {
function inner() {
}

myClosure = inner;

/* or:
*
* myClosure = function() {
* };
*/
}

What's special about closures is that the local variables that could be
accessed still can be; they survive just as the inner function does.

A more in-depth explanation can be found in the FAQ notes[1].

[snip]
Is there always a value for the "this" keyword that is implied
somehow?

Yes. There are four scenarios in which the this operator value changes.

The new operator:

When the new operator is used, an object is created, the
prototype property of the function reference operand is added
(if it's an object) to the prototype chain of the new object,
and the function object is then called with the this operator
referring to the newly created object.

var object = new MyObject();

1. Create a new object.
2. If the prototype property of MyObject is an object,
assign it to the internal [[prototype]] property of the
object in (1).
3. Call MyObject, using a reference to the object in (1) as
the value of the this operator.
4. If the return value of MyObject is an object reference,
assign that to the variable, object. Otherwise assign a
reference to the object in (1).

The Function.prototype.apply and call methods:

These two methods allow a function to be called with any
arbitrary object as the this operator value. If the first
argument is null or undefined, the this operator value will
refer to the global object.

var myObject = { myMethod : function() {return 'object';} },
myMethod = function() {return 'global';};

function myFunction() {
alert(this.myMethod());
}

myFunction.call(myObject); /* 'object' */

myFunction.call(); /* 'global' */
/* or:
*
* myFunction.call(null);
*/

Methods:

When a function is called as a member of some object, using
either dot or square bracket notation, the this operator will
reference that object. A method can also be transferred from
one object to another, and the this operator will refer to that
new object.

var A = { myMethod : function() {alert(this.identifier);},
identifier : 'A' },
B = { identifier : 'B' };

B.myMethod = A.myMethod;
A.myMethod(); /* 'A' */
B.myMethod(); /* 'B' */

What is important to note here is that the this operator value
is determined by /how/ a function is called. This will become
very important in the next (and final) section.

The global and activation/variable objects:

All variables are properties of some object, including global
and local variables. Global variables are properties of the
global object. Local variables are properties of the variable
object: an object that sits at the top of the scope chain of
each and every function, and therefore first encountered when
resolving identifiers. If an identifier resolves to a property
of one of these objects, the this operator value will refer to
the global object.

var global = this; /* The this operator also refers to the
* global object in global scope.
*/

function myFunction() {
alert(this == global);
}
function outer() {
function inner() {
alert(this == global);
}
inner();
}

myFunction(); /* true */
outer(); /* true */

Hope that helps,
Mike


[1] <http://www.jibbering.com/faq/faq_notes/closures.html>
 
S

Sean Dockery

Thanks go out to Jasen Betts, RobG, and Michael Winter for their supportive
responses in helping me to understand how closures work in JavaScript. I
had an "a ha" moment on Sunday, and I think that I get them now.

Thanks especially to Michael Winter for his superb explanation and examples.
I am very grateful for the time that you must have put into composing your
response.

Here is my solution, for anyone who is interested...

<html>
<head>
<script type="text/javascript">
<!--
function ClickHandler(person) {
this.person = person;
}
ClickHandler.prototype.attach = function(child) {
var that = this;
child.attachEvent('onclick', function() { that.onclick(); });
}
ClickHandler.prototype.onclick = function() {
alert(this.person.firstName + ' ' + this.person.lastName + ' with id = '
+ this.person.id + ' was clicked');
}
function handleWindowLoad() {
var person0 = { id: 123, firstName: 'John', lastName: 'Doe' };
var person1 = { id: 245, firstName: 'Jack', lastName: 'Rabbit' };
var person2 = { id: 345, firstName: 'Mary', lastName: 'Lamb' };
var people = [ person0, person1, person2 ];
var parent = document.getElementById("parent");
for (var i = 0; i < people.length; i++) {
var person = people;
var child = document.createElement("div");
child.innerHTML = person.firstName + ' ' + person.lastName;
var handler = new ClickHandler(person);
handler.attach(child);
parent.appendChild(child);
}
}
window.onload = handleWindowLoad;
//-->
</script>
</head>
<body>
<div id="parent">
</div>
</body>
</html>




Michael Winter said:
On 01/01/2006 00:19, Sean Dockery wrote:

[snip]
I had always believed that [SGML comment declarations in scripts] were a
considered a "best practice". Can you give an example of situation where
they would be harmful?

To say that they are potentially harmful, whilst theoretically true (as
Rob explained), can be detrimental to the case because it's hard to prove
(at least with HTML). Irrespective of that, they are /not/ best practice,
and haven't been for a long time.

[snip]
[...] I have come to believe that I don't understand closures yet. I had
thought that they worked the same as Java's anonymous inner classes, but
they do not.

Closures in ECMAScript are a fairly simple concept but the deeper
mechanics, which do need to be understood to use closures effectively, are
much more complex.

We begin with nested functions.

function outer(argument) {
var local;

function inner() {
}
}

As you are no doubt aware, nested functions have access to all local
variables (including formal arguments and other nested functions) of the
enclosing function object. Under normal circumstances, returning from the
outer function will cause the inner function to be destroyed (there will
be no references to it, so it will be garbage collected like any other
object).

A closure is formed when a nested function survives after the enclosing
function has returned. This can be achieved by either returning a
reference to the nested function, or by assigning that reference to a
variable.

function outer() {
function inner() {
}

return inner;

/* or:
*
* return function() {
* };
*/
}


var myClosure;

function outer() {
function inner() {
}

myClosure = inner;

/* or:
*
* myClosure = function() {
* };
*/
}

What's special about closures is that the local variables that could be
accessed still can be; they survive just as the inner function does.

A more in-depth explanation can be found in the FAQ notes[1].

[snip]
Is there always a value for the "this" keyword that is implied
somehow?

Yes. There are four scenarios in which the this operator value changes.

The new operator:

When the new operator is used, an object is created, the
prototype property of the function reference operand is added
(if it's an object) to the prototype chain of the new object,
and the function object is then called with the this operator
referring to the newly created object.

var object = new MyObject();

1. Create a new object.
2. If the prototype property of MyObject is an object,
assign it to the internal [[prototype]] property of the
object in (1).
3. Call MyObject, using a reference to the object in (1) as
the value of the this operator.
4. If the return value of MyObject is an object reference,
assign that to the variable, object. Otherwise assign a
reference to the object in (1).

The Function.prototype.apply and call methods:

These two methods allow a function to be called with any
arbitrary object as the this operator value. If the first
argument is null or undefined, the this operator value will
refer to the global object.

var myObject = { myMethod : function() {return 'object';} },
myMethod = function() {return 'global';};

function myFunction() {
alert(this.myMethod());
}

myFunction.call(myObject); /* 'object' */

myFunction.call(); /* 'global' */
/* or:
*
* myFunction.call(null);
*/

Methods:

When a function is called as a member of some object, using
either dot or square bracket notation, the this operator will
reference that object. A method can also be transferred from
one object to another, and the this operator will refer to that
new object.

var A = { myMethod : function() {alert(this.identifier);},
identifier : 'A' },
B = { identifier : 'B' };

B.myMethod = A.myMethod;
A.myMethod(); /* 'A' */
B.myMethod(); /* 'B' */

What is important to note here is that the this operator value
is determined by /how/ a function is called. This will become
very important in the next (and final) section.

The global and activation/variable objects:

All variables are properties of some object, including global
and local variables. Global variables are properties of the
global object. Local variables are properties of the variable
object: an object that sits at the top of the scope chain of
each and every function, and therefore first encountered when
resolving identifiers. If an identifier resolves to a property
of one of these objects, the this operator value will refer to
the global object.

var global = this; /* The this operator also refers to the
* global object in global scope.
*/

function myFunction() {
alert(this == global);
}
function outer() {
function inner() {
alert(this == global);
}
inner();
}

myFunction(); /* true */
outer(); /* true */

Hope that helps,
Mike


[1] <http://www.jibbering.com/faq/faq_notes/closures.html>
 
S

Sean Dockery

Yes, I know that I put SGML comments between the script tags again. What
can I say? Old habits die hard. :)

In our production application, all of the JavaScript is maintained in
external files and diligently kept separate from the (XHTML 1.0/strict)
presentation layer, so I'm in no danger of perpetuating this bad habit.

Sean Dockery said:
Thanks go out to Jasen Betts, RobG, and Michael Winter for their
supportive responses in helping me to understand how closures work in
JavaScript. I had an "a ha" moment on Sunday, and I think that I get them
now.

Thanks especially to Michael Winter for his superb explanation and
examples. I am very grateful for the time that you must have put into
composing your response.

Here is my solution, for anyone who is interested...

<html>
<head>
<script type="text/javascript">
<!--
function ClickHandler(person) {
this.person = person;
}
ClickHandler.prototype.attach = function(child) {
var that = this;
child.attachEvent('onclick', function() { that.onclick(); });
}
ClickHandler.prototype.onclick = function() {
alert(this.person.firstName + ' ' + this.person.lastName + ' with id =
' + this.person.id + ' was clicked');
}
function handleWindowLoad() {
var person0 = { id: 123, firstName: 'John', lastName: 'Doe' };
var person1 = { id: 245, firstName: 'Jack', lastName: 'Rabbit' };
var person2 = { id: 345, firstName: 'Mary', lastName: 'Lamb' };
var people = [ person0, person1, person2 ];
var parent = document.getElementById("parent");
for (var i = 0; i < people.length; i++) {
var person = people;
var child = document.createElement("div");
child.innerHTML = person.firstName + ' ' + person.lastName;
var handler = new ClickHandler(person);
handler.attach(child);
parent.appendChild(child);
}
}
window.onload = handleWindowLoad;
//-->
</script>
</head>
<body>
<div id="parent">
</div>
</body>
</html>




Michael Winter said:
On 01/01/2006 00:19, Sean Dockery wrote:

[snip]
I had always believed that [SGML comment declarations in scripts] were a
considered a "best practice". Can you give an example of situation where
they would be harmful?

To say that they are potentially harmful, whilst theoretically true (as
Rob explained), can be detrimental to the case because it's hard to prove
(at least with HTML). Irrespective of that, they are /not/ best practice,
and haven't been for a long time.

[snip]
[...] I have come to believe that I don't understand closures yet. I had
thought that they worked the same as Java's anonymous inner classes, but
they do not.

Closures in ECMAScript are a fairly simple concept but the deeper
mechanics, which do need to be understood to use closures effectively,
are much more complex.

We begin with nested functions.

function outer(argument) {
var local;

function inner() {
}
}

As you are no doubt aware, nested functions have access to all local
variables (including formal arguments and other nested functions) of the
enclosing function object. Under normal circumstances, returning from the
outer function will cause the inner function to be destroyed (there will
be no references to it, so it will be garbage collected like any other
object).

A closure is formed when a nested function survives after the enclosing
function has returned. This can be achieved by either returning a
reference to the nested function, or by assigning that reference to a
variable.

function outer() {
function inner() {
}

return inner;

/* or:
*
* return function() {
* };
*/
}


var myClosure;

function outer() {
function inner() {
}

myClosure = inner;

/* or:
*
* myClosure = function() {
* };
*/
}

What's special about closures is that the local variables that could be
accessed still can be; they survive just as the inner function does.

A more in-depth explanation can be found in the FAQ notes[1].

[snip]
Is there always a value for the "this" keyword that is implied
somehow?

Yes. There are four scenarios in which the this operator value changes.

The new operator:

When the new operator is used, an object is created, the
prototype property of the function reference operand is added
(if it's an object) to the prototype chain of the new object,
and the function object is then called with the this operator
referring to the newly created object.

var object = new MyObject();

1. Create a new object.
2. If the prototype property of MyObject is an object,
assign it to the internal [[prototype]] property of the
object in (1).
3. Call MyObject, using a reference to the object in (1) as
the value of the this operator.
4. If the return value of MyObject is an object reference,
assign that to the variable, object. Otherwise assign a
reference to the object in (1).

The Function.prototype.apply and call methods:

These two methods allow a function to be called with any
arbitrary object as the this operator value. If the first
argument is null or undefined, the this operator value will
refer to the global object.

var myObject = { myMethod : function() {return 'object';} },
myMethod = function() {return 'global';};

function myFunction() {
alert(this.myMethod());
}

myFunction.call(myObject); /* 'object' */

myFunction.call(); /* 'global' */
/* or:
*
* myFunction.call(null);
*/

Methods:

When a function is called as a member of some object, using
either dot or square bracket notation, the this operator will
reference that object. A method can also be transferred from
one object to another, and the this operator will refer to that
new object.

var A = { myMethod : function() {alert(this.identifier);},
identifier : 'A' },
B = { identifier : 'B' };

B.myMethod = A.myMethod;
A.myMethod(); /* 'A' */
B.myMethod(); /* 'B' */

What is important to note here is that the this operator value
is determined by /how/ a function is called. This will become
very important in the next (and final) section.

The global and activation/variable objects:

All variables are properties of some object, including global
and local variables. Global variables are properties of the
global object. Local variables are properties of the variable
object: an object that sits at the top of the scope chain of
each and every function, and therefore first encountered when
resolving identifiers. If an identifier resolves to a property
of one of these objects, the this operator value will refer to
the global object.

var global = this; /* The this operator also refers to the
* global object in global scope.
*/

function myFunction() {
alert(this == global);
}
function outer() {
function inner() {
alert(this == global);
}
inner();
}

myFunction(); /* true */
outer(); /* true */

Hope that helps,
Mike


[1] <http://www.jibbering.com/faq/faq_notes/closures.html>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top