What are ES6 arrow functions, how do they work? [duplicate] - javascript

This question already has answers here:
Are 'Arrow Functions' and 'Functions' equivalent / interchangeable?
(4 answers)
Closed 6 years ago.
I am curious about ES6 arrow functions (fat arrow functions). Are they simply syntactic sugar derived from CoffeeScript, or is there more to them than meets the eye?

ES6 Arrow functions in depth
One of the prettiest features of ES6, it could easily win a beauty contest, if such a contest would be held. What many people don’t know is that the arrow function is not simply a form of syntactic sugar that we can use instead of the regular callback.
As I like to explain it to the people who attend my trainings/workshops, arrow functions are this-less, arguments-less, new.target-less and super-less.
Let us now get past the shorter syntax and dive deeper into the specifics of the arrow function.
Lexical-bound this
Previously, regular functions would have their this value set to the global object if they were used as callbacks, to a new object in case they were called with the new operator or, in the case of libraries like jQuery, they would be set to the object that triggered an event in case of event handlers, or the current element in a $.each iteration.This situation proved very confusing even for experienced developers.
Let’s say you have a piece of code like the one below.
var obj = {
nameValue: 'default',
initializeHandlers: function() {
var nameInput = document.querySelector('#name');
nameInput.addEventListener('blur', function(event) {
this.nameValue = event.target.value;
});
}
};
obj.initializeHandlers();
The problem is that this inside the blur event handler is set to the global object rather than obj. In strict mode — ‘use strict’; — you risk breaking your application because this is set to undefined. In order to side-step this issue we have two options:
Convert the event handler to a function bound to the outer scope, using Function.prototype.bind
Use the dirty var self = this; expression in the initializeHandlers function (I see this as a hack)
Both options are illustrated below.
[...]
initializeHandlers: function() {
var nameInput = document.querySelector('#name');
// more elegant but we can do better
var blurHandler = function(event) {
this.nameValue = event.target.value;
}.bind(this)
nameInput.addEventListener('blur', blurHandler);
}
[...]
[...]
initializeHandlers: function() {
var nameInput = document.querySelector('#name');
// ugly and error-prone
var self = this;
nameInput.addEventListener('blur', function(event) {
self.nameValue = event.target.value;
});
}
[...]
On the other hand, arrow functions have no internal context. They inherit their context from the outer scope. Let’s take a look at how arrow functions solve this problem.
const obj = {
nameValue: 'default',
initializeHandlers: function() {
const nameInput = document.querySelector('#name');
nameInput.addEventListener('blur', (event) => {
// this references obj instead of the global object
this.nameValue = event.target.value;
});
}
};
In our new implementation this is a hard reference to the obj object and doesn’t get lost due to nesting.
Lexical arguments
Have you ever tried to access the arguments object inside an arrow function? I have, and I wasted 3 solid hours trying to figure out why do I get the arguments of the outer function instead of those of the arrow functions.
Thankfully, MDN exists, and as good practice dictates, you check the documentation at the end, when you sit in a corner, knees tucked to your chest, rocking and repeating to yourself: “I should have been a carpenter!”
Fun aside, arrow functions do not expose an arguments object. If you try to access it, you will get the arguments of the surrounding function. In our case, given the fact that the outer function is an arrow function as well, and we have no more functions further up the chain, we will get a ReferenceError.
const variadicAdder = (x) => {
return () => {
let args = Array.prototype.slice.call(arguments, 0);
return args.reduce((accumulator, current) => {
return accumulator + current;
}, x);
}
}
const variadicAdderOf5 = variadicAdder(5);
console.log(variadicAdderOf5(10, 11, 12));
// ReferenceError: arguments is not defined
There is no fix here, as there is nothing broken. What we can do is to return a plain function, rather than an arrow, from our variadicAdder().
This will give us the opportunity to access the arguments object without an issue. The updated code will look like the one below with the only difference
that it will actually work and not throw an error.
const variadicAdder = (x) => {
return function() {
let args = Array.prototype.slice.call(arguments, 0);
return args.reduce((accumulator, current) => {
return accumulator + current;
}, x);
}
}
const variadicAdderOf5 = variadicAdder(5);
console.log(variadicAdderOf5(10, 11, 12));
// 38
To find out more about Array.prototype.reduce, head to the Mozilla Developer Network.
Other characteristics
As I mentioned in the introductory section of this article, arrow functions have several more characteristics besides the context and the arguments.
The first thing I would like to mention is that you are unable to use the new operator with arrow functions. As a direct implication, arrow functions also don’t have super(). Snippets like the one below would simply throw a TypeError.
const Person = (name) => {
this.name = name;
};
let p = new Person('John');
// TypeError: Person is not a constructor
The third characteristic, which is as well, a direct implication of the inability to use the new operator, is the fact that arrow functions don’t have new.target. In a nutshell, new.target allows you to detect whether or not a function has been called as a constructor.
Arrow functions, inherit new.target from their surrounding scope. If the outer scope is a function, and it is called like a constructor (e.g. new Person('Adrian');), then new.target will point to the outer function.
The Mozilla Developer Network hosts a detailed explanation on new.target and I encourage you to check it out.
This article is also published on my blog, here: /es6-arrow-functions-in-depth/

