DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for JavaScript Regular/Normal vs Arrow Function: My Beef with Arrow Functions.
Ebenezer Enietan (Niza)
Ebenezer Enietan (Niza)

Posted on

JavaScript Regular/Normal vs Arrow Function: My Beef with Arrow Functions.

When I First encountered arrow functions, I was exited about the syntax difference. So I started putting them in everything; that was a huge mistake. I soon noticed abnormal unwanted behavior in the "this" keyword, so for a while I hated arrow functions but we have reconciled.

Both function declaration and expression can be written as Normal/Regular Function

// function declaration
function test(mama) {
   return ` ${mama} is the best`
}

// function expression
const test = function(app) {
   return `Hey ${app} is not working`
}
Enter fullscreen mode Exit fullscreen mode

Arrow function or fat arrow function was introduced in ES6

const callNurse= (nurse) => {
   return  `${nurse} please come! I feel a piercing arrow`
}
Enter fullscreen mode Exit fullscreen mode

The Differences

1. Implicit Return

In normal regular functions, you must use the return keyword to return any value. If you don’t return anything then the function will return undefined. Arrow functions can return a value without the return keyword; If the arrow function contains one expression, you can omit the curly braces, and then the expression will be implicitly returned.

2. Curly braces {}

Curly braces {} are not required if its only one line of statement

const uno = (name) => name + β€œ ye”;
uno(β€œniza”);
// niza ye
Enter fullscreen mode Exit fullscreen mode

3. Parenthesis()

Parenthesis () not required if you pass only one argument and you can use only underscore _ if there is no other argument as it is one character shorter (_ and $ are valid identifiers in js )

let add = x => x + x;
let shootArrow = _ => console.log("shooting arrow");
Enter fullscreen mode Exit fullscreen mode

4. Arguments binding

In regular functions, the arguments keywords can be used to list the arguments of which passed to the function. Arrow functions on the other hand do not have an argument binding.However, if you want to access arguments in an arrow function, you can use the rest parameter:The rest parameter syntax allows a function to accept an indefinite number of arguments as an array.

function normalFunc(x,y) {
   console.log(arguments)
}
normalFunc(1,2) // Arguments[1,2]

const arrowFunc = (x,y) => console.log(arguments)
arrowFunc(1,2) //ReferenceError: arguments is not defined
Enter fullscreen mode Exit fullscreen mode
var arrowFunction = (...args) => {
   console.log(...args)
}arrowFunction(1,2)
// 1 2

Enter fullscreen mode Exit fullscreen mode

5. this

In normal regular functions, "this" changes according to how the function is invoked. "this" may refer to the global object or undefined if you are using strict mode. It could be the object that owns the method the if the function is a method of an object. Other times it refers to the newly created instance when it is used as a constructor.

Arrow functions don’t have their own β€œthis”, and they don’t redefine the value of this within the function. β€œthis” inside an arrow function always refer to "this" from the outer context. This is my biggest beef with arrow functions

//normal function
const person = {
 run() {
     console.log(this);
 }
};
person.run();
// logs person object


//arrow function
const person = {
        run: () => {
              console.log(this);
        }
    };
person.run();

// logs window object
Enter fullscreen mode Exit fullscreen mode

6. new

Regular functions are constructible, they can be called using the new keyword. However, arrow functions can never be used as constructor functions. Hence, they can never be invoked with the new keyword

function add (a, b) {
   console.log(a + b)
}
let sum = new add(2,3); // 5

let add = (a, b) => console.log(a + b);
const sum = new add(2,4);

// TypeError: add is not a constructor
Enter fullscreen mode Exit fullscreen mode

7. No duplicate named parameters

In normal function, we can do this: row functions can never have duplicate named parameters, whether in strict or non-strict mode.

// allowed
function add(x, x) {}

// not allowed
'use strict';
function add(y, y) {}
// Uncaught SyntaxError: Duplicate parameter name not allowed in this context
const arrowFunc = (a,a) => {}
// Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Enter fullscreen mode Exit fullscreen mode

8. Function Hoisting

In regular function, the function gets hoisting at the top. But in arrow function, function get hoisted where you define. So, if you call the function before initialization you will get a ReferenceError

get referenceError.
normalFunc()
function normalFunc() {
   return "Shooting bullets"
}
// "Normal Function"

arrowFunc()
const arrowFunc = () => {
   return "Shooting Arrows"
}
// ReferenceError: Cannot access 'arrowFunc' before initialization
Enter fullscreen mode Exit fullscreen mode

Top comments (12)

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ • Edited on

...and only underscore _ is required when there is no argument

This is not correct. If you put an underscore there, your function is accepting an argument called _ - and you are just choosing to ignore it. The correct way to do it is:

const shootArrow = () => console.log("shooting arrow")
Enter fullscreen mode Exit fullscreen mode

This can also be verified by checking the length property of the function that returns the number of non-optional parameters it takes:

( _ => console.log("shooting arrow") ).length  // 1
( () => console.log("shooting arrow") ).length  // 0
Enter fullscreen mode Exit fullscreen mode

If you write functions that take no arguments in the way you describe - you could end up with issues when working with libraries that make use of .length. It is also not good for readability.

Collapse
 
niza profile image
Ebenezer Enietan (Niza) • Edited on

Thanks for the feedback i have adjusted that portion to better convey my thoughts

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

I also adjusted my comment to better convey my thoughts πŸ˜€

Thread Thread
 
niza profile image
Ebenezer Enietan (Niza)

lol .. just followed you

Collapse
 
seanmay profile image
Sean May • Edited on

