함수 : 함수는 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것.
[함수를 사용하는 이유]
필요할 때마다 한번 정의해 둔 것을 호출만 하면서 재사용할 수 있는 장점이 있다. 이러한 장점으로 우리는 함수를 사용한다. 그것도 아주 많이. + 코드의 가독성 향상 측면도 아주 크다.
[함수 리터럴]
함수 리터럴의 구성요소
var f = function add(x, y) {
return x + y;
}
- function 키워드
- 함수 이름
- 매개 변수 목록
- 함수 몸체
[함수정의]
-함수 선언문 : function add(x, y) { return x+y; } 방식
- 함수 표현식 : var add = function add(x, y) { return x+y; }; 이렇게 변수에 할당하는 방식
- Function 생성자 함수 : var add = new Function( 'x', 'y', 'return x + y ' );
- 화살표 함수(중요) : var add = (x, y) => x + y;
- 함수 생성 시점과 호이스팅
너무 중요한 내용이라 이전에 따로 빼서 정리한 내용 + 내용 추가 해두었으니 봐주세요~
화살표 함수 (feat : 함수표현식 vs 함수선언식)
[화살표 함수 (feat : 함수표현식 vs 함수선언식)
프로젝트를 하며 js를 다루다가, api 연동 혹은 리액트 코드 작성을 위해 사용한 함수식은 대부분 화살표 함수로 작성되어있다는 것을 깨달았다. 과연 내가 제대로 알고 쓰는것일까? 문득 궁금하
sjindev.tistory.com](https://sjindev.tistory.com/1)
- Function 생성자 함수
Function 생성자는 문자열로 받은 코드로 새로운 함수를 생성하는 방식이다. 런타임에 동적으로 함수를 만들 수 있지만, 일반적으로 권장되지는 않는다.
const sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 3)); // 5
- 매개변수와 인수 : 매개변수의 수보다 인수를 적게 넘기면 undefined, 많으면 초과된 인수는 무시된다.
function greet(name) {
console.log(`Hello, ${name}`);
}
greet('Sjin'); // 'Sjin'은 인수, 'name'은 매개변수
- 인수확인 : 자바스크립트는 매개변수의 개수에 제한이 없기 때문에,
arguments 객체나 기본값 설정으로 인수 유무를 확인할 수 있다.
function show(name) {
if (name === undefined) {
console.log('이름이 없습니다');
} else {
console.log(`이름: ${name}`);
}
}
ES6 이후에는 name = '익명' 같은 기본값 매개변수로 처리하는 방식이 일반적이다.
- 매개변수의 최대 개수
명확한 최대 개수 제한은 없지만, 일반적으로 3개 이하로 하는 것이 가장 일반적이라고 한다. 너무 많아지면 오히려 관리하기 힘들 뿐더러 코드 가독성을 해치고, 유지보수를 어렵게 만든다. 여러 값을 전달해야 할 경우 매개변수로 일일히 나열하기 보다는, 객체나 배열을 매개변수로 묶어서 전달하는 것이 좋다.
구조 분해 할당과 함께 쓰면 매개변수가 많아도 깔끔하게 표현할 수 있다.
아래는 그 예시다.
문제 상황: 매개변수가 너무 많을 때
function registerUser(name, age, email, phone, address, job) {
console.log(`이름: ${name}`);
console.log(`나이: ${age}`);
console.log(`이메일: ${email}`);
console.log(`전화번호: ${phone}`);
console.log(`주소: ${address}`);
console.log(`직업: ${job}`);
}
registerUser('Kim', 25, 'kim@example.com', '010-1234-5678', 'Seoul', 'Developer');
매개변수가 많아질수록 순서를 정확히 지켜야 하고, 가독성도 떨어짐
일부 정보만 전달하고 싶을 때도 undefined가 끼게 되어 처리 번거로움 발생
================================================================
구조 분해 할당 + 객체 매개변수 이용하여 해결
function registerUser({ name, age, email, phone, address, job }) {
console.log(`이름: ${name}`);
console.log(`나이: ${age}`);
console.log(`이메일: ${email}`);
console.log(`전화번호: ${phone}`);
console.log(`주소: ${address}`);
console.log(`직업: ${job}`);
}
const userInfo = {
name: 'Kim',
age: 25,
email: 'kim@example.com',
phone: '010-1234-5678',
address: 'Seoul',
job: 'Developer'
};
registerUser(userInfo);
매개변수를 객체로 하나만 받고, 내부에서 구조 분해 할당으로 꺼내서 사용
전달 순서와 무관하며, 일부 값만 전달하거나 디폴트 값 지정도 쉬움
[참조에 의한 전달과 외부 상태의 변경]
값에 의한 호출 vs 참조에 의한 호출 이 각각 어떻게 발생하는지 그림을 보고 잘 이해하자.
아래는 참조에 의한 호출에 대한 예시이다.
객체나 배열을 함수에 전달하면 참조가 전달되어 함수 내부에서 값을 변경하면 외부 상태에도 영향을 미침.
function change(obj) {
obj.value = 100;
}
const data = { value: 1 };
change(data);
console.log(data.value); // 100
불변성을 유지하고 싶다면 복사본을 만들어서 처리하는 것이 좋다.
- 중첩함수
함수 내부에 또 다른 함수를 정의하는 방식으로, 외부 함수의 변수에 접근할 수 있다
function outer() {
let count = 0;
function inner() {
count++;
return count;
}
return inner;
}
- 순수함수와 비순수 함수
순수 함수(Pure Function) : 외부 상태를 변경하지 않고 같은 입력에 대해 항상 같은 결과를 반환하는 함수.
function add(a, b) {
return a + b;
}
순수 함수는 디버깅, 테스트, 유지보수에 유리하다.
비순수 함수 :외부 상태를 읽거나 변경하는 함수로, 예측이 어려운 결과를 초래할 수 있다.
let count = 0;
function increase() {
count++;
}
[스코프]
정의 : 모든 식별자(변수 이름, 함수이름, 클래스 이름 등)는 자신이 선언된 위치에 따라 다른 코드가 나 자신을 참조할 수 있는 유효범위가 결정되며 이를 스코프라 한다.
1. var 키워드로 선언한 변수의 중복 선언
var 키워드는 ES6 이전까지 자바스크립트에서 변수를 선언하는 유일한 방법이었다. 하지만 다음과 같은 특이점을 가지고 있다.
var x = 10;
var x = 20;
console.log(x); // 20
- 같은 스코프 안에서 var로 중복 선언 가능
- 중복 선언 시 에러 없이 덮어쓰기 됨
- 반면 let과 const는 같은 스코프 내 중복 선언 불가능
이는 코드 예측을 어렵게 만들고, 실수를 유발할 가능성이 높다.
2. 스코프의 종류
자바스크립트에는 크게 다음과 같은 스코프가 존재한다.
- 전역 스코프 (Global Scope)
- 함수 레벨 스코프 (Function Scope)
- 블록 레벨 스코프 (Block Scope) ← ES6 이후 도입 (let, const)
이 외에도 모듈 스코프, eval 스코프, 렉시컬 스코프와 같은 개념도 존재한다.
3. 전역 스코프와 지역 스코프
전역 스코프
전역에서 선언된 변수는 코드 어디서든 접근 가능하다.
var globalVar = 'I am global';
function foo() {
console.log(globalVar); // 접근 가능
}
지역 스코프
함수 또는 블록 내부에서 선언된 변수는 해당 영역 내부에서만 접근 가능하다.
function bar() {
var localVar = 'I am local';
console.log(localVar); // OK
}
console.log(localVar); // ReferenceError
4. 스코프 체인 (Scope Chain)
스코프는 중첩될 수 있으며, 내부 함수는 외부 함수의 변수에 접근 가능하다.
이처럼 스코프가 중첩되었을 때, 식별자를 찾는 구조를 스코프 체인이라고 한다.
function outer() {
var a = 1;
function inner() {
console.log(a); // 1
}
inner();
}
inner()는 자신의 스코프에서 a를 찾지 못하면, 상위 스코프인 outer()로 올라가서 찾는다.
5. 스코프 체인에 의한 변수/함수 검색
변수를 참조하거나 함수를 호출할 때, 자바스크립트 엔진은 가장 가까운 스코프부터 상위 스코프로 올라가며 탐색한다.
var x = 1;
function foo() {
var x = 10;
function bar() {
console.log(x);
}
bar();
}
foo(); // 10
- bar()는 x를 자신의 스코프에서 찾지 못하자 foo() 스코프에서 찾아 사용함
6. 함수 레벨 스코프 (Function-Level Scope)
var는 블록({})이 아닌 함수 단위로 스코프를 만든다.
즉, if문이나 for문 내부에서 선언해도 해당 함수 전체에서 접근 가능하다.
function test() {
if (true) {
var a = 10;
}
console.log(a); // 10
}
이와 같은 특징은 버그를 유발할 수 있기 때문에, ES6에서는 let, const를 통해 블록 스코프를 도입하였다.
let, const 에 대해서는 아래서 더 자세히 다룰 예정이다.
7. 렉시컬 스코프 (Lexical Scope)
자바스크립트는 렉시컬 스코프(정적 스코프)를 따른다.
즉, 함수가 어디에서 호출되었는지가 아니라, 어디에서 정의되었는지에 따라 스코프가 결정된다.
const x = 1;
function outer() {
const x = 10;
function inner() {
console.log(x);
}
return inner;
}
const func = outer();
func(); // 10
- inner()는 outer() 안에서 정의되었기 때문에, outer의 스코프를 기억하고 있음
8. 변수의 생명주기
변수는 선언된 위치에 따라 생성되고 사라지는 시점이 다르다.
- 전역 변수: 브라우저가 종료될 때까지 유지됨
- 지역 변수: 함수 실행 시 생성 → 함수 종료 시 메모리에서 사라짐
function temp() {
var x = 100; // 함수 실행 시 생성됨
}
temp(); // x는 함수 실행이 끝나면 사라짐
블록 레벨 스코프 변수(let, const)는 블록을 벗어나면 바로 제거됨
9. 전역 변수의 문제점
전역 변수는 모든 스코프에서 접근 가능하다는 점에서 편리하지만, 다음과 같은 문제를 야기할 수 있다.
- 이름 충돌의 위험: 여러 스크립트에서 같은 이름을 사용하면 충돌 가능
- 디버깅 어려움: 어디서 변경되었는지 추적이 어려움
- 메모리 누수 위험: 브라우저 종료 전까지 계속 유지되므로 과도한 사용은 위험함
var msg = 'Hello'; // 전역
function update() {
msg = 'Hi'; // 다른 곳에서도 의도치 않게 변경될 수 있음
}
10. 모듈 패턴 (Module Pattern)
전역 변수의 문제를 해결하기 위한 방식 중 하나가 모듈 패턴이다.
즉시 실행 함수(IIFE)를 사용해 스코프를 한정하고, 외부에 필요한 것만 노출하는 구조다.
const Counter = (function () {
let count = 0;
return {
increase() {
count++;
console.log(count);
},
getCount() {
return count;
}
};
})();
Counter.increase(); // 1
Counter.increase(); // 2
console.log(Counter.count); // undefined (은닉됨)
모듈 패턴은 은닉성, 충돌 방지, 캡슐화를 제공하며,
ES6 이후에는 import/export를 사용하는 모듈 시스템(ESM)이 이를 대체하고 있음
이처럼 스코프는 자바스크립트의 기본 동작 원리를 이해하는 데 핵심적인 개념이다.
변수 선언 방식(var, let, const), 함수 정의 위치, 블록의 범위 등을 고려하여 스코프를 잘 설계해야 안정적이고 예측 가능한 코드를 작성할 수 있다.
[let, const 키워드와 블록레벨 스코프]
자바스크립트에서 var는 오랫동안 변수 선언을 위한 유일한 방법이었음. 하지만 ES6 이후 let과 const 키워드가 도입되면서 변수 선언 방식에 변화가 생겼다!!!! 결론부터 말하자면, 이 키워드들이 블록 레벨 스코프를 지원하고, 변수 중복 선언, 호이스팅 문제 등을 개선함으로써 더 안전하고 예측 가능한 코드 작성을 가능하게 해주었다. 그럼 아래 더 자세히 알아보자.
1. var 키워드로 선언한 변수의 문제점
1.1 변수 중복 선언 허용
var는 같은 스코프 내에서 변수를 중복해서 선언해도 에러가 발생하지 않는다. 이는 의도치 않은 값의 덮어쓰기를 유발하고, 디버깅을 어렵게 만든다.
var x = 10;
var x = 20;
console.log(x); // 20 (덮어쓰기됨)
이러한 특성은 규모가 큰 애플리케이션에서 예기치 않은 결과와 버그를 초래할 수 있다.
1.2 함수 레벨 스코프
var로 선언한 변수는 함수 단위로 스코프를 갖는다.
function test() {
if (true) {
var a = 1;
}
console.log(a); // 1 (if 블록 외부에서도 접근 가능)
}
이는 제어문이나 반복문 내부에서 선언한 변수도 외부에서 접근 가능하다는 것을 의미한다. 이러한 특징은 실수를 유발하기 쉬운 구조다.
2. let 키워드: 변수 중복 선언 금지
let은 var와 달리, 동일한 스코프 내에서 변수 중복 선언을 허용하지 않는다.
let x = 10;
let x = 20; // SyntaxError: Identifier 'x' has already been declared
이는 변수가 의도치 않게 재선언되는 오류를 사전에 차단하여, 코드 안정성과 예측 가능성을 향상시킨다.
또한 let은 블록 레벨 스코프를 지원하므로, 다음과 같은 코드도 안전하게 작동한다.
if (true) {
let message = 'Hello';
console.log(message); // Hello
}
console.log(message); // ReferenceError: message is not defined
블록 내부에서 선언된 변수는 해당 블록을 벗어나면 참조할 수 없다.
3. 블록 레벨 스코프
블록 레벨 스코프(Block-Level Scope)란, 중괄호 {}**로 묶인 영역 내부에서만 변수의 유효 범위가 유지되는 것**을 말한다.
이는 let과 const에서만 적용되며, var는 적용되지 않는다.
for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log(i); // ReferenceError: i is not defined
위의 예제에서 let으로 선언된 i는 for문 블록 내부에서만 유효하며, 밖에서는 접근이 불가능하다.
이러한 특징은 변수의 오염(외부 접근 또는 충돌)을 방지하고, 스코프 관리를 명확하게 만든다.
4. 변수 호이스팅 (Variable Hoisting)
자바스크립트에서 변수 선언은 호이스팅(Hoisting)이라는 메커니즘에 의해
코드의 실행 이전에 변수 선언이 최상단으로 끌어올려지는 현상이 발생한다.
4.1 var의 호이스팅
var로 선언한 변수는 선언만 끌어올려지고, 초기화는 끌어올려지지 않는다.
console.log(x); // undefined
var x = 10;
이 코드는 다음과 같이 해석된다:
var x;
console.log(x); // undefined
x = 10;
초기화 전에 참조하면 undefined가 반환된다. 이 때문에 초기화 시점을 착각하기 쉬움.
4.2 let과 const의 호이스팅 (TDZ)
let과 const도 호이스팅 되지만, Temporal Dead Zone (TDZ)에 갇히므로 선언 전에 참조할 수 없다.
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;
- let, const는 선언 전에 접근 불가 → 에러 발생
- 변수는 블록이 시작되는 시점부터 선언 전까지 TDZ에 존재함
이러한 차이점은 let/const가 var보다 안전한 이유 중 하나이다.
아래는 책에 나와있는 예시이다.
console.log(foo) //foo is not defined
let foo;
console.log(foo); // undefined
foo = 1;
console.log(foo); // 1
요약 :
- var는 중복 선언이 가능하고 함수 레벨 스코프만 지원하며, 호이스팅 시 의도치 않은 동작이 발생할 수 있음
- let과 const는 블록 레벨 스코프를 지원하고, 중복 선언을 방지하며, TDZ로 인해 호이스팅 오류를 방지함
- 대부분의 현대 자바스크립트 코딩에서는 var 대신 let과 const를 사용하는 것이 권장됨
- 변경이 필요한 변수는 let, 불변 데이터는 const로 선언하여, 명확한 의도를 전달하고 버그를 줄이는 것이 바람직함
let, const 키워드의 전역 객체와 불변성에 대한 고찰
ES6에서 도입된 let과 const는 변수 선언 시 더 안전하고 의도 명확한 코드를 작성하게 도와주는 핵심적인 키워드이다.
- 전역 객체와 let
- const 키워드 : const 키워드로 선언한 변수는 선언과 동시에 초기화해야 한다. +) 재할당 금지
- const 키워드와 객체 : const 키워드로 선언된 변수에 객체를 할당하면, 값을 변경할 수 있다.
1. 전역 객체와 let
브라우저 환경에서 최상위 객체는 window, Node.js에서는 global이다. 이를 전역 객체(Global Object)라고 하며, var 키워드로 선언한 변수는 이 전역 객체의 프로퍼티로 자동 등록된다.
var a = 1;
console.log(window.a); // 1 (브라우저 환경)
반면, let과 const로 선언한 변수는 전역 스코프에 선언되더라도 전역 객체의 프로퍼티로 등록되지 않는다.
let b = 2;
const c = 3;
console.log(window.b); // undefined
console.log(window.c); // undefined
이는 let과 const가 보다 안전한 변수 선언 방식임을 보여주는 예이다.
전역 객체에 노출되지 않기 때문에 의도치 않은 전역 변수 오염 방지에 효과적이다.
2. const 키워드: 선언과 동시에 초기화 + 재할당 금지
const는 "상수(constant)"를 의미하며, 선언과 동시에 반드시 초기화되어야 하고, 재할당이 불가능하다.
const PI = 3.14;
PI = 3.14159; // TypeError: Assignment to constant variable.
또한 다음과 같이 초기화 없이 선언만 하려고 하면 에러가 발생한다.
const value; // SyntaxError: Missing initializer in const declaration
이러한 제약은 불변성을 강제하여 예측 가능한 코드 작성을 돕는다.
따라서 변경이 필요 없는 값에는 const를 우선 사용하고, 값 변경이 필요한 경우에만 let을 사용하는 것이 모범적인 패턴이다.
3. const 키워드와 객체: 참조값은 불변, 내용은 가변
많은 개발자가 오해하는 부분이 바로 const로 선언한 변수에 객체를 할당할 경우 내부 값을 변경할 수 있다는 점이다.
const user = {
name: 'Jin',
age: 25
};
user.age = 26;
console.log(user); // { name: 'Jin', age: 26 }
- const user는 user라는 변수 식별자에 저장된 참조값이 고정된 것
- 하지만 참조하는 객체의 내부 속성은 변경 가능
객체 자체를 재할당하면 오류 발생
user = { name: 'Kim' }; // TypeError
이처럼 const는 변수 자체의 주소(참조값) 변경만 막을 뿐, 객체 내부의 값까지 불변으로 만들지는 않는다.
진정한 불변 객체를 만들고 싶다면?
- Object.freeze() 사용
const config = Object.freeze({ debug: true });
config.debug = false;
console.log(config.debug); // 여전히 true
단, Object.freeze()는 얕은 불변성(shallow immutability)만 적용되므로 중첩된 객체까지 불변으로 만들려면 재귀적 동결이 필요하다.
요약
- let, const는 전역 스코프에 선언되어도 전역 객체(window/global)의 프로퍼티가 되지 않는다.
- const는 선언과 동시에 초기화가 필수이며, 재할당 불가
- 하지만 객체나 배열을 할당한 경우, 내부 값은 자유롭게 변경 가능
- const는 참조의 불변성을 보장하며, 완전한 불변성을 원할 경우 **Object.freeze()** 또는 라이브러리의 도움 필요
var vs let vs const
가급적 변수 선언은 const 로 하는 것이 가장 좋다. 그 외 let은 재할당이 필요한 경우에만 사용하는 것이 좋다.
ES6 부터는 var 키워드는 사용하지 않는다.(var의 상위호환 버전으로 let이 도입되었으므로 var를 사용할 이유가 없다.)
'개발' 카테고리의 다른 글
MPA 와 SPA (2) | 2025.07.13 |
---|---|
상태관리 (0) | 2025.07.13 |
DOM과 DOM API (0) | 2025.07.10 |
비동기와 API (1) | 2025.07.09 |
[모던 자바스크립트 DeepDive] Js 심화 스터디 week 02 (6) | 2025.07.08 |