Arrow and Lexical Scope in JS - javascript

Given the following code:
const outter = {
inner: {
laughter: "hahaha",
laugh: () => {
console.log(this) //`this` is window
}
}
}
const x = {
test: function () {
const laugh = outter.inner.laugh
console.log(this) //`this` is x
laugh()
}
}
x.test()
I would expect the this inside of the laugh method to be equal to x - because we're invoking laugh from x
In the scope where we invoke laugh, this is equal to x - so it's strange that this changes in the arrow method, which should follow lexical scope.
(note, even if we change laugh to a normal function, non-arrow, this is still Window - but that makes sense)
Can anyone explain why this inside of laugh is Window?
Thanks

According Jason Orendorff, the guy who implemented arrow functions for Firefox, wrote the following (here):
Arrow functions do not have their own this value. The value of this inside an arrow function is always inherited from the enclosing scope.
The arrow function is not assigned when invoked, rather, when it's defined.
In your example, the arrow function of the laugh property will be bound to the this in the scope it was defined in, which in your example is window.
Update
The OP remarked in the comments that since the arrow function is found inside an inner property of the outter [sic] object, it should pick up the scope of that object. Superficially, this is the way it should be.
The caveat is that when creating objects, JS has to assign the property values from the inside out. In other words, the inner object literal is created first, at a time when the context of outter doesn't even exist yet!. And, therefore, since arrow functions get bound at the time of creation, the function for the outter.inner.laugh method is being bound with the context that it is found in; at the time of creating outter.inner, that would be window.

Arrow Functions:
Do not have this.
Do not have arguments.
Can’t be called with new.
They also don’t have super.
Arrow functions have no “this”
If this is accessed, it is taken from the outside.
const outter = {
inner: {
laughter: "hahaha",
laugh: () => {
console.log(this) // the outer `this` is window.
}
}
}
Read more here.

Related

Callback arrow function does not inherit 'this' from it's parent function **this is not a duplicate**

