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());
Related
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
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();
We have two different way for doing function expression in JavaScript:
Named function expression (NFE):
var boo = function boo () {
alert(1);
};
Anonymous function expression:
var boo = function () {
alert(1);
};
And both of them can be called with boo();. I really can't see why/when I should use anonymous functions and when I should use Named Function Expressions. What difference is there between them?
In the case of the anonymous function expression, the function is anonymous — literally, it has no name. The variable you're assigning it to has a name, but the function does not. (Update: That was true through ES5. As of ES2015 [aka ES6], often a function created with an anonymous expression gets a true name [but not an automatic identifier], read on...)
Names are useful. Names can be seen in stack traces, call stacks, lists of breakpoints, etc. Names are a Good Thing™.
(You used to have to beware of named function expressions in older versions of IE [IE8 and below], because they mistakenly created two completely separate function objects at two completely different times [more in my blog article Double take]. If you need to support IE8 [!!], it's probably best to stick with anonymous function expressions or function declarations, but avoid named function expressions.)
One key thing about a named function expression is that it creates an in-scope identifier with that name for the function within the functon body:
var x = function example() {
console.log(typeof example); // "function"
};
x();
console.log(typeof example); // "undefined"
As of ES2015, though, a lot of "anonymous" function expressions create functions with names, and this was predated by various modern JavaScript engines being quite smart about inferring names from context. In ES2015, your anonymous function expression results in a function with the name boo. However, even with ES2015+ semantics, the automatic identifier is not created:
var obj = {
x: function() {
console.log(typeof x); // "undefined"
console.log(obj.x.name); // "x"
},
y: function y() {
console.log(typeof y); // "function"
console.log(obj.y.name); // "y"
}
};
obj.x();
obj.y();
The assignment fo the function's name is done with the SetFunctionName abstract operation used in various operations in the spec.
The short version is basically any time an anonymous function expression appears on the right-hand side of something like an assignment or initialization, like:
var boo = function() { /*...*/ };
(or it could be let or const rather than var), or
var obj = {
boo: function() { /*...*/ }
};
or
doSomething({
boo: function() { /*...*/ }
});
(those last two are really the same thing), the resulting function will have a name (boo, in the examples).
There's an important, and intentional, exception: Assigning to a property on an existing object:
obj.boo = function() { /*...*/ }; // <== Does not get a name
This was because of information leak concerns raised when the new feature was going through the process of being added; details in my answer to another question here.
Naming functions is useful if they need to reference themselves (e.g. for recursive calls). Indeed, if you are passing a literal function expression as an argument directly to another function, that function expression cannot directly reference itself in ES5 strict mode unless it is named.
For example, consider this code:
setTimeout(function sayMoo() {
alert('MOO');
setTimeout(sayMoo, 1000);
}, 1000);
It would be impossible to write this code quite this cleanly if the function expression passed to setTimeout were anonymous; we would need to assign it to a variable instead prior to the setTimeout call. This way, with a named function expression, is slightly shorter and neater.
It was historically possible to write code like this even using an anonymous function expression, by exploiting arguments.callee...
setTimeout(function () {
alert('MOO');
setTimeout(arguments.callee, 1000);
}, 1000);
... but arguments.callee is deprecated, and is outright forbidden in ES5 strict mode. Hence MDN advises:
Avoid using arguments.callee() by either giving function expressions a name or use a function declaration where a function must call itself.
(emphasis mine)
You should always use named function expressions, that's why:
You can use the name of that function when you need recursion.
Anonymous functions doesn't help when debugging as you can't see the name of the function that causes problems.
When you do not name a function, later on its harder to understand what it's doing. Giving it a name makes it easier to understand.
var foo = function bar() {
//some code...
};
foo();
bar(); // Error!
Here, for example, because the name bar is used within a function expression, it doesn't get declared in the outer scope. With named function expressions, the name of the function expression is enclosed within its own scope.
If a function is specified as a Function Expression, it can be given a name.
It will only be available inside the function (except IE8-).
var f = function sayHi(name) {
alert( sayHi ); // Inside the function you can see the function code
};
alert( sayHi ); // (Error: undefined variable 'sayHi')
This name is intended for a reliable recursive function call, even if it is written to another variable.
In addition, the NFE (Named Function Expression) name CAN be overwritten with the Object.defineProperty(...) method as follows:
var test = function sayHi(name) {
Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
alert( test.name ); // foo
};
test();
Note: that with the Function Declaration this can not be done. This "special" internal function name is specified only in the Function Expression syntax.
Using named function expressions is better, when you want to be able to reference the function in question without having to rely on deprecated features such as arguments.callee.
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/
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