Function vs. lambda expressions & "this"

Topics: Language Specification
Jun 1, 2013 at 7:01 PM
Consider the following seeming equivalent statements:
class C
{
 m(){
  x.foo = function() { this.a = 1; };
  x.foo()
 }
}
Here, "this" gets bound to window.
class C
{
 m(){
  x.foo = () => { this.a = 1; };
  x.foo()
 }
}
Here, "this" gets bound to an instance of c, if m was called on the context of it.

IMO, it's odd to have so greatly different semantics for equivalent syntax.
"this" should be really worked around in TS, and always point to the class instance.
Even if this means diverging form ES.

TS has the opportunity to be better than ES, and since it'll always compile to ES, there's not need to keep the semantics identical.
Jun 2, 2013 at 10:45 AM
@yaakovd,

probably one of the first things to bear in mind with TypeScript is that () => {} and function(){} are not equivalent.

We need both because sometimes we want access to the "this" context provided by the designer of an API.

 var element = document.body;
element.innerHTML = "<div>foo</div>"

element.onclick = function(){
    console.log(this.firstChild.innerHTML); // foo
}

The request to fix "this" is not an infrequent topic on this forum - and IMO rather tedious, because it is often raised by those coming directly to TypeScript from another language, having bypassed JavaScript altogether.

https://typescript.codeplex.com/discussions/445319
https://typescript.codeplex.com/discussions/445528
https://typescript.codeplex.com/discussions/429350
https://typescript.codeplex.com/discussions/437633
https://typescript.codeplex.com/discussions/437989
https://typescript.codeplex.com/discussions/433717
https://typescript.codeplex.com/discussions/403675
https://typescript.codeplex.com/discussions/430229
https://typescript.codeplex.com/discussions/397774

https://typescript.codeplex.com/workitem/248
https://typescript.codeplex.com/workitem/468
https://typescript.codeplex.com/workitem/477
https://typescript.codeplex.com/workitem/851
Jun 2, 2013 at 11:06 AM
Edited Jun 2, 2013 at 11:31 AM
We need both because sometimes we want access to the "this" context provided by the designer of an API.
  1. I'd argue that "this" in the above example should be replaced with "element".
The request ... is often raised by those coming directly to TypeScript from another language, having bypassed JavaScript altogether
You're assuming wrong.
I converted JS code to TS, wrapping prototype-based JS "classes" as TS classes, and had to replace all function expressions with lambda expressions to maintain proper binding. So ironically TS's attempt to stay compatible with ES, actually broke my JS code.
I.e. The following code yielded the proper "this" semantics under JS, but NOT under TS:
var Class = {};
Class.prototype.foo = function()
{
   setTimeout(100, function(){this.x = 5;}); //"this" is a Class instance
}
Doing a straightforward TS porting broke my code:
class Class
{
  foo()
  {
    setTimeout(100, function(){this.x = 5;});//"this"  is window!
  }
}
You should either not break things, or break them properly. Since TS porting breaks JS, it should do so in a meaningful & predictable manner.

The "non-infrequency" of this topic might suggest that people want TS to be something other, and better, than ES.
Jun 2, 2013 at 12:06 PM
@yaakovd,

No offence to you personally: I was simply articulating something I had observed on the forum.

Yes, in my example one could replace "this" with "element", but the "this" context can be anything as decided by the API designer. I personally don't like "this" to be changed at all - but that is how many popular JavaScript libraries have been developed. I believe TypeScript is simply trying to cater for both situations.

There appears to be something wrong with your code: In the first snippet Class is an object literal and won't have a prototype. Also settimeout should take the duration as the second parameter.

In both JS and TS the "this" context will be window.
var Class = function (){};

Class.prototype.foo = function()
{
   setTimeout(function(){this.x = 5;}, 100); //"this" is a window instance
}
Jun 2, 2013 at 12:39 PM
You're right about the mistakes in my code examples; I wrote them ad-hoc without copying from any production code.

You're correct also about the "this" binding in the examples -- it will bind to window indeed.

I rechecked our code to understand what went wrong during the porting. Here's what I found:

With JS prototypical classing, the code would look like this:
var Class = function (){};
Class.foo = function()
{
  ...
}

Class.bar = function()
{
  setTimeout(Class.foo, 500);
}
foo here is called on the prototype, instead of the instance level. I believe this isn't what the original author who wrote it intended.
There's only a single instance of Class in our app, so everything worked on the "static" level, even when invoked at the instance level.
ie. it was used like this:
var c = new Class();
c.bar(); //bar called at the instance level, but the inner foo resorts to the prototype level. 
When converting to TS, I replaced
setTimeout(Class.foo, 500);
with
setTimeout(this.foo, 500);
which broke it.

So, it's not TS's fault after all, but the way it was originally written, and the fact that I changed the calling semantics from static to instance.