Related

JavaScript Object Constructors [duplicate]

This question already has answers here:
Methods in ES6 objects: using arrow functions
(6 answers)
Closed last year.
Can someone please explain why defining a prototype function with lambda expression doesn't work? I thought this must be asked before but couldn't find it.
function Book(title, year) {
this.title = title;
this.year = year;
// define a function within the object, which works fine
this.printYear = () => console.log("instance function of an object: " + this.year);
}
this doesn't work
Book.prototype.printTitle2 = () => {
console.log(this.title);
}
and this is fine of course:
Book.prototype.printTitle = function() {
console.log(this);
console.log(this.title);
}
One of the chief features of arrow functions is that they close over the this from the context in which they're created; they don't get it based on how they're called like other functions do. So...
// ...whatever `this` is *here*
Book.prototype.printTitle2 = () => {
// ...is what `this` will be *here*
console.log(this.title);
};
But your function relies on this varying depending on how it's called.
This just isn't a use-case for arrow functions. Use a normal function:
Book.prototype.printTitle2 = function() {
console.log(this.title);
};
Or better yet, use the new class syntax:
class Book {
constructor(title, year) {
this.title = title;
this.year = year;
}
printTitle2() {
console.log(this.title);
}
}
The Arrow function would resolve the context this belongs to the scope where the function was defined. I believe you have defined that function in window scope. So the this will point to window in your function.
You can use normal anonymous function here. And we have to be careful while using arrow functions.
In addition to #t-j-crowder's answer, I wanted to leave a test case (mocha assert) which you can use to visualize which is not working.
Also you can read more about the scope of arrow functions here:
You Don't Know JS
by Kyle Simpson, who explains this in detail.
Basically, an arrow function's this points to the surrounding context of the current context, which comes in handy if you have enclosing functions.
What it does is basically doing the var self = this; thing.
Or as Kyle says:
[...]
Lexical this in the arrow function callback in the previous snippet now points to the same value as in the enclosing makeRequest(..) function. In other words, => is a syntactic stand-in for var self = this.
In cases where var self = this (or, alternatively, a function .bind(this) call) would normally be helpful, => arrow functions are a nicer alternative operating on the same principle. [...]
You can test it yourself with my gist:
https://gist.github.com/jay-bricksoft/96738dd8a48ceb9f517e914b834cc1ee
In my test case this was the output:
Lambda
function(){}
√ should equal function´s type
1) should have equal context as function
2) should be able to be used as constructor
1 passing (39ms)
2 failing
1) Lambda function(){} should have equal context as function:
AssertionError: 'undefined' == 'string'
+ expected - actual
-undefined
+string
at Context.<anonymous> (test\index.js:29:14)
2) Lambda function(){} should be able to be used as constructor:
TypeError: construct_l is not a constructor
at Context.<anonymous> (test\index.js:34:20)
EDIT: added example / reference to Kyle Simpson's "You Don't Know ES6"
https://github.com/getify/You-Dont-Know-JS/tree/master/es6%20%26%20beyond

