javascript scope and everything about 'this' - callback

I am trying to understand in depth how 'this' works in javascript.
All I have known about this so far is,
Every function has properties and whenever the function executes, it newly defines the this property.
this refers to the object that a function is invoked to (including window object in browser).
this refers to the scope of the object(where the object is defined) instead of referring to the object itself if you use arrow syntax when defining a function because arrow function does not newly defines its own this.
The examples below are to help understanding the behaviour of this
class Example {
constructor() {
this.name = 'John';
}
method1() { //case1 : Closure
console.log(this.name);
function method2() {
console.log(this.name);
}
method2();
}
}
const a = new Example()
a.method1();
function testing(callback) {
return callback();
}
class Example2 {
constructor() {
this.name = 'John';
}
method1() { //case2: callback
console.log(this.name);
testing(function() {
console.log(this.name);
})
}
}
const b = new Example2()
b.method1();
function testing(callback) {
return callback();
}
class Example3 {
constructor() {
this.name = 'John';
}
method1() { //case3: arrow syntax callback
console.log(this.name);
testing(() => {
console.log(this.name);
})
}
}
const c = new Example3()
c.method1(); // logs 'John'
// logs 'John'
function testing(callback) {
return callback();
}
class Example4 {
constructor() {
this.name = 'John';
}
method1() { // case4: calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example4()
d.render()
function testing(callback) {
return callback();
}
class Example5 {
constructor() {
this.name = 'John';
this.method1 = this.method1.bind(this);
}
method1() { //case5: bind method && calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example5()
d.render()
I wonder how those above cases are different and what the this refers to inside each inner function and callback. Could you please explain about it? thank you :)

Since the in-depth precise explanation can be pretty big and boring, here is an exceptional article by kangax that perfectly lays it out.
And just in case, if you need a short and ultra condensed version of it here goes my short and approximate take:
#
When you call a function the this is determined by the specific base value which is usually pointing to whatever is on the left of the .
in MemberExpression so in x.y() this === x, and in x.y.z() this === x.y.
In case of a simple CallExpression without the ., say just x(),
the base value is implicitly inferred to point to undefined, which in non-strict mode is converted to global window and in strict mode stays the same.
This is the general mental model which should cover 99% of all the day-to-day problems with drawing the this context out correctly.
Now on, to the actual cases:
CASE 1:
a.method1(); call has a base value a so the this inside of its body points to a, so no surprises here.
method2 has implicit base value undefined.method2, thus you have the TypeError which explicitly states that.
CASE 2:
function testing(callback) {
return callback();
}
callback() is called with implicit baseValue undefined, i.e. undefined.callback(),
and since the passed function is declared within class
testing(function() {
console.log(this.name);
})
that triggers the strict mode of code execution, that's why undefined is not converted again to global window, thus we have the same error as before.
CASE 3:
Arrow function
testing(() => {
console.log(this.name);
})
creates a hard binding from the this in enclosing scope,
basically under the hood it's the same as writing:
var _this = this;
testing((function() {
console.log(_this.name);
});
That's why you get the same object resolved as this
CASE 4:
Alright, this one is interesting and needs more mechanics explanation.
So when you pass this.method in:
render() {
testing(this.method1)
}
what you actually pass is not the reference this.method, but the actual underlying Function Object value, to which this reference points to, so
when it gets executed it has its this always pointing to undefined, here look, so it's pretty much "in stone".
And yes of course since this.method1 is declared in strict context again, thanks to enclosing es6 class, undefined remains undefined without conversion to global window.
CASE 5:
Same mechanics as with arrow function. Bind creates a wrapper function, which holds the cached this value, which is not possible to override with .call and .apply, the same as in => function.
Hope this clarifies a bit it all a bit.

Related

Can I add a method on es6 class after it is defined?

Method
method() {}
function
function func() {}
Above is just to elaborate difference between method and function.
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
method1(){}
}
In the above class, after writing the definition.
I want to add a method2 to the class, similar to the way method1 is there.
I can add a function like soo
Student.prototype.func = function(){...}
But I do not have a way to add a method on the same class. and inside function I will not be able to use super as that is just available inside the method.
Is there a way I can add method after the class is defined ?
So that I will be able to use super inside that.
As has already been explained, you can only use super() inside the regular class definition. But, long before we had ES6, we were calling parent method implementations manually. It can be done using the parent's prototype:
class Person {
talk() {
// some implementation here
}
}
class Student extends Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Student.prototype.talk = function(data) {
// now call base method manually
Person.prototype.talk.call(this, data);
// then do our extra work
log(data);
}
Of course, normally you could just declare all your methods within the class declaration so this would not be something you would normally need to do.
Your snippet adding a new property to the prototype is only approach for adding a function later. One main difference in this case is that simple assignment like that will create the property as enumerable by default, whereas class syntax would create is as non-enumerable. You could use
Object.defineProperty(Student.prototype, "func", {
configurable: true,
writable: true,
value: function() {
},
});
to address that at least.
Unfortunately as you've seen, adding things to the prototype afterward does not allow usage of super.foo. There is no way for this to be supported, because the behavior of super is based specifically on the lexical nesting of the method syntax method(){} being inside of the class syntax. Methods added programmatically later on would have no way to know which prototype is the "super" one.

what happens in react when setState with object instance of a class

I have this fiddle
let m = new Mine();
this.setState(m, () => {
console.log('1:', m instanceof Mine, m.x, m.meth);
// => 1: true 123 function meth() {}
console.log('2:', this.state instanceof Mine, this.state.x, this.state.meth);
// => 2: false 123 undefined
});
As you can see I create an instance of the Mine class and then set state in a react component with that instance.
I would expect this.state to contain exactly that instance but while the instance properties that are set in the constructor are available I can't access any of the class methods on that instance.
The test in the fiddle shows that this.state is not an instance of the class Mine.
Does anybody understand what is going on or is this unintended behavior?
After more investigation I found out the reason why that happens.
The function _processPendingState from react uses Object.assign to set the new state, so since the target object is a new object (different than what is passed to setState) the new state loses the quality of being an instance of the "Mine" class.
And because Object.assign only copies own enumerable properties from the sources to the target the new state also won't have the class methods.
If in the fiddle we replace the line...
let m = new Mine();
with...
let m = {x: 123};
Object.defineProperty(m, 'meth', {
enumerable: false,
get() { return function() {}; }
});
we still don't have the "meth" property on the resulting state. Even if "m" owns the "meth" property it is not enumerable.
The best solution is to surface the method as an arrow function:
class Blah {
constructor() {
// no definition here for surfacedMethod!
}
surfacedMethod = () => {
// do something here
}
}
Then you can set instances of this class in setState and use their methods as if they were attributes set on the instance.
// other component innards
this.setState(state => ({blah: new Blah()}))
// later
this.state.blah.surfacedMethod(); // this will now work
In such case use replaceState, it should work.

Class definition order

Can someone explain why the following typescript code compiles? It seems to me that it could never successfully run.
class Xyz
{
static x : Abc = new Abc();
}
class Abc
{
}
There is no compile-time error, since it is equivalent to this syntactically valid JavaScript
var Xyz = (function () {
function Xyz() {
}
Xyz.x = new Abc();
return Xyz;
})();
var Abc = (function () {
function Abc() {
}
return Abc;
})();
but it will have a runtime error since you try to instantiate a member of Abc before Abc is defined.
TypeScript doesn't do any enforcement of ordering of constructs in your code. Consider some slight variant that would be valid -- it's not immediately obvious what things should be allowed or disallowed in terms of ordering.
class Xyz
{
static x = () => new Abc();
}
class Abc
{
}
There's an issue tracking adding this as an option for the straightforward cases.

Implementing TypeScript interface with bare function signature plus other fields

How do I write a class that implements this TypeScript interface (and keeps the TypeScript compiler happy):
interface MyInterface {
(): string;
text2(content: string);
}
I saw this related answer:
How to make a class implement a call signature in Typescript?
But that only works if the interface only has the bare function signature. It doesn't work if you have additional members (such as function text2) to be implemented.
A class cannot implement everything that is available in a typescript interface. Two prime examples are callable signatures and index operations e.g. : Implement an indexible interface
The reason is that an interface is primarily designed to describe anything that JavaScript objects can do. Therefore it needs to be really robust. A TypeScript class however is designed to represent specifically the prototype inheritance in a more OO conventional / easy to understand / easy to type way.
You can still create an object that follows that interface:
interface MyInterface {
(): string;
text2(content: string);
}
var MyType = ((): MyInterface=>{
var x:any = function():string { // Notice the any
return "Some string"; // Dummy implementation
}
x.text2 = function(content:string){
console.log(content); // Dummy implementation
}
return x;
}
);
There's an easy and type-safe way to do this with ES6's Object.assign:
const foo: MyInterface = Object.assign(
// Callable signature implementation
() => 'hi',
{
// Additional properties
text2(content) { /* ... */ }
}
)
Intersection types, which I don't think were available in TypeScript when this question was originally asked and answered, are the secret sauce to getting the typing right.
Here's an elaboration on the accepted answer.
As far as I know, the only way to implement a call-signature is to use a function/method. To implement the remaining members, just define them on this function. This might seem strange to developers coming from C# or Java, but I think it's normal in JavaScript.
In JavaScript, this would be simple because you can just define the function and then add the members. However, TypeScript's type system doesn't allow this because, in this example, Function doesn't define a text2 member.
So to achieve the result you want, you need to bypass the type system while you define the members on the function, and then you can cast the result to the interface type:
//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => {
//"any" type specified to bypass type system for next statement.
//Defines the implementation of the call signature.
var result: any = () => "Hello";
//Defines the implementation of the other member.
result.text2 = (content: string) => { };
//Converts the temporary variable to the interface type.
return <MyInterface>result;
})(); //Invokes the closure to produce the implementation
Note that you don't need to use a closure. You could just declare your temporary variable in the same scope as the resulting interface implementation. Another option is to name the closure function to improve readability.
Here's what I think is a more realistic example:
interface TextRetriever {
(): string;
Replace(text: string);
}
function makeInMemoryTextRetriever(initialText: string) {
var currentText = initialText;
var instance: any = () => currentText;
instance.Replace = (newText: string) => currentText = newText;
return <TextRetriever>instance;
}
var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");

Function Downcasting in UnityScript

I actually posted this on the Unity forums, but none of my language-related questions ever seem to get answered there. So, let's say I have a function defined as so in Unity Script:
function GetSomething : SomeClass
{
return new SomeClass();
}
Where SomeClass is some class defined elsewhere. Now, I have a variable, theFunction, of type Function, and I want to make sure that it returns something, anything. So what I do is the following:
// theFunction is set to GetSomething somewhere else in the program.
var functionThatReturnsSomething = theFunction as function() : Object;
if (functionThatReturnsSomething != null)
//... call it and do stuff with the returned value.
Now unfortunately, in the above code functionThatReturnsSomething will be null. For it not to be null, I have to be more specific and cast to function() : SomeClass OR just override the function definition to return an Object as so:
function GetSomething : Object
{
return new SomeClass();
}
This is very annoying because its easy to forget to do :Object (especially since if you leave it out it will correctly infer it to be of return type SomeClass), and the result is not an error, but rather a very subtle bug since the cast fails. Is there any way to get the behavior I want, which is for it to properly downcast to function() : Object, the same way I can downcast normal objects?
The only thing i can think of is you forgot the parenthesis after the function name when you declare it. If i put everything into the following script, attach it to an object and run it then i get access to the function with no problems.
#pragma strict
import System.Collections.Generic;
function Start ()
{
// theFunction is set to GetSomething somewhere else in the program.
var functionThatReturnsSomething = GetSomething as function() : Object;
if (functionThatReturnsSomething != null)
{
//... call it and do stuff with the returned value.
Debug.Log(functionThatReturnsSomething);
}
else Debug.Log("null");
}
function GetSomething() : List.<int>
{
return new List.<int>();
}