Problem #5/#6 is a good thing to me. People mess up this bindings way too often. I stopped using this ~2012, and stopped writing mutative code around the same time, and life has only gotten better, as whole classes of bugs disappeared. But if it helps:

class Example {
  data = Math random();
  doX = () => console log(this.data);
}
Enter fullscreen mode Exit fullscreen mode

using the arrow like this will always get the binding right. Even if you pass the function in as a callback, or save it as a variable, or reference it on a different object, et cetera. Something that the other functions do not get right.

class GoodPerson {
  name;
  constructor (name) {
    this.name = name;
  }
  sayName = () => console.log(this.name);
}

class BadPerson {
  name;
  constructor (name) {
    this.name = name;
  }
  sayName() { console.log(this.name); }
}

const bob = new GoodPerson("Bob");
const doug = new BadPerson("Doug");

bob.sayName(); // "Bob"
doug.sayName(); // "Doug"

const evilDoug = {
  name: "Broken!",
  sayName: doug.sayName,
};
const evilBob = {
  name: "Broken!",
  sayName: bob.sayName,
};

evilDoug.sayName(); // "Broken!"
evilBob.sayName(); // "Bob"
Enter fullscreen mode Exit fullscreen mode

Issue #7 isn't because of the arrow function, it's because of the const keyword. A const can't have a competing name in the same context.

function test() {}
const test = 5; // <-- this explodes because "test" is already declared
Enter fullscreen mode Exit fullscreen mode

The same thing happens when you use const to shadow the name of an argument.

const f = (x) => {
  const x = 5; // <-- explodes for the same reason as above
};
Enter fullscreen mode Exit fullscreen mode

Issue #8 actually has 2 bugs. The first is to do with const, not the function.

console.log(x); // <-- access before initialization
const x = 5;
Enter fullscreen mode Exit fullscreen mode

This is the deadzone. You can't use a let or a const above where it is declared. Even if there is something with the same name in an outer scope.

const x = 5;

const f = () => {
  console.log("outer x");
  console.log(x); // <-- this errors; can't use both xs in the same scope with let/const. It's treated as inner-x and is used in the deadzone
  // inner x
  const x = 6;
};
Enter fullscreen mode Exit fullscreen mode

The second problem with #8 is specific to functions, but not unique to arrows.

Function hoisting does not work on any function expression of any kind, including assignment statements. Even if you are using var.

function hoisted () {}

var f1 = function nope () {};
nope(function not_me_either () {});
window.addEventListener("load", function not_a_chance() {});
Enter fullscreen mode Exit fullscreen mode

The only functions that are hoisted are function declarations.

For that reason and others, it's better to not run the code in the same file that you define the code.

Collapse
 
niza profile image
Ebenezer Enietan (Niza)

but arrow function are always written as expression otherwise they are anonymous so...

Collapse
 
seanmay profile image
Sean May • Edited on

So are function expressions. Even if you name a function expression it is still not hoisted.

There are many ways of defining functions and there is exactly 1 of those ways that is hoisted.

// function declaration
// hoisted
function a () {}

// function expression: named
// not-hoisted
const b = function B () {}; // name only exists inside B (ie: recursion)

// function expression: anonymous
// not-hoisted
const c = function () {};

// lambda expression
// not-hoisted
const d = () => {};
Enter fullscreen mode Exit fullscreen mode

So your problem is with literally all function expressions, which... fine... prefer declarations if you want to. But for people who prefer expressions, there is no difference between using the arrow and not, for #8, so using arrows is just more concise.

Thread Thread
 
niza profile image
Ebenezer Enietan (Niza)

Yes i gave an example of that in the beginning; you can write normal function as an expression but its optional.

also no # 7 is not because of the const keyword (try let keyword with my example to confirm)

I Appreciate #6 comment

Thread Thread
 
seanmay profile image
Sean May

Yes, function expressions are optional. Function declarations are optional, too. Only function declarations can be hoisted, so if you choose to use any form of expression, your functions aren't hoisted, regardless of whether they are named, unnamed, arrow, or otherwise.

Re: #7
let and const have the same rules regarding deadzones and variable shadowing. Try it again with var.
Or just try

function x () {}
let x = 5;
Enter fullscreen mode Exit fullscreen mode

There's no arrow function there, so why is the error the same?

Thread Thread
 
niza profile image
Ebenezer Enietan (Niza) • Edited on

#7 was about parameters not declaration or identifiers

Thread Thread
 
seanmay profile image
Sean May • Edited on

Perhaps I remembered incorrectly.

But if your problem with arrow functions is that you can't have (x, x, x, x) => {} then it's a problem with the ECMAScript 2015 standard, not with arrows, as anything following the ES5 (ECMAScript 2015) standard (ie: "strict mode") is going to give you the same problems. Everything these days is based on ES5-strict. ES Modules are automatically strict just by using import/export, TS might let you export ES3, but it will make you write ES5-strict source code.

Arrow functions also don't provide caller information, which ES3 functions did, and was a large security concern. But no ES5+ functions provide caller information, it's just that arrows didn't exist before ES6, so no functions that looked like them ever had caller information or shadowed parameter names.

Thread Thread
 
niza profile image
Ebenezer Enietan (Niza) • Edited on

I'm not against anything in JS just that I ran back as quickly as went into arrows function at first because of the strange behavior but its history now and I honestly appreciate your engagement

Let's Get Hacking

Join the DEV x Linode Hackathon 2022 and use your ingenuity and creativity to build using Linode.

β†’ Join the Hackathon <-