Object method functions: Anonymous or named? [duplicate]

This question already has answers here:
When should I use arrow functions in ECMAScript 6?
(9 answers)
Closed 6 years ago.
I've been seeing both types of code and I'm wondering if there is a preference; Using anonymous or named functions:
function myFunc() {
this.myMethod = () => {
//..
}
}
function myFunc() {
this.myMethod = function() {
//..
}
}
Taken from MDN:
An arrow function expression has a shorter syntax compared to function
expressions and does not bind its own this, arguments, super, or
new.target. Arrow functions are always anonymous. These function
expressions are best suited for non-method functions and they can not
be used as constructors.
It makes sense to us anonymous as you might want to access myFunc properties without having to do _this = this. On the other hand it states anonymous functions are best suited for non-method functions (i.e. callbacks).
Those are not contradictory.
It makes sense to use anonymous arrow functions as you might want to access myFunc instance properties without having to do _this = this.
Yes. Though if it was a method, you could simply use this within the function expression and it would work.
On the other hand it states anonymous functions function expressions are best suited for non-method functions (i.e. callbacks).
The "non-method" refers to functions that are not (always) invoked using the object.method(…) pattern that does set the this keyword. It does not matter whether the function is stored as an object property or not.
Btw, none of these points have anything to do with named vs anonymous expressions.
Technically - it doesn't matter.
var myFunction = function() { } //anonymous
and
var myFunction = function myFunction() { } //named
Will be identical in all regards but one - using debug tools and looking at stack traces will show different identifiers. The first version will show up as (anonymous function) while the latter will show up with its name - myFunction. So, named functions are purely for developer convenience and for development.
Worth noting that the name of the function does not need to be the same as the reference to it, for example you can have
var myFunction = function someOtherName() { /* ... */ }
and then this will show up as someOtherName in dev tools. However you will not be able to call it by doing someOtherName() - the name and the reference to the function are different things. For simplicity they are usually set to the same identifier.
Now, onto your example - there is a difference to what you posted
function myFunc() {
this.myMethod = () => {
//..
}
}
This is not equivalent to a named function. This is using the ES6 arrow functions - normally, they will be named the same as the variable they are assigned to:
var arrowFunction = () => {};
var obj = {
arrowMethod: () => {}
}
console.log("function name is: " + arrowFunction.name);
console.log("object property function name is: "+ obj.arrowMethod.name);
(note that this works in Chrome but not in Firefox for some reason - the .name property is supposed to be set)
Further to the naming differences, arrow functions have other differences to a "plain" function. Most notably, their context is lexically bound. Here is what this means in practice
function arrowExample() {
this.data = "Hello arrow";
this.myMethod = () => {
return this.data;
}
}
function normalFunctionExample() {
this.data = "Hello normal function";
this.myMethod = function myMethod() {
return this.data;
}
}
var arrowInstance = new arrowExample();
var normalFunctionExampleInstance = new normalFunctionExample();
console.log("Invoking arrow with context: " + arrowInstance.myMethod());
console.log("Invoking normal function with context: " + normalFunctionExampleInstance.myMethod());
var arrowReference = arrowInstance.myMethod;
var normalFunctionReference = normalFunctionExampleInstance.myMethod;
console.log("Invoking arrow without context: " + arrowReference());
console.log("Invoking normal function without context: " + normalFunctionReference());

what is the meaning of this? setTimeOut(() => this.active=true, 0)

