함수 만들기 실습 #1
- n개의 정수를 전달하면 해당 정수들의 총합과 평균을 반환하는 calcNumbersTotalAndAverage함수 작성하기
1. n개의 정수를 전달한다 → 몇 개의 정수를 전달할지 모르는 → 스프레드 문법을 사용하여 전달받을 여러 개의 정수를 배열로 받기
2. 총합과 평균을 반환한다 → 하나의 객체를 만들어 프로퍼티로 총합과 평균을 담기
function calcNumbersTotalAndAverage(...numbers) {
var total = 0;
var average = 0;
for(var number of numbers) {
total += number;
}
average = total/numbers.length;
// 프로퍼티의 키를 생략하면 변수명이 키가 된다.
return {total: total, average: average}
}
var result = calcNumbersTotalAndAverage(10, 20, 30, 40, 50);
console.log(`총합: ${result.total}, 평균: ${result.average}`);
------------------------------------------------------------------
즉시 실행 함수: 선언과 동시에 실행되는 함수를 의미한다. 일회성(한 번 사용되는)의 특징을 지닌다.
재귀 함수는 함수 내에서 자기 자신을 다시 호출하여 작업을 수행하는 함수로 종료 조건을 직접 작성해 주어야 한다.
중첩 함수는 함수 안에 함수를 또 정의하는 것을 의미한다 → A함수 내에 선언된 B함수는 A함수를 호출하지 않으면 애초에 존재하지 않는 함수다. A함수 내에 B함수를 선언하고, B함수를 호출해야 A함수가 호출됐을 때 B함수가 실행될 수 있다.
------------------------------------------------------------------
변수의 scope
* 식별자(변수, 함수, 클래스 등)는 자신이 선언된 위치에 의해 다른 코드가 자신을 참조할 수 있는
유효범위가 결정되는데 이를 스코프라고 부른다 → scope: 식별자를 사용 및 호출할 수 있는 유효범위
함수 내에 선언된 변수는 블록{}내에서만 사용이 가능하다. 함수 블록 내에 선언된 변수는(매개변수 포함) 지역변수로,
지역변수는 함수가 호출되면서 생성되고, 함수가 종료되면서 소멸된다. 블록{} 내에 선언되지 않은, 어느 블록에도 속해있지 않은,
전역(global)에서 사용이 가능한 변수를 전역변수라고 한다. 지역변수의 예: 매개변수, {}블록내에 선언된 변수
- 이름이 동일한 변수가 존재한다면(전역변수명 x, 지역변수명 x) 가장 가까운 변수, 자신이 속해 있는 변수, 지역변수를 우선시 한다.
* var 키워드는 변수의 중복 선언을 암무적으로 허용(→ 에러가 나지 않는다)한다. var 키워드를 사용한, 중복된 변수는 의도치 않은 재할당으로 인해 값의 변경이 일어나므로 var 키워드의 사용을 지양한다.
var 키워드의 문제점
- 중복 선언을 허용한다 → 동일 이름 변수가 선언된 지 모르고 변수를 중복 선언하면 의도치 않게 값이 변경되는 문제점이 발생한다.- 블록 레벨 스코프를 지원하지 않는다 (함수만 블록으로 인정 ) → var 키워드는 오로지 함수의 영역만을 지역 스코프로 인식한다. 따라서 함수 외의(if문, for문 등의) 블록에서 var 키워드를 사용해 선언한 변수는 모두 전역변수로 일괄 적용된다. 전혀 관련 없는 변수의 값이 사용될 수 있고, 전역 변수가 남용될 가능성이 있다. - 변수 호이스팅: var 키워드로 변수를 선언하면 호이스팅에 의해 변수 선언문이 자동으로 맨위로 끌어올려진 것처럼 동작한다. 흐름 해석에 방해되어 가독성과 유지보수성을 현격하게 떨어트린다.
var 키워드 사용시 변수가 중복 선언되는 문제점을 보완해주는 let 키워드let 키워드를 사용해서 변수 선언시 변수의 중복 선언이 불가능하다. 또한 블록 레벨 스코프를 인정해준다. if문, while문, for문 등의 블록{} 내부에 let 키워드를 사용해서 변수 선언시 외부에서 해당 변수를 사용하지 못한다. let 키워드 사용시 변수 호이스팅 문제 또한 발생하지 않는다.
let과 const 키워드의 차이:
const 키워드를 사용해서 변수 선언시 선언과 동시에 값을 할당해야 하고, 처음 할당한 값은 추후에 변경이 불가하다 → 재할당 불가능. const 키워드는 객체, 배열, 함수에도 사용이 가능하며 const 키워드를 사용해서 객체를 선언할 경우 해당 객체가 지니는 속성의 값은 변경이 가능하다. 객체를 가리키는[참조하는] 주소 값 변경이 불가능한 것이지 객체 내부의 속성값은 변경이 가능한 것 → 힙 영역에 만들어진 객체의 주소 값이 스택 영역에 선언된 변수에 할당되고, 스택 영역의 변수에 저장된 객체를 가리키는 주소 값의 변경이 불가하다는 의미다. 값이 바뀔 가능성이 있는 변수들은 let 키워드를 사용해서 선언하고, 값을 할당한다.
---------------------------------------------------------------------
일급객체의 조건
- 무명의 리터럴로 생성할 수 있다 → 이름없이 중괄호에 선언하는 것. 즉, 런타임에 생성이 가능하다 → 이름이 없는 함수(익명함수)
- 변수나 자료구조(객체, 배열 등)에 저장할 수 있다 → 객체나 배열 안에 함수를 선언할 수 있다. 함수를 변수에 담을[저장할] 수 있다. 함수는 배열이나 객체에 저장이 가능하다.
- 함수의 매개변수에 함수를 전달할 수 있다 → 콜백함수
- 반환 값으로 함수를 전달할 수 있다.→ 자바스크립트의 함수는 위의 조건을 모두 만족하므로 일급 객체라 할 수 있다.
ES6문법에서 나온 화살표 함수
화살표 함수는 function이라는 키워드를 사용하지 않는다. 화살표 함수는 내부 코드가 단 한 줄이라면 → 블록{} 내부에 작성된 코드가 한 줄이라면 블록{} 생략이 가능하다. 또 그 한 줄의 코드가 리턴문이라면 return 키워드 또한 생략이 가능하다.
- 매개변수가 단 한 개라면 소괄호 생략이 가능하다 → const sayHello = name => console.log(`${name}님 안녕하세요!`);
→ 매개 값이 없거나() 여러 개라면(n1, n2, ...) 소괄호() 생략이 불가하다.
예를 들어,
const multiply = (n1, n2) => {
return n1*n2; // 화살표 함수의 내부 코드가 단 한 줄이라면 중괄호{} 생략이 가능하다.
}
//▼
const multiply = (n1, n2) => return n1*n2;
// 화살표 함수의 내부 코드가 단 한 줄이라면 중괄호{} 생략이 가능하며,
// 그 단 한 줄이 return문이라면 return 키워드 또한 생략이 가능하다▼
const multiply = (n1, n2) => n1*n2;
***객체가 지니는 일반 함수를 화살표 함수로 바꾸었을 때, this.프로퍼티를 인식하지 못하는 문제가 발생***
const dog = {
name: '초코',
age: 3,
favorite: ['산책', '낮잠'],
play: function() {
console.log(`${this.name} 멍멍이가 신나게 놉니다.`);
} // → 일반 함수를 화살표 함수로 작성했을 때 this가 dog객체를 참조하지 못함.
// 화살표 함수는 자신의 this를 가지지 않고, 외부의 this를 참조한다??
// 이 경우, this는 dog객체를 가리키지[참조하지] 않아 this.name이 undefined가 된다.
// 이를 해결하려면 화살표 함수가 아닌 일반 함수로 정의하는 것을 고수하면 된다.
}
콜백함수
동작을 파라미터화 시킨다.
---------------------------------------------------------------
배열 고차함수
- forEach() 함수는 배열의 요소를 반복하면서 하나씩 소비한다. forEach() 함수는 매개변수로 콜백함수를 받는다.
forEach() 함수는 반환 값이 없는 void타입이다.
- filter() 함수는 특정 조건에 의해 필터링된 새로운 배열이 반환된다.
filter() 함수의 경우 조건에 맞는 몇 개만 필터링한다. map() 함수는 조건대로 전부 매핑해서 배열을 반환한다.
예를 들어 filter() 함수의 경우 계정(account)의 값이 abc1234인 객체만 반환할 경우에 사용해서 반환되는 배열의 길이는 1개,
map() 함수의 경우 배열에 저장되어 있는 모든 객체에서 계정과 이름만 추출할 경우 각 객체별로 계정과 이름만 매핑되어 새로운 배열로 반환된다.
- reduce() 함수는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
리듀서 함수는 네 개의 인자를 가진다, 누산기, 현재값, 현재 인덱스, 원본 배열이다. 리듀서 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 된다 → array.reduce((누적값, 배열 요소) => 반복해서 실행할 내용(누적연산), (acc → 누적값의) 초기값)
* reduce: (규모·크기·양 등을) 줄이다[축소하다] → to make (something) smaller in size, amount, number, etc.
(가격 등을) 낮추다[할인/인하하다]; (음식 등을 끓일 때 국물을) 줄이다[졸이다]; (국물이) 줄어들다[졸다]; 체중을 줄이다, 살을 빼다
→ to cause (someone) to be in a specified state or condition
--------------------------------------------------------------------------------
let count = 0; // 전역 변수
// 카운트를 증가시키는 함수
// count++ → 후위연산자로 현재 count의 값을 반환한 뒤에 count의 값을 1증가시킨다.
// const increase = () => { return count++; }
/*
const increase = () => {
// 현재 count변수에 담긴 값을 새로 선언한 변수, currentValue에 담고,
const currentValue = count;
count++; // count의 값을 1증가시킨 다음
return currentValue; // currentValue변수를 반환한다.
}
*/
const increase = () => count++;
console.log(increase()); // 0
console.log(increase()); // 1
console.log(increase()); // 2
----------------------------------------------------------------------------
const increase = () => ++count; // count의 값을 1증가시킨 후 반환한다.
/*
const increase = () => {
count++;
return count;
}
*/
console.log(increase()); // 1
console.log(increase()); // 2
전역 변수의 경우 어느 곳에서나 접근할 수 있기에 값을 사용하는 것은 물론 값의 변경 또한 가능하다.
지역 변수는 자신이 속해 있는 영역이 끝나면 메모리에서 소멸되기 때문에 값을 유지할 수 없다 → 함수 호출이 종료되면 소멸하는 지역 변수의 특성
외부에서 값을 변경할 수 없게 하면서도(전역변수의 단점을 보완) 값을 유지하려고할 때(지역변수의 단점을 보완) 사용하는 함수가
바로 클로저다. 클로저를 사용하면 외부에서 접근할 수 없는 지역변수의 값을 누적해서 연산할 수 있는 강점이 있다(값 유지 가능)
----------------------------------------------
자바스크립트에선 실행중인 모든 각각의 함수, 코드블럭{...}, 전체 스크립트는 lexical environment(렉시컬 환경)이라 불리는 an internal (hidden) associated object(내부의 숨겨진 관련된 객체)를 갖는다 → 코드가 실행될 때 만들어지는 구조로 특정 범위 내에서 변수와 함수에 대한 정보를 저장한다 → 코드가 실행될 때 렉시컬 환경이라 불리는 객체가 만들어지고, 그 객체는 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성되는데, 환경 레코드에는 현재 범위에 정의된 변수와 함께 함수에 대한 실제 데이터를 포함한다. 예를 들면 함수 내에서 정의된 변수나 파라미터 등이다. 외부 렉시컬 환경에 대한 참조는 현재 범위의 부모 스코프에 대한 참조를 포함한다. 외부 렉시컬 환경에 대한 참조로 자바스크립트는 중첩된 함수나 블록 내에서 변수의 스코프를 확인할 수 있다.
let count = 0;
count 변수처럼 어떤 곳에서든 사용할 수 있고, 접근하여 변경할 수 있는 전역 변수는 전역 렉시컬 환경에서 관리된다.
전역 렉시컬 환경(객체)에서 환경 레코드에 프로퍼티로 저장된다 → count: 0
코드의 실행이 시작되고, 실행 흐름이 이어져 나가면서 렉시컬 환경은 변화한다.
스크립트의 실행이 시작되면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라간다.
스크립트의 실행이 시작되면 변수명인 count가 렉시컬 환경에 올라가고, let 키워드를 만나기 전까지 count라는 변수를 참조할 수 없다(사용할 수 없다) count: <uninitialized> → 만약 count를 let count; 이렇게만 선언했다면 count에 아직 값을 할당하진 않았으므로 프로퍼티의 값은 undefined이며, 선언은 했으므로 이후로 count를 사용할 순 있다. 그리고 선언 이후에 값을 할당하기 위해 count = 0; 이렇게 작성했다면 count에 0이라는 값이 할당되어 렉시컬 환경의 환경 레코드에 count: 0 이렇게 저장된다.
- 함수도 변수처럼 값이다. 변수와 다른 점은 함수 선언은 즉시 완전히 초기화된다는 것이다 → 자바스크립트 엔진은 코드가 실행되기 전에 함수 선언을 호이스팅(위로 끌어올린다)하기 때문에 함수가 정의되기 전에 호출해도 정상적으로 작동한다 → 자바스크립트가 스크립트를 실행하기 전에 함수 선언(function 함수명() {...}, Function Declaration)을 최상단으로 끌어올려서 초기화한다. 단, 함수 표현식(const 변수명 = function() {...})은 변수에 할당된 후에야 초기화되기 때문에 함수가 정의되기 전에 호출하려고 하면 에러가 발생한다. 함수를 호출해 함수가 실행될 때, 호출시 전달받은 매개변수와 함수의 지역변수가 저장되기 위한 새로운 렉시컬 환경이 자동으로 만들어진다. 함수 호출중엔 2개의 렉시컬 환경을 갖는데, 호출 중인 함수를 위한 내부 렉시컬 환경과 내부 렉시컬 환경이 가리키는 외부 렉시컬 환경이다.
클로저는 외부 변수를 기억하고, 이 외부 변수에 접근할 수 있는 함수다. 클로저는 함수이며, 클로저가 선언된 환경, 자기 자신이(클로저) 선언된 바로 그 영역에 선언된 지역변수를 기억하고(함수(클로저) 외부에 선언된 지역 변수), 그 지역변수에 접근할 수 있기에 클로저의 실행에 따라 그 지역변수의 값이 변경될 수 있다.
'공부기록용' 카테고리의 다른 글
[ javascript ] 끝말잇기 게임 프로그래밍 (3) | 2024.11.05 |
---|---|
[ javascript ] 24.10.30 클로저 기록 (1) | 2024.10.30 |
[ javascript ] 배열, 함수 기초(~ing) (0) | 2024.10.24 |
[ javascript ] 정리1 (5) | 2024.10.22 |
[ Servlet & JSP ] 톰캣 설치, JSP 프로그램 작성 (수동) (6) | 2024.10.18 |