JavaScript is weird. If you’ve spent more than 5 minutes with the language, you already know that. One of the hardest topics to explain to a non front-end engineer is a function expression and the whole idea of functions being first-class citizens.
In Vue, until recently, all the functions were declared inside the methods option, which made this debate irrelevant. But with the introduction of the Composition API in Vue 3, declaring a function is now possible in both ways.
Let’s explore the differences and which one should be used.
tl;dr Same thing, Use Function Declarations for Readability
Function Declarations
A Function Declaration is the traditional or “normal” way of declaring a function in non-functional programming languages. It begins with the function keyword, followed by the name of the function, a pair of parentheses, and finally, a pair of curly braces that enclose the function body.
Here's an example:
function greet(name) {
console.log(`Hello, ${name}!`);
}
One important characteristic of Function Declarations is that they are hoisted to the top of their scope. This means that regardless of where the Function Declaration is written in the code, it will be available to be called anywhere within the same scope.
For example:
greet("stranger"); // prints "Hello, stranger!"
function greet(name) {
console.log(`Hello, ${name}!`);
}
Even though the greet() function is called before it is defined in the code, the Function Declaration is hoisted to the top of the scope, so the call is valid, and the greeting is logged to the console.
Function Expressions
On the other hand, a Function Expression is an expression that results in a function. It also uses the function keyword, which is assigned to a variable or a property of an object.
Here's an example:
const greet = function() {
console.log("Hello!");
};
or the same example using an arrow function:
const greet = () => {
console.log("Hello!");
};
One key difference between the two is that Function Expressions are not hoisted. They can only be called after they have been defined.
greet("stranger"); /* "ReferenceError:
Cannot access 'greet' before
initialization */
const greet = function(name) {
console.log(`Hello, ${name}!`);
}
Function Expressions are also more flexible than Function Declarations, as they can be used in more contexts, such as being passed as arguments to other functions or used as a method of an object.
<button id="myButton">Click me!</button>
<script>
const myButton = document.querySelector('#myButton');
myButton.addEventListener('click', function() {
console.log('Button clicked!');
});
</script>
There was a time not long ago that Function Expressions were considered superior because they were not polluting the global scope. But thankfully, ES modules are nowadays widely adopted, and this problem is no longer valid.
So are they the same thing? Actually,** YES**! Nowadays, it is actually down to personal preference on which one to choose. And my strong preference is using Function Declarations. To demonstrate why let's review a Vue component using both approaches.
Function declaration:
<script setup>
import { debounce } from "debounce";
import { ref, watch } from "vue";
const searchTerm = ref("");
const products = ref([]);
watch(searchTerm, debounce(performSearch, 600));
async function performSearch() {
if (searchTerm.value === "") {
products.value = [];
return;
}
if (searchTerm.value.length < 2) {
return;
}
const searchUrl = getSearchUrl();
const response = await (await fetch(searchUrl)).json();
products.value = response.products;
}
function getSearchUrl() {
const url = "https://dummyjson.com/products/search";
const params = {
q: searchTerm.value,
limit: "5"
};
const searchParams = new URLSearchParams(params);
return `${url}?${searchParams}`;
};
function clearSearch() {
searchTerm.value = "";
products.value = [];
}
</script>
Function expression:
<script setup>
import { debounce } from "debounce";
import { ref, watch } from "vue";
const searchTerm = ref("");
const products = ref([]);
const performSearch = async () => {
if (searchTerm.value === "") {
products.value = [];
return;
}
if (searchTerm.value.length < 2) {
return;
}
const searchUrl = getSearchUrl();
const response = await (await fetch(searchUrl)).json();
products.value = response.products;
}
watch(searchTerm, debounce(performSearch, 600));
const getSearchUrl = () => {
const url = "https://dummyjson.com/products/search";
const params = {
q: searchTerm.value,
limit: "5"
};
const searchParams = new URLSearchParams(params);
return `${url}?${searchParams}`;
};
const clearSearch = () => {
searchTerm.value = "";
products.value = [];
}
</script>
As you already know, everything declared inside the script setup is automatically available in the template. And being able to identify methods from the data or computed variables is much easier when using Function Declarations since they all start with the function keyword. The difference is subtle, but after all, the best code is what other people can easily understand.
What is your preference? Leave a comment below.
Top comments (2)
Declaration all the way 🚀
I share the same thoughts as you.
However, if we are to use FP currying to define functions, using arrow functions would be even better.
As for addressing readability issues, I later thought that by using the editor's folding feature, we can fold the functions to enhance readability. This way, the visual load difference between using function declarations and arrow function definitions would be much smaller.
But it's undeniable that starting with function is indeed quite recognizable.