I am practicing angularjs2 and I notice this sentence but cannot understand what this means.
#Component({
selector: 'hero-form',
templateUrl: 'app/hero-form.component.html'
})
export class HeroFormComponent {
model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');
active = true;
newHero() {
this.model = new Hero(42, '', '');
this.active = false;
setTimeOut(()=> this.active=true, 0)*
}
}
I know in JavaScript there is a function name setTimeOut but cannot understand the () and arrow =>...
Thanks in advance!
This is the new JavaScript arrow function notation. The line you quoted is almost equivalent to this piece of code in the traditional function notation:
setTimeout(function() {
this.active = true;
}, 0);
However in the traditional notation, this will be bound to the global object (outside of strict mode - in strict mode, you will get a ReferenceError), due to how binding of this in execution contexts works. The traditional workaround before the introduction of arrow functions would be:
var self = this;
setTimeout(function() {
self.active = true;
}, 0)
Arrow functions solve this problem by lexically binding this, just as any other variable, instead of defining their own. In addition to how this is treated, arrow functions do not implicitly define their own arguments or super variables either, but bind them lexically.
Digging deeper, what does newHero() do? It is a constructor function. When you call it, a new object is allocated, and it can be referred to within the function body by the variable this. The constructor sets two properties on the this object; these properties are .model and .active, being given specific values.
The expression (() => this.active = true) creates a function object. It describes what should execute at a later time. When the function object is called, the body gets executed. So the line of code creates the function object and gives it to setTimeout(), which will call the function object after the given length of time - in this case, 0 milliseconds.

Why cannot use lambda to define prototype function [duplicate]

This question already has answers here:
Methods in ES6 objects: using arrow functions
(6 answers)
Closed last year.
Can someone please explain why defining a prototype function with lambda expression doesn't work? I thought this must be asked before but couldn't find it.
function Book(title, year) {
this.title = title;
this.year = year;
// define a function within the object, which works fine
this.printYear = () => console.log("instance function of an object: " + this.year);
}
this doesn't work
Book.prototype.printTitle2 = () => {
console.log(this.title);
}
and this is fine of course:
Book.prototype.printTitle = function() {
console.log(this);
console.log(this.title);
}
One of the chief features of arrow functions is that they close over the this from the context in which they're created; they don't get it based on how they're called like other functions do. So...
// ...whatever `this` is *here*
Book.prototype.printTitle2 = () => {
// ...is what `this` will be *here*
console.log(this.title);
};
But your function relies on this varying depending on how it's called.
This just isn't a use-case for arrow functions. Use a normal function:
Book.prototype.printTitle2 = function() {
console.log(this.title);
};
Or better yet, use the new class syntax:
class Book {
constructor(title, year) {
this.title = title;
this.year = year;
}
printTitle2() {
console.log(this.title);
}
}
The Arrow function would resolve the context this belongs to the scope where the function was defined. I believe you have defined that function in window scope. So the this will point to window in your function.
You can use normal anonymous function here. And we have to be careful while using arrow functions.
In addition to #t-j-crowder's answer, I wanted to leave a test case (mocha assert) which you can use to visualize which is not working.
Also you can read more about the scope of arrow functions here:
You Don't Know JS
by Kyle Simpson, who explains this in detail.
Basically, an arrow function's this points to the surrounding context of the current context, which comes in handy if you have enclosing functions.
What it does is basically doing the var self = this; thing.
Or as Kyle says:
[...]
Lexical this in the arrow function callback in the previous snippet now points to the same value as in the enclosing makeRequest(..) function. In other words, => is a syntactic stand-in for var self = this.
In cases where var self = this (or, alternatively, a function .bind(this) call) would normally be helpful, => arrow functions are a nicer alternative operating on the same principle. [...]
You can test it yourself with my gist:
https://gist.github.com/jay-bricksoft/96738dd8a48ceb9f517e914b834cc1ee
In my test case this was the output:
Lambda
function(){}
√ should equal function´s type
1) should have equal context as function
2) should be able to be used as constructor
1 passing (39ms)
2 failing
1) Lambda function(){} should have equal context as function:
AssertionError: 'undefined' == 'string'
+ expected - actual
-undefined
+string
at Context.<anonymous> (test\index.js:29:14)
2) Lambda function(){} should be able to be used as constructor:
TypeError: construct_l is not a constructor
at Context.<anonymous> (test\index.js:34:20)
EDIT: added example / reference to Kyle Simpson's "You Don't Know ES6"
https://github.com/getify/You-Dont-Know-JS/tree/master/es6%20%26%20beyond

