1. 함수 선언 방식
자바스크립트에서는 여러 가지 방식으로 함수를 정의할 수 있습니다. 각 방식은 함수의 스코프, 호이스팅, this와의 관계 등에 영향을 미칩니다.
1.1 함수 선언문 (Function Declaration)
• 정의: 가장 일반적인 함수 선언 방식입니다. 함수 선언문은 코드에서 독립적인 함수로 정의되며, 해당 함수는 스코프 내에서 어디서든 호출할 수 있습니다.
• 호이스팅: 함수 선언문은 자바스크립트 엔진에 의해 호이스팅됩니다. 즉, 함수가 선언된 위치와 상관없이 스코프의 맨 위로 끌어올려지기 때문에 함수 선언 전에 함수 호출이 가능합니다.
• 예시:
function greet() {
console.log("Hello!");
}
greet(); // Hello!
• 함수 선언 전 호출:
greet(); // Hello!
function greet() {
console.log("Hello!");
}
• this: 함수 선언문 내부에서의 **this**는 호출되는 방식에 따라 결정됩니다.
• 전역에서 호출하면 전역 객체를 가리키며, 객체의 메서드로 호출하면 그 객체를 가리킵니다.
1.2 함수 표현식 (Function Expression)
• 정의: 함수 표현식은 함수 선언과 달리, 함수가 변수에 할당됩니다. 함수가 할당된 변수는 함수로서 사용됩니다.
• 호이스팅: 함수 표현식은 변수와 마찬가지로 호이스팅되지 않으므로, 함수 선언 후에만 호출할 수 있습니다.
• 예시:
const greet = function() {
console.log("Hello!");
};
greet(); // Hello!
• this: 함수 표현식 내부에서의 this 역시 호출 방식에 따라 다릅니다. 일반적으로, 전역에서 호출하면 전역 객체, 메서드로 호출하면 그 객체를 가리킵니다.
1.3 화살표 함수 (Arrow Function)
• 정의: ES6에서 도입된 새로운 함수 표현 방식으로, 간결하게 함수를 선언할 수 있습니다.
• 호이스팅: 화살표 함수는 함수 표현식처럼 변수에 할당되기 때문에 호이스팅되지 않습니다.
• this: 화살표 함수의 가장 큰 특징은 렉시컬 스코프를 따른다는 점입니다. 즉, 화살표 함수는 자신이 정의된 위치의 스코프에서 **this**를 상속받습니다. 따라서 전역 객체나 호출 객체에 의존하지 않고, 상위 스코프의 **this**를 사용합니다.
• 예시:
const obj = {
name: "Alice",
regularFunction: function() {
console.log(this.name); // "Alice"
},
arrowFunction: () => {
console.log(this.name); // undefined (전역 스코프의 this)
}
};
obj.regularFunction(); // "Alice"
obj.arrowFunction(); // undefined
• 화살표 함수는 this를 바인딩하지 않고, 외부 스코프의 this를 참조하는 특징 때문에 클래스 메서드나 콜백 함수로 유용하게 사용됩니다.
1.4 즉시 실행 함수 (Immediately Invoked Function Expression, IIFE)
• 정의: 함수를 선언하자마자 즉시 실행되는 함수입니다. 주로 독립된 스코프를 만들고 싶을 때 사용됩니다.
• 호이스팅: 일반 함수처럼 선언 후 실행되지 않으며, 정의된 즉시 실행되므로 별도의 호출이 필요 없습니다.
• 예시:
(function() {
console.log("IIFE executed!");
})();
• this: IIFE 내부의 **this**는 호출 맥락에 따라 다릅니다.
2. 클래스와 함수
자바스크립트에서는 ES6 이후로 클래스 문법이 도입되었으며, 함수와 클래스는 긴밀하게 연관됩니다. 클래스의 메서드는 사실상 함수로 정의됩니다.
2.1 클래스 선언
• 클래스는 class 키워드를 사용해 선언됩니다. 클래스 내부에는 메서드가 정의되며, 이 메서드들은 모두 함수입니다.
• 예시:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}`);
}
}
const alice = new Person("Alice");
alice.greet(); // Hello, Alice
2.2 클래스에서의 this
• 클래스 내부에서 **this**는 해당 인스턴스 객체를 가리킵니다. 이는 클래스 내의 메서드가 호출될 때, 그 메서드가 속한 객체를 참조하는 방식입니다.
• **this**는 생성된 객체의 속성에 접근할 때 자주 사용됩니다.
2.3 메서드와 this
• 클래스 메서드 내부의 **this**는 해당 메서드를 호출한 인스턴스를 가리킵니다.
• 예시:
class Animal {
constructor(type) {
this.type = type;
}
describe() {
console.log(`This is a ${this.type}`);
}
}
const dog = new Animal("dog");
dog.describe(); // This is a dog
• 만약 메서드를 외부에서 별도로 호출하거나 메서드 내부에서 콜백을 사용한다면 **this**가 달라질 수 있습니다. 이를 해결하기 위해 화살표 함수나 bind 메서드를 사용해 this를 고정할 수 있습니다.
3. this의 다양한 동작 방식
자바스크립트에서 **this**는 함수가 호출되는 방식에 따라 다르게 결정됩니다. 이 동작 방식을 정리하면 다음과 같습니다.
3.1 일반 함수에서의 this
• 전역 스코프에서 호출된 일반 함수의 **this**는 기본적으로 전역 객체를 가리킵니다.
• 브라우저 환경에서는 window, Node.js에서는 global 객체를 참조합니다.
• 예시:
function showThis() {
console.log(this);
}
showThis(); // window (브라우저 환경)
3.2 객체의 메서드에서의 this
• 객체의 메서드에서 호출된 함수의 **this**는 그 객체 자신을 가리킵니다.
• 예시:
const person = {
name: "Alice",
greet: function() {
console.log(this.name); // "Alice"
}
};
person.greet(); // Alice
3.3 생성자 함수에서의 this
• 생성자 함수에서 호출된 **this**는 새롭게 생성된 인스턴스 객체를 가리킵니다.
• 예시:
function Person(name) {
this.name = name;
}
const john = new Person("John");
console.log(john.name); // John
3.4 화살표 함수에서의 this
• 화살표 함수는 렉시컬 스코프를 따르기 때문에, 화살표 함수 내부의 **this**는 상위 스코프의 **this**를 그대로 상속받습니다.
• 예시:
const obj = {
name: "Alice",
regularFunction: function() {
console.log(this.name); // "Alice"
},
arrowFunction: () => {
console.log(this.name); // undefined (상위 스코프의 this)
}
};
obj.regularFunction(); // "Alice"
obj.arrowFunction(); // undefined
4. 클래스에서 화살표 함수와 this의 활용
클래스 내부에서 메서드를 정의할 때, 화살표 함수를 사용하면 렉시컬 스코프를 따르기 때문에 this의 값이 고정되어 유지됩니다. 이를 활용하면 클래스 메서드에서 this가 콜백 함수 내부에서 변경되지 않도록 할 수 있습니다.
4.1 예시: 콜백 함수에서의 this 문제 해결
일반 함수의 경우, 메서드 내부에서 사용된 콜백 함수는 this의 값이 바뀔 수 있습니다. 이 문제를 해결하기 위해서는 화살표 함수를 사용할 수 있습니다.
• 문제점:
class Counter {
constructor() {
this.count = 0;
}
increment() {
setTimeout(function() {
this.count++; // `this`는 전역 객체를 가리킴
console.log(this.count); // NaN 또는 오류 발생
}, 1000);
}
}
const myCounter = new Counter();
myCounter.increment(); // 예상과 달리 `this`가 전역 객체를 참조
위 코드에서 setTimeout 내의 함수는 콜백 함수로서 호출되기 때문에, this는 Counter 클래스 인스턴스를 가리키지 않고 전역 객체(브라우저에서는 window 객체)를 가리킵니다. 따라서 this.count는 정의되지 않았거나 undefined가 되어 오류가 발생합니다.
• 해결 방법 1: 화살표 함수 사용:
화살표 함수는 상위 스코프의 this를 상속받기 때문에, this가 바뀌지 않고 클래스 인스턴스를 가리킵니다.
class Counter {
constructor() {
this.count = 0;
}
increment() {
setTimeout(() => {
this.count++; // `this`는 `Counter` 인스턴스를 가리킴
console.log(this.count); // 정상적으로 1 출력
}, 1000);
}
}
const myCounter = new Counter();
myCounter.increment(); // 1 출력
• 해결 방법 2: bind를 사용해 this 고정:
화살표 함수 대신 일반 함수를 사용하고 싶다면, bind 메서드를 통해 this를 고정할 수 있습니다.
class Counter {
constructor() {
this.count = 0;
}
increment() {
setTimeout(function() {
this.count++; // `this`는 `Counter` 인스턴스를 가리킴
console.log(this.count); // 정상적으로 1 출력
}.bind(this), 1000);
}
}
const myCounter = new Counter();
myCounter.increment(); // 1 출력
자바스크립트에서 함수는 함수 선언문, 함수 표현식, 화살표 함수, 즉시 실행 함수(IIFE) 등 다양한 방식으로 정의되며, 각 방식에 따라 호이스팅과 **this**의 동작 방식이 다르게 작동합니다.
• 함수 선언문은 호이스팅되며, this는 호출 방식에 따라 결정됩니다.
• 함수 표현식은 호이스팅되지 않고, this 역시 호출 방식에 따라 다릅니다.
• 화살표 함수는 상위 스코프의 this를 상속받아 콜백 함수나 클래스 메서드에서 유용하게 사용됩니다.
• IIFE는 즉시 실행되는 함수로, 독립적인 스코프를 만들 때 사용됩니다.
또한, 클래스와 함수는 긴밀하게 연관되며, 클래스 내부의 this는 해당 인스턴스를 가리킵니다. 메서드 내부에서 this의 변경을 막기 위해 화살표 함수나 **bind**를 사용할 수 있으며, 이를 통해 보다 안전하게 this를 관리할 수 있습니다.
'JavaScript' 카테고리의 다른 글
[JS] 자바스크립트의 동작방식(비동기,동기) (0) | 2024.09.08 |
---|---|
[JS] Callback (0) | 2024.09.08 |
[JS] Scope (0) | 2024.09.08 |
[JS] Execution context (0) | 2024.09.08 |
[JS] 자바스크립트 변수 선언과 호이스팅 (0) | 2024.09.08 |