2

Assigning a function handler to a variable leads me to believe that my variable is now a reference to my function. While this all seems fine and dandy, why can I concatenate a string with my "reference" and why is this concatenation reflected when I write my "reference" to the screen?

Why is my function reference being both handled and written as a string?

What is going on behind the scenes with references and type?

var x = tester;

document.getElementById("test_button").onclick = x;

document.getElementById("test_p").innerHTML += (x + "<br>");
x += "Did I just concatenate a string?";
document.getElementById("test_p").innerHTML += (x);

function tester() {
  document.write("Hello World!");
}
<button id="test_button">Press Me</button>
<br>
<p id="test_p"></p>

I assign the reference as an onclick for a button, and the function still executes as expected.

[Press Me] 
function tester() { document.write("Hello World!"); }
function tester() { document.write("Hello World!"); }Did I just concatenate 
a string?

Above is output, and when the button is pressed "Hello World!" is written to the screen.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
alexshoofs
  • 33
  • 2

2 Answers2

1

why can I concatenate a string with my "reference"

Because JavaScript performs type conversion when applying operators to values of data types that the operator does not support. The concatenation operator (+) is not defined for functions, so the function is converted to a string first.
The string representation of a user defined function is its source code.

why is this concatenation reflected when I write my "reference" to the screen?

Because x += y (and .innerHTML += y) is the same as x = x + y. You are performing string concatenation and assign the new value (the string) back to x (.innerHTML). x used to hold a function, after that operation it holds a string.

Why is my function reference being both handled and written as a string?

It's not. The value of document.getElementById("test_button").onclick does not change when you do x += ... (or .innerHTML += ...).

When you did

document.getElementById("test_button").onclick = x

you assigned a copy of the current value of x to element.onclick. At this point the value of x is a reference to a function.

When you do x += ... you assign a new value to x, but that doesn't change the value of the onclick property.

We start with

                        +---------------+
                        |               |
x: ref ---------------->|function tester|
                        |               |
                        +---------------+

After the assignment to .onclick we have

                                +---------------+
                                |               |
x: ref ------------------------>|function tester|
                                |               |
                                +---------------+
                                        ^        
                                        |        
element.onclick: ref  ------------------+        

And after assigning the concatenated string to x we have

                                          +---------------+
                                          |               |
                                          |function tester|
                                          |               |
x: string "function tester()...."         +---------------+
                                                  ^        
                                                  |        
element.onclick: ref  ----------------------------+        

What is going on behind the scenes with references and type?

References don't really have anything to do with this here. Objects in JavaScript are reference type values, but it's not like other languages where you can either access the reference or the referenced value. This all happens behind the scene.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

Functions can be stringified by calling Function.toString(). The type coercion from function to string happens automatically when you concatenate (the + operator with a string operand).

The MDN reference for Function.prototype.toString() states:

For user-defined Function objects, the toString method returns a string containing the source text segment which was used to define the function.

JavaScript calls the toString method automatically when a Function is to be represented as a text value, e.g. when a function is concatenated with a string.

In the case of setting an .onclick or .innerHTML property, the function object itself is assigned to the property. The difference is that .innerHTML ultimately stringifies the function by calling fn.toString() while .onclick invokes the function as a callback.

You can insert a console.log into the function's toString to validate that it truly was called when innerHTML was set or when you perform a concatenation:

const foo = () => "hello";
foo.toString = () => console.log("toString called!") || "this is the toString() string";

42 + foo; // was toString really called?

const div = document.createElement("div");
document.body.appendChild(div);
div.innerHTML = foo; // was toString really called?

... but not when onclick was set or invoked:

const foo = () => console.log("hello");
foo.toString = () => console.log("toString called!") || "this is the toString() string";

const btn = document.createElement("button");
document.body.appendChild(btn);
btn.innerText = "click me to see if toString is called";
btn.onclick = foo;
Community
  • 1
  • 1
ggorlen
  • 44,755
  • 7
  • 76
  • 106