ECMAS6 arrrow function and classical function scope [duplicate]

It seems to me that, in ES6, the following two functions are very nearly identical:
function () {
return this;
}.bind(this);
() => {
return this;
};
The end result seems the same: arrow functions produce a JavaScript function object with their this context bound to the same value as the this where they are created.
Obviously, in the general sense, Function.prototype.bind is more flexible than arrow functions: it can bind to values other than the local this, and it can bind any function's this at any point in time, potentially long after it is initially created. However, I'm not asking how bind itself is different from arrow functions, I'm asking how arrow functions differ from immediately calling bind with this.
Are there any differences between the two constructs in ES6?
There are no (significant) differences.
Well, okay, that's a little premature. There are three tiny differences unique to arrow functions.
Arrow functions cannot be used with new.
This means, of course, that they do not have a prototype property and cannot be used to create an object with the classically-inspired syntax.
new (() => {}) // TypeError: () => {} is not a constructor
This is probably for the best, though—the way new works would not make much sense with bound functions.
Arrow functions do not have access to the special arguments object that ordinary JavaScript functions have access to.
(() => arguments)(1, 2, 3) // ReferenceError: arguments is not defined
This one is probably a little bit more of a gotcha. Presumably this is to remove one of JavaScript's other oddities. The arguments object is its own special beast, and it has strange behavior, so it's not surprising that it was tossed.
Instead, ES6 has splats that can accomplish the same thing without any magic hidden variables:
((...args) => args)(1, 2, 3) // [1, 2, 3]
Arrow functions do not have their own new.target property, they use the new.target of their enclosing function, if it exists.
This is consistent with the other changes to remove "magically" introduced values for arrow functions. This particular change is especially obvious, considering arrow functions can't be used with new anyway, as mentioned above.
Otherwise, arrows are just like bound functions, semantically. It's possible for arrows to be more performant, since they don't have to carry around the extra baggage and since they don't need to be converted from ordinary functions first, but they're behaviorally exactly the same.
There are a few differences:
Arrow functions cannot be constructed. While both arrow functions and bound functions both don't have a .prototype property, the former do throw an exception when called with new while the latter just ignore the bound value and call their target function as a constructor (with the partially applied bound arguments, though) on the new instance.
function F() {}
var f = () => {},
boundF = F.bind({});
console.log(new boundF(), new boundF instanceof F) // {}, true
console.log(new f) // TypeError
Arrow functions do have lexical arguments, new.target and super as well (not only lexical this). A call to an arrow function does not initialise any of those, they are just inherited from the function the arrow function was defined in. In a bound function, they just refer to the respective values of the target function.
Arrow functions don't actually bind a this value. Rather, they don't have one, and when you use this it is looked up like a variable name in the lexical scope. This does allow you to lazily define an arrow function while this is not yet available:
class X extends Object {
constructor() {
var f = () => this, // works
boundF = function(){ return this; }.bind(this);
// ^^^^ ReferenceError
super(); // initialises `this`
console.log(f(), f() == this); // {}, true
}
}
new X;
Arrow functions cannot be generator functions (though they can return generators). You can use .bind() on a generator function, yet there is no way to express this using an arrow function.
Here is one more subtle difference:
Arrow functions can return a value without using the 'return' keyword, by omitting the {} braces following the => immediately.
var f=x=>x; console.log(f(3)); // 3
var g=x=>{x}; console.log(g(3)); // undefined
var h=function(x){x}; console.log(h(3)); // undefined
var i=x=>{a:1}; console.log(i(3)); // undefined
var j=x=>({a:1}); console.log(j(3)); // {a:1}

Categories

Resources