This is not a duplicate, please do not close it again.
I went through What does "this" refer to in arrow functions in ES6? but didn't find an answer.
class A{
static myf(test){
console.log(this); //when A.myf executes, logs 'A'
test();
}
}
A.myf(()=>{
console.log(this); // logs 'window'
})
Could someone help me with this? In the case above, the arrow function's lexical environment is A.myf, and 'this' of the arrow function should inherit from 'this' of myf. so why logs 'window' instead of A?
A new Lexical Environment is created whenever another block is entered.
Blocks are delimited by {s and }s - usually seen at the beginning of functions function foo() { or at the beginning of loops for (...) { while (...) {. (Object literals are not blocks.)
You're correct when you say
As far as I know, arrow functions' 'this' inherits scope from its lexical environment.
There are 2 such environments here (which can be visualized as containers mapping identifier names to their values in that block): the environment at the top level, and the environment inside the callback:
// Here is the outer environment
A.myf(()=>{
// Here is the inner environment
console.log(this); // logs 'window'
})
With arrow functions, just look to the outer environment's this to see what the inner environment's this refers to:
const outerThis = this;
A.myf(()=>{
console.log(outerThis === this); // this will ALWAYS be true
// if the block is created from an arrow function
})
In sloppy mode, this is the global object on the top level, so this is window inside the callback.

Why doesn't JS always bind `this` to `foo` when foo.fn() is called?

I think JavaScript ES6's arrow functions have two changes:
get rid of the "lost binding issue".
make this not bind to foo when foo.fn() is called.
For (1) above, the following code can illustrate (running inside of Node console):
var peter = {
name: "Peter Pan",
greetLater: function() {
setTimeout(function() {
console.log("Hi my name is", this.name);
}, 0);
}
};
peter.greetLater(); // => Hi my name is undefined
var michael = {
name: "Michael J Fox",
greetLater: function() {
setTimeout(() => {
console.log("Hi my name is", this.name);
}, 0);
}
};
michael.greetLater(); // => Hi my name is Michael J Fox
For (2) above, the following code can illustrate:
var winona = {
name: "Winona Ryder",
greet: function() {
console.log("Hi my name is", this.name);
}
};
winona.greet(); // => Hi my name is Winona Ryder
var demi = {
name: "Demi Moore",
greet: () => {
console.log("Hi my name is", this.name);
}
};
demi.greet(); // => Hi my name is undefined
I can understand (1) is a good thing as it solves the "lost binding" issue. But (2) is like "cutting the binding". It might seem that (2) is to solve the issue of (1), which is to go up the lexical scope to find this, but I think having (1) and not having (2) can co-exist: that is, when foo.fn() is called, the this inside the code of fn() can still refer to foo.
What is wrong with when foo.fn() is called, the this inside the code of fn() can still refer to foo, and when it is "dangling function", as in the SetTimeout() in (1) above, or in the following code:
winonaGreet = winona.greet;
winonaGreet(); // => Hi my name is undefined
then it uses the new behavior of going up the lexical scope to find the this? Isn't this more coherent with everything?
That is, when it is fn() or passed to SetTimeout and stored as fn and later invoked as fn(), then look up the lexical scope, but for foo.fn(), it just make a lot of sense to bind this to foo instead of looking up the lexical scope.
Why would we have 2 new rules, one to keep the binding and one to cut it?
And it seems for (2), the ways we can still call foo.fn() and have this inside of fn() bind to foo is to not use arrow function, or use ES6's class, as in Mozilla's example:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}
but then, when we have (1), why do we have (2) also? It is like a hybrid of most other languages, where if foo.fn() is called, the this is always bound to foo.
this and Arrow Functions:
Arrow functions, introduced in ES6, provides a concise way to write functions in >JavaScript.
Another significant advantage it offers is the fact that it does not bind its own this. In other words, the context inside arrow functions is lexically or statically defined.
What do we mean by that?
Unlike other functions, the value of this inside arrow functions is not dependent on how they are invoked or how they are defined.It depends only on its enclosing context.
Thus, regardless of whether the arrow function was called using function invocation or method invocation, it retains the value of this from its enclosing context.In other words, an arrow function’s this value is the same as it was immediately outside it.
If used outside any enclosing function, an arrow function inherits the global context, thereby setting the value of this to the global object.
In classic function expressions, the this keyword is bound to different values based on the context in which it is called.
There are a few subtle differences in behavior between ordinary function functions and arrow functions. Arrow functions do not have their own this value. The value of this inside an arrow function is always inherited from the enclosing scope.
Arrow Functions lexically bind their context so this actually refers to the originating context.
There are a few other differences: arrow functions don’t get their own arguments, super, or new.target keywords. Arrow functions are anonymous, which means that they are not named, so if your function needs to have a self-reference at any point (e.g. recursion, event handler that needs to unbind), it will not work.
More info https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/
A scope in JS is associated to a function. When you create an object using new, this is created in constructor.
const foo = new Foo();
foo.fn()
In above code, context is created in constructor and fn derives its context from foo.
How does this work?
A function has an internal property [[prototype]], which stores context. By default it is not set (or set to default global scope) and it derives/ overrides context via caller. So in foo.fn(), context refers to foo. But if you do var fn = foo.fn; fn(), here context is of global/default scope.
To avoid this, we use Arrow function. Arrow function does not have an internal context and uses the context it is defined in. So above code var fn = foo.fn; fn(); will work.
However, if you define Arrow function inside an object literal, it will still point to the parent scope it was defined in and will not fallback to object.
Sample:
function Foo1() {
this.fn = function () {
console.log(this.constructor.name, this);
}
}
var foo2 = {
fn: function() {
console.log(this.constructor.name, this);
}
}
function test() {
var foo3 = {
fn: function() {
console.log(this.constructor.name, this);
}
}
foo3.fn();
}
function test2() {
var foo3 = {
fn: () => {
console.log(this.constructor.name, this);
}
}
foo3.fn();
}
var foo1 = new Foo1();
foo1.fn();
foo2.fn();
test();
test2();

Use of .call(this) on IIFE

I've seen an IIFE wrapped up with .call(this), rather than just (). Why would this be done?
The context: https://github.com/dmauro/Keypress/blob/development/keypress.js
(As I understand it, this would refer to the window object - which is where the IIFE would be called anyway. It seems redundant.)
If the IIFE is invoked without using call, the this inside it would refer to the global object window (or undefined in strict mode). Using .call(this) will pass to it whatever this is refereing to at the moment of the call (whatever the current context is). A situation where you want to use .call(this) instead of just calling it normally (), for example, is inside a class method where this will refer to the instance of the class and you want to pass that to your IIFE:
function A() {
(function() {
this.name = "A"; // "this" is not the instance of the class A
})();
}
var a = new A;
console.log(a.name);
function B() {
(function() {
this.name = "B"; // "this" is the instance of the class B
}).call(this); // because we explicitly passed it to the IIFE
}
var b = new B;
console.log(b.name);
Note:
It's worth mentioning that with arrow functions you can have the benifit of using the this of the enclosing execution without having to use .call(this) because arrow functions don't have their own this (they don't bind this):
function C() {
(() => {
this.name = "C"; // "this"'s value here is whatever value it has outside the IIFE
})(); // no need for .call(this) here
}
var c = new C;
console.log(c.name);
It's just a way of passing the value of lexical this into the IIFE - nothing more so that this inside the IIFE will have the same lexical value it had in the surrounding code. Depending upon where the IIFE is, this may not be the window object. It may have an actual lexical and local value.
If you don't use .call(this) on the IIFE, then the this value will be reset in the function to either the global object (which is the window object in a browser) or in strict mode, this will become undefined in the IIFE. Javascript resets the value of this according to how a function was called. You can see all the six different ways that this is controlled in this answer:
When you pass 'this' as an argument.
To tell you more about your situation, we'd have to see the actual code in context.
In the particular code you added a link to, I don't see any particular reason for doing it as I don't see any references to this at the top level of the IIFE. It appears to just be a coding habit that is generally used and is useful in some circumstances, but not needed or useful in others.

Shadowing `this` - lexical name resolution

EDIT: I think this isn't a duplicate; it being a hidden parameter, I wanted to ask about shadowing in relation to this, and lexical scoping, having read this SO Q/A re. shadowing.
I had thought that the meaning of the name this might be resolved kind of dynamically (in terms of scoping), so as to explain why it doesn't seem to me to be resolved lexically:
function foo() {return this;}
var a = {f: foo};
var b = {f: foo};
a.f() !== b.f();
a.f() !== foo();
a.f() !== window; //not strict mode
But then I read that every function receives this as an additional parameter, silently. (I realise arrow functions are different.)
Obviously helper() doesn't work as we might hope:
ob = {
meth: function(){
var helper = function() {return this;};
return helper();
}
};
ob.meth(); //Window or undefined
As far as I understand, rather than this being resolved by looking at the enclosing scope (the result being ob), instead the interpreter is calling helper() with this set to undefined (strict mode), silently passed as an argument.
So is the surrounding scope's this effectively being shadowed, hence lexical scoping is in fact in action?
You are correct. Except in the case of arrow functions, this is never resolved lexically. It always refers to one of the following:
the object upon which a function was called, such as valueOfThis.foo()
the first argument to apply or call, such as foo.apply(valueOfThis, params) or foo.call(valueOfThis, ...).
the DOM element, in the case of event handlers, such as <button onclick="alert(this.tagName.toLowerCase());"/>
the object being constructed, when a function is used as a constructor, such as Foo = function(){ ... }; new Foo()
the object bound to the function, if the function was created using bind, such as bar = function(){ ... this ...}; foo = bar.bind(valueOfThis); foo()
in a getter/setter, this refers to the object for which the property is being accessed or set, such as valueOfThis.someProperty = 123
window (usually) if none of the above cases, such as foo()
#Bergi provided a great reference for all these in the comments below.
In the case of bound functions, the function being called is actually a different function than was passed to the bind method, because the bind method creates a new function
So is the surrounding scope's this effectively being shadowed, hence lexical scoping is in fact in action?
Yes, one could view it from that perspective. The this bound to the helper scope shadows the one bound to the meth scope. (And if you had used an arrow function, it wouldn't).
However, you still need to remember that this is not an ordinary variable but a special keyword. It is only bound to function scopes, it's not writable, it has weird coerce-to-object semantics in sloppy mode, and it's always implicitly bound - as you say, a hidden parameter.
Unless you're trying to understand how this works in arrow functions (and their lexical resolution), the analogy with scopes and shadowing is pretty useless.

Understanding Javascript scope with "var that = this" [duplicate]

This question already has answers here:
In Javascript, why is the "this" operator inconsistent?
(8 answers)
Closed 9 years ago.
Say I have the following property method in an object:
onReady: function FlashUpload_onReady()
{
Alfresco.util.Ajax.jsonGet({
url: Alfresco.constants.PROXY_URI + "org/app/classification",
successCallback: {
fn: function (o) {
var classButtonMenu = [],
menuLabel, that = this;
var selectButtonClick = function (p_sType, p_aArgs, p_oItem) {
var sText = p_oItem.cfg.getProperty("text");
that.classificationSelectButton.set("label", sText);
};
for (var i in o.json.items) {
classButtonMenu.push({
text: o.json.items[i].classification,
value: o.json.items[i].filename,
onClick: {fn: selectButtonClick}
});
}
this.classificationSelectButton = new YAHOO.widget.Button({
id: this.id + "-appClassification",
type: "menu",
label: classButtonMenu[0].text,
name: "appClassification",
menu: classButtonMenu,
container: this.id + "-appClassificationSection-div"
});
},
scope: this
},
failureMessage: "Failed to retrieve classifications!"
});
It took me some guess work to figure out that in the selectButtonClick function that I needed to reference that instead of this in order to gain access to this.classificationSelectButton (otherwise it comes up undefined), but I'm uncertain as to why I can't use this. My best guess is that any properties in the overall object that gets referenced within new YAHOO.widget.Button somehow looses scope once the constructor function is called.
Could someone please explain why it is that I have to reference classificationSelectButton with var that = this instead of just calling `this.classificationSelectButton'?
The most important thing to understand is that a function object does not have a fixed this value -- the value of this changes depending on how the function is called. We say that a function is invoked with some a particular this value -- the this value is determined at invocation time, not definition time.
If the function is called as a "raw" function (e.g., just do someFunc()), this will be the global object (window in a browser) (or undefined if the function runs in strict mode).
If it is called as a method on an object, this will be the calling object.
If you call a function with call or apply, this is specified as the first argument to call or apply.
If it is called as an event listener (as it is here), this will be the element that is the target of the event.
If it is called as a constructor with new, this will be a newly-created object whose prototype is set to the prototype property of the constructor function.
If the function is the result of a bind operation, the function will always and forever have this set to the first argument of the bind call that produced it. (This is the single exception to the "functions don't have a fixed this" rule -- functions produced by bind actually do have an immutable this.)
Using var that = this; is a way to store the this value at function definition time (rather than function execution time, when this could be anything, depending on how the function was invoked). The solution here is to store the outer value of this in a variable (traditionally called that or self) which is included in the scope of the newly-defined function, because newly-defined functions have access to variables defined in their outer scope.
Because this changes its value based on the context it's run in.
Inside your selectButtonClick function the this will refer to that function's context, not the outer context. So you need to give this a different name in the outer context which it can be referred to by inside the selectButtonClick function.
There's lexical scope: variables declared in functions and arguments passed to functions are visible only inside the function (as well as in its inner functions).
var x = 1; // `1` is now known as `x`
var that = this; // the current meaning of `this` is captured in `that`
The rules of lexical scope are quite intuitive. You assign variables explicitly.
Then there's dynamic scope: this. It's a magical thing that changes it's meaning depending on how you call a function. It's also called context. There are several ways to assign a meaning to it.
Consider a function:
function print() { console.log(this); }
Firstly, the default context is undefined in strict mode and the global object in normal mode:
print(); // Window
Secondly, you can make it a method and call it with a reference to an object followed by a dot followed by a reference to the function:
var obj = {};
obj.printMethod = print;
obj.printMethod(); // Object
Note, that if you call the method without the dot, the context will fall back to the default one:
var printMethod = obj.printMethod;
printMethod(); // Window
Lastly, there is a way to assign a context is by using either call/apply or bind:
print.call(obj, 1, 2); // Object
print.apply(obj, [ 1, 2 ]); // Object
var boundPrint = print.bind(obj);
boundPrint(); // Object
To better understand context, you might want to experiment with such simple examples. John Resig has very nice interactive slides on context in JavaScript, where you can learn and test yourself.
Storing it in a variable lets you access it in other scopes where this may refer to something else.
See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this, http://www.quirksmode.org/js/this.html and What is the scope of variables in JavaScript? for more information about the this keyword.
The this reference doesn't work when a method of a class is called from a DOM event. When a method of an object is used as an event handler for onclick, for example, the this pointer points to the DOM node where the event happened. So you have to create a private backup of this in the object.
this is a keyword in javascript, not a default variable defined within every function, hence, as Gareth said, this will refer to the context in which the function is invoked, or the global object if there's no context.

Categories

Resources