JavaScript has 5 main methods for declaring functions:
Key differences:
this
Classic way of declaring a function.
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alexander")); // "Hello, Alexander!"
Features:
this
context// Works thanks to hoisting!
console.log(sayHello("World")); // "Hello, World!"
function sayHello(name) {
return `Hello, ${name}!`;
}
Function as a variable value.
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Alexander")); // "Hello, Alexander!"
Features:
this
context// Error! sayHello is not defined
// console.log(sayHello("World")); // ReferenceError
const sayHello = function(name) {
return `Hello, ${name}!`;
};
// Named function expression
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
Short syntax for functions, introduced in ES6.
// Regular arrow function
const greet = (name) => {
return `Hello, ${name}!`;
};
// Short notation (implicit return)
const greetShort = name => `Hello, ${name}!`;
// No parameters
const sayHello = () => "Hello!";
// Multiple parameters
const add = (a, b) => a + b;
console.log(greet("Alexander")); // "Hello, Alexander!"
console.log(add(5, 3)); // 8
Features:
this
— inherits contextarguments
const obj = {
name: "Alexander",
// Regular function
regularMethod: function() {
console.log(this.name); // "Alexander"
const inner = function() {
console.log(this.name); // undefined (this = window/global)
};
inner();
},
// Arrow function
arrowMethod: function() {
console.log(this.name); // "Alexander"
const inner = () => {
console.log(this.name); // "Alexander" (inherits this)
};
inner();
}
};
Short syntax for object methods (ES6).
const person = {
name: "Alexander",
age: 30,
// Short method syntax
greet() {
return `Hello, I'm ${this.name}!`;
},
// Equivalent to:
greetOld: function() {
return `Hello, I'm ${this.name}!`;
}
};
console.log(person.greet()); // "Hello, I'm Alexander!"
Features:
this
contextsuper
in classesFunction for creating objects.
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
return `Hello, I'm ${this.name}!`;
};
}
// Creating object
const person = new Person("Alexander", 30);
console.log(person.greet()); // "Hello, I'm Alexander!"
// Type checking
console.log(person instanceof Person); // true
Features:
new
operatorthis
points to the created objectMethod | Hoisting | this | Constructor | Syntax |
---|---|---|---|---|
Function Declaration | ✅ Yes | Dynamic | ✅ Yes | function name() {} |
Function Expression | ❌ No | Dynamic | ✅ Yes | const name = function() {} |
Arrow Function | ❌ No | Lexical | ❌ No | const name = () => {} |
Method Definition | ❌ No | Dynamic | ✅ Yes | methodName() {} |
Constructor Function | ✅ Yes | New object | ✅ Yes | function Name() {} |
this
in Functionsfunction regularFunction() {
console.log(this); // depends on call method
}
// Global call
regularFunction(); // window (browser) or global (Node.js)
// Call as method
const obj = { method: regularFunction };
obj.method(); // obj
// Explicit binding
regularFunction.call({ name: "test" }); // { name: "test" }
const arrowFunction = () => {
console.log(this); // always lexical context
};
const obj = {
name: "Alexander",
regularMethod() {
console.log(this.name); // "Alexander"
const arrow = () => {
console.log(this.name); // "Alexander" (inherits from regularMethod)
};
arrow();
}
};
console.log(typeof foo);
console.log(typeof bar);
function foo() {}
var bar = function() {};
const obj = {
name: "Alexander",
regular: function() {
return this.name;
},
arrow: () => {
return this.name;
}
};
console.log(obj.regular());
console.log(obj.arrow());
function Person(name) {
this.name = name;
}
const person1 = new Person("Alexander");
const person2 = Person("Maria");
console.log(person1);
console.log(person2);
console.log(window.name); // in browser
const calculator = {
value: 0,
add: function(num) {
this.value += num;
return this;
},
multiply: (num) => {
this.value *= num;
return this;
}
};
calculator.add(5).multiply(2);
console.log(calculator.value);
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: () => {
count--;
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // ?
console.log(counter.decrement()); // ?
// For regular functions
function calculateSum(a, b) {
return a + b;
}
// For short functions and callbacks
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
// For object methods
const user = {
name: "Alexander",
greet() {
return `Hello, ${this.name}!`;
}
};
// For constructors (better to use classes)
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, I'm ${this.name}!`;
}
}
// ✅ Good: callbacks and short functions
const users = ['Alexander', 'Maria', 'Ivan'];
const greetings = users.map(name => `Hello, ${name}!`);
// ✅ Good: preserving this context
class Timer {
constructor() {
this.seconds = 0;
}
start() {
setInterval(() => {
this.seconds++; // this points to Timer instance
}, 1000);
}
}
// ❌ Bad: object methods
const obj = {
name: "Alexander",
greet: () => {
return `Hello, ${this.name}!`; // this doesn't point to obj
}
};
// ❌ Bad: losing context
const person = {
name: "Alexander",
greet() {
return `Hello, ${this.name}!`;
}
};
const greetFunc = person.greet;
console.log(greetFunc()); // "Hello, undefined!"
// ✅ Good: binding context
const boundGreet = person.greet.bind(person);
console.log(boundGreet()); // "Hello, Alexander!"
// ✅ Or using arrow function
const arrowGreet = () => person.greet();
console.log(arrowGreet()); // "Hello, Alexander!"
this
this
, cannot be used as constructorKey differences:
this
determines function behaviorUnderstanding differences between function declaration methods is the foundation for writing quality JavaScript code!
Want more articles on interview preparation? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