1. APP 작동원리
여기서 App의 기본 작동원리가 있다.
위와 같은 작동원리가 있다. 여기서
1-1. 입력(Input)
사용자 또는 다른 시스템으로부터 받은 데이터이다. 예를 들어, 사용자가 웹 폼에 입력하는 정보, 센서에서 측정한 값, 파일에서 읽은 데이터 등이 여기에 해당된다.
1-2. 처리(Process)
입력받은 데이터에 대해 실행되는 모든 작업을 의미한다. 이 과정에서 데이터는 다양한 방식으로 변환, 계산, 분석된다.
여기에 기본요소로는 연산자, 함수 그리고 데이터를 저장하는 변수 등이 있다.
변수(Variables)
데이터를 저장하는 공간으로, 처리 과정에서 생성, 수정, 사용될 수 있는 데이터의 값을 담고있다.
연산자(Operators)
또한 산술, 비교, 논리 연산 등 데이터를 조작하는데 사용되는 기호이다. 예를 들어 더하기(+), 빼기(-), 곱하기(*), 나누기(/)와 같은 산술 연산자가 있다.
이러한 단순 연산자 뿐만 아니라 복잡한 작업을 수행하는 코드의 묶음인 함수가 있다.
함수(Function)
특정작업을 수행하기 위해 정의된 코드의 집합이다. 함수는 입력을 받아 처리 후 결과를 반환할 수 있으며, 코드의 재사용성을 높이는 데 유용하다.
함수 정의
function(함수정의키워드) add(<- 함수이름)(a, b)(<- 매개변수, 인자 parameter) {
return(<- 결과값 반환) a + b;
};
add(1, 2); // 함수호출
프로그래밍에서 중복되는 것을 함수 단위로 작은 단위의 일들을 묶어야 한다. 또한 수행 하는 일을 잘 나타낼 수 있는 이름으로 지어야한다. 매개변수 또한 의미있게 잘 지어야한다.
객체타입에 함수도 포함이 되어있다.
함수도 결국 객체이기 때문에 함수의 object가 heap에 저장이 된다.
함수의 이름은 함수가 저장되어있는 객체에 메모리주소를 가르키게 된다.
즉, 함수의 이름은 함수를 참조하고 있다. 함수의 객체가 저장되어 있는 메모리주소를 가지고 있다.
1-3. 출력(Output)
처리 과정을 거친 후의 결과이다. 이 결과는 사용자에게 표시되거나, 파일로 저장되거나, 다른 시스템으로 전송될 수 있다.
2. 함수(Function)
function add(a, b) {
const result = num1 + num2; // 변수로 바로 반환하는 경우 이렇게 안해도 된다.
return result;
}
함수의 예제들을 봐보자.
//사용예제 1
function add(a, b) {
console.log('function')
return a + b;
}
const result = add(1, 2);
console.log(result);
// 사용예제 2
function fullName(firstName, lastName) {
return `${lastName} ${firstName}`;
};
let lastName = '김';
let firstName = '지수';
console.log(fullName(firstName, lastName));
function fullName1(firstName1, lastName1) {
return `${lastName1} ${firstName1}`;
};
let lastName1 = '박';
let firstName1 = '철수';
console.log(fullName1(firstName1, lastName1));
함수 저장은 메모리 블록 어딘가에 저장이 되어있다.
함수의 이름 자체는 함수를 가리키는 변수와 동일하다.
함수의 이름을 어딘가에 할당한다는 것은 함수를 가리키는 그 메모리주소를 복사해서 할당하는 것과 같다.
함수의 이름 자체는 함수를 가리키고, 함수 호출하고 싶을땐 소괄호()를 이용해서 호출, 함수가 원하는 인자를 전달해줘야 함.
function add(a, b) {
return a + b;
}
const sum = add;
sum(1, 2);
add(1, 2);
console.log(sum(1, 2));
console.log(add(1, 2));
2-1. Return
Return을 명시적으로 하지 않으면 자동으로 undefined이 반환됨
function add(a, b) {
// return a + b;
return undefined;
}
const result = add(1, 2);
console.log(result); // undefined
return을 함수 중간에 하게 되면 함수가 종료됨
사용예시 : 조건이 맞지 않는 경우 함수 도입부분에서 함수를 일찍 종료함
function print(num) { // 함수 도입부분에서 전달받은 인자가 조건에 맞는지 안맞는지 확인해서 안맞으면 return으로 종료시킬수 있다.
if (num < 0) {
return;
}
console.log(num);
}
print(12); // 조건문에 일치하지 않아 다음 조건을 넘어가 12를 출력
print(-12); // 조건문에 일치하여 그대로 출력하지 않고 그대로 종료
2-2. 매개변수(parameters)
매개변수의 기본값은 무조건 undefined이다.
매개변수의 정보는 함수 내부에서 접근이 가능한 arguments 객체에 저장됨
매개변수 기본값 -> Default Parameters a=1, b = 1(기본값) 이렇게 할당이 가능
undefined인 경우에만 쓰임, 외부에 주어진 값이 있다면 그 값으로 쓰임
function add(a, b) { // 여기서 설정이 가능함
console.log(a);
console.log(b);
console.log(arguments[0]); // 1 첫번째
console.log(arguments[1]); // 2 두번째
console.log(arguments[2]); // 3 세번째
return a + b;
}
add(); // 전달하지 않으면 기본값인 undefined로 출력이 됨
Rest Parameters
Rest 매개변수 Rest Parameters는 배열로 바꾸고 싶을 때, ...을 사용한다. 인자의 숫자가 정해지지 않으면 배열로 받아올 수 있다.
function sum(a, b, ...numbers) {
console.log(a);
console.log(b);
console.log(numbers);
}
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
2-3. 함수 표현식(Function Expression), 함수 선언문(Function Delaration)
함수 선언문(Fuction Declaration)
함수 선언문은 function 키워드로 시작하여, 그 뒤에 함수의 이름, 괄호안의 매개변수 목록, 그리고 중괄호{} 안에 함수의 본문이 온다.
함수 선언문은 호이스팅(Hoistion)이 적용된다. 즉, 함수 선언문으로 정의된 함수는 코드 내 어디에서든지 호출할 수 있다. 코드가 실행되기 전 함수 선언이 메모리에 저장되기 때문이다.
function sayHello(name) {
console.log("Hello " + name);
}
sayHello("Doyoung"); // "Hello Doyoung"가 출력.
함수 표현식(Function Expression)
함수 표현식에서는 함수를 변수에 할당한다. 이 경우에는 일반적으로 이름이 없는 익명 함수이다.(물론 이름을 붙일수도 있다)
함수 표현식은 호이스팅이 적용되지 않는다. 함수가 변수에 할당되는 순서를 따르기 때문에, 함수를 변수에 할당하기 전에는 호출할 수가 없다.
let sayGoodbye = function(name) {
console.log("Goodbye " + name);
};
sayGoodbye("Doyoung"); // "Goodbye Doyoung"가 출력.
이는 화살표 함수로 표현할 수 있는데,
const sayGoodbye = (name) => "Goodbye" + name;
console.log("Goodbye" + name); // "Goodbye doyoung"이 출력.
여기서 요약하자면, 함수 선언문은 호이스팅에 의해 코드의 어느곳에서든 호출할 수 있고, 명시적으로 함수 이름을 가지고 선언된다. 반면 함수 표현식은 변수에 함수를 할당하는 방식으로, 해당 변수를 통해 함수를 참조하고 호출한다. 함수 표현식은 호이스팅의 영향을 받지 않아 선언 전에 호출 할 수 없다.
let add = function sum(a, b) { // 이름을 내분에서 지을수 있지만, 외부에서 접근을 못하기 때문에 에러가 나온다.
return a + b;
};
console.log(sum(1, 2)); // 이렇게 하면 에러가 나온다 그래서 변수로 선언된 것으로 넣어야함 add
함수를 변수에 할당할 때 함수에 이름을 지정하는 것은 문법적으로는 가능핟. 하지만 지정된 이름은 함수 내에서만 접근할 수 있는 식별자로 사용된다. 이를 함수 표현식의 명명된 함수 표현식(Named Function Expression)이라고 한다. 명명된 함수 표현식의 이점은 디버깅시 스택 트레이스에 이름이 나타나 함수를 더 쉽게 식별할 수 있다는 것이다. 하지만 이 이름은 함수 외부에서 접근할 수 없다.
여기서 sum 이라는 이름은 함수 내부에서만 접근 할 수 있으며, 함수 외부에서는 add라는 변수를 통해 함수를 참조해야 한다. 따라서 console.log(sum(1, 2)); 는 에러를 발생시킨다. 왜냐하면 sum은 외부 스코프에서 정의되지 않았기 때문이다. 그래서 올바른 호출 방식은
console.log(add(1, 2)); // 3 출력
와 같다.
IIFE(Immediately-Invoked Function Expressions)
이는 실행 함수 표현식으로 정의되자마자 즉시 실행되는 자바스크립트 함수이다. IIFE는 함수를 선언하고, 즉시 실행하기 위해 괄호() 로 묶는 방식으로 사용한다. 이 방식은 함수와 그 함수를 호출하는 괄호를 포함하는 전체 표현식을 다시 괄호로 감싸서 구분된다.
(function run() { // 함수를 ()로 묶으면 값으로 변환됨. 바로 호출이 가능함.
console.log('👍');
})();
위 코드에서는 run 함수는 정의 되자마자 바로 실행되며, "👍"가 콘솔에 출력이 된다. 함수에 이름을 지정하는 것은 선택이다. 이름이 없는 익명 함수를 사용한 IIFE도 있다.
(function() {
console.log('👍');
})();
함수에 이름을 제공하는 것은 디버깅 시 함수 스택에 이름이 표시되게 하여 디버깅을 용이하게 하기 위한 목적등에 유용할 수 있다. 그러나 IIFE의 본질은 이름이 없는 익명 함수를 즉시 실행하는 것이다.
IIFE는 주로 다음과 같은 상황에서 사용된다.
1. 전역 스코프의 오염을 방지하며, 코드를 모듈화 하는데 사용된다.
2. 임시 변수를 전역 스코프에 추가하지 않고, 일회성 계산이나 초기화 작업을 수행하는 데 유용하다.
3. 자바스크립트 라이브러리나 프레임워크 내에서 내부변수와 함수를 외부로부터 보호하는 사용된다.
3. 콜백함수(Callback Function)
3-1. 일급 객체(first-class object)
일급 객체 또는 일급 함수는 프로그래밍 언어에서 함수를 일반 객체처럼 다룰수 있는 개념을 뜻한다. 즉 함수를 변수에 할당할 수 있으며, 다름 함수의 매개변수로 전달하거나, 함수에서 반환값으로 사용할 수 있다. 또한 함수를 동일 비교 대상으로 사용할 수 있다.
자바(JAVA), 파이썬(Python), 코틀린(Kotlin), 스위프트(Swift) 등 다양한 프로그래밍 언어가 이 개념을 지원한다.
3-2. 고차함수(Higher-order Function)
고차 함수는 다른 함수를 매개 변수로 받거나(콜백 함수로), 함수를 반환하는 함수이다. 이는 함수형 프로그래밍의 핵심 개념 중 하나로, 코드의 재사용성을 높이고, 모듈화를 용이하게 하고, 추상화 수준을 높일 수 있게 해준다.
3-3. 콜백 함수(Callback Function)
콜백 함수는 다른 함수의 매개변수로 전달되는 함수를 말한다. 콜백 함수는 전달된 그 순간에 바로 실행되는 것이 아니라, 받는 함수(고차 함수) 내에서 필요한 순간에 호출된다.
콜백 함수는 비동기 작업 처리, 이벤트 처리, 배열 내장 함수(map, filterm reduce 등)의 사용 등 다양한 상황에서 활용된다.
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
전달된 action은 콜백함수이다.
전달된 당시에 함수를 바로 호출해서 반환된 값을 전달하는 것이 아닌 함수를 가리키고 있는 함수의 레퍼런스(참조값)이 전달된다.
그래서 함수는 고차함수안에서 필요한 순간에 호출이 나중에 된다.
function calculator(a, b, action) { // 외부로부터 함수를 전달받아서 전달할 당시에 바로 호출하는게 아니라,
//나중에 내부에서 호출한다. 이를 콜백함수라고 한다.
let result = action(a, b);
console.log(result);
return result;
}
calculator(1, 2, add); // 이름만 전달했을 뿐, 위에 선언된 변수의 주소값(참조값)을 전달함, 콜백형태로 전달함
calculator(1, 2, multiply);
여기 calculator 함수는 고차 함수이다. 왜냐하면 다른 함수(add, multiply)를 인자로 받아서 내부에서 이를 호출하기 때문이다. 전달된 action 매개변수는 콜백 함수의 역할을 한다. 콜백함수(add, multiply)는 calculator 함수 내에서 호출되며, 이 때 calculator 함수는 고차 함수 역할을 한다. 이 방식을 사용함에 따라 calculator 함수는 매우 유연해진다. 왜냐하면 어떤 종류의 계산을 수행하지를 호출 시점에서 결정할 수 있기 때문이다. 이러한 점으로 calculator 함수는 다양한 계산에서 재사용이 가능하다.
4. 퀴즈(Quiz)
문제
1. 주어진 숫자 만큼 0부터 순회하는 함수
2. 순회하면서 주어진 특정한 일을 수행해야함
예시)
5, 숫회하는 숫자를 다 출력하고 싶음(0, 1, 2, 3, 4, 5)
5, 순회하는 숫자의 두배값을 출력(0, 2, 4, 6, 8, 10)
내 답안
let numDouble = (max) => max * 2;
function iterate(max, action) {
for (let i = 0; i < max; i++) {
console.log(i, action(i));
};
};
iterate(6, numDouble);
위와 같이 변수에 순회하는 숫자를 두배하는 함수를 할당해주었다. 그리고 선언문에 반복문으로 순회하는 숫자를 출력하고, 그리고 콜백함수로 순회하는 값을 두배하는 함수를 매개변수로 넣어주어 출력하게 했다.
다른 사람 정답
function iterate(max, action) {
for (let i = 0; i < max; i++) {
action(i);
}
}
function log(num) {
console.log(num);
}
function doubleAndLog(num) {
console.log(num * 2);
}
// iterate(3, log); // action이 i 를 전달함, log가 0, 1, 2가 호출이 된다.
iterate(3, (num) => console.log(num));
iterate(3, (num) => console.log(num * 2));
setTimeout(() => {
console.log('3초 뒤에 이 함수 실행');
}, 3000);
5. 부수 효과(Side Effects), 순수 함수(Pure Function), 불변성(Immutability)
함수형 프로그래밍에서는 가능한 순수 함수를 사용하도록 권장하고 있다. 순수함수는 주어진 입력에 대해서만 결과를 결정하고, 외부 상태를 변경하지 않고, 외부 상태에 의존하지도 않는다. 이러한 원칙을 따르게 되다면, 프로그램 예측이 가능하며, 테스트가 쉬워지고, 버그를 줄일 수 있다.
function display(num) {
num = 5; // 절대 안됨, 원시의 값이지만 obj의 경우는 심각해짐
console.log(num);
}
const value = 4;
display(value);
console.log(value);
5-1. display 함수와 원시 값
- display 함수는 인자로 받은 num의 값을 변경한다. 그러나 이 변경은 함수 외부에 있는 value 변수에 영향을 주지 않는다. 왜냐하면 원시값(예: 숫자, 문자열 등)은 값에 의한 복사(pass-by-value)가 일어나기 때문이다. 즉 num은 value의 복사본이며, display 함수내에서 num을 변경한다 해도 원본 value에는 영향을 미치지 않는다.
- 결과적으로, 함수 외부의 value는 여전히 4이다.
5-2. displayOjb 함수와 객체
function displayObj(obj) { // 변경한다는 걸 명확하게 해야함
obj.name = 'Bob'; // 외부로부터 주어진(오브젝트)를 내부에서 변경
console.log(obj);
}
- displayOjb 함수는 인자로 받은 객체 obj 내부의 속성을 변경한다. 객체는 참조에 의한 복사(pass-by-reference)가 일어나므로, obj를 변경하면 이 변경이 원본 객체에도 영향을 미친다.
- 따라서 displayObj(doyoung)를 호출한 후 doyoung 객체의 name 속성은 Bob으로 변경된다.
5-3. ChangeName 함수와 순수 함수
function changeName(obj) { // 이름부터 변경하는 느낌을 주도록!
return { ...obj, name: 'Bod' }; // 반환할때는 새로운 오브젝트를 만들기!
}
- change 함수는 순수함수의 예이다. 이 함수는 새로운 객체를 생성하여 반환하며, 원본객체를 변경하지 않는다. 이 방식은 함수가 외부 상태를 변경하지 않으므로 부수 효과를 방지하고 함수의 순수성을 유지한다.
- changeName을 호출하더라도 원본 obj 객체는 변경되지 않으며, 변경된 새 객체가 반환된다.
5-4. 불변성(Immutability)
function changeName(obj) {
return { ...obj, name: 'New Name' };
}
const originalObj = { name: 'Original Name' };
const newObj = changeName(originalObj);
console.log(originalObj); // { name: 'Original Name' }
console.log(newObj); // { name: 'New Name' }
여기서 함수 선언식은 ...스프레드 연산자를 통해 복사를 한 후 name의 속성 값을 new name으로 변경한다.
그래서 아래 변수에 원본을 할당 해준 후, 이 원본 객체는 함수가 호출될 때 매개변수로 전달이 된다. 근데 이 원본 객체는 변경되지 않는다. 스프레드 연산자를 통해, 모든 속성을 복사 한 후, 새로운 객체는 원본 객체의 얕은 복사본이 된다.
그리고 복사된 새로운 객체에 대해서 name 속성을 New Name으로 설정한다. 이 변경은 복사된 새로운 객체에만 적용이 되며, 원본 객체에는 영향을 미치지 않는다.
마지막으로 이 새로운 객체(원본 객체의 속성을 복사하고, name 속성이 수정된)가 함수의 반환값으로 사용된다.
이렇게 함수에 관한 내용을 배우게 되었는데, 처음 얕은 복사에 대해서 이해가 되지 않았다. 근데 계속 봐보고 검색을 해보니 이해가 되는 거 같다. 원본 객체을 복사해준 후, 원본 객체는 놔두고 복사본에 대해 변경을 하는 것이다. 즉 매개변수로 함수의 원본객체를 전달 후 복사본을 만들 후 속성을 변경한다.
'JavaScript' 카테고리의 다른 글
[모던자바스크립트 Deep Dive] 5장. 표현식과 문 (0) | 2024.08.07 |
---|---|
[모던자바스크립트 Deep Dive] 4장. 변수 (0) | 2024.08.07 |
JavaScript 제어문 (1) | 2024.03.25 |
JavaScript 연산자 (1) | 2024.03.19 |
JavaScript 기본 개념 정리 (0) | 2024.03.17 |