■ 내용정리
<익명함수>
함수도 자료형중에 하나입니다.
// 익명함수
// 함수도 하나의 자료형이기 때문에 아래와 같은 형태로 구성이 가능합니다.
const test_fn = function () {
console.log("기록...")
}
// 일반적인 함수 형태
function test_fn2(){
console.log("기록222....")
}
<매개변수>
// 매개변수에 타입 정의는 오류
// 다른 언어에서는 매개변수의 타입을 정의해서 사용을 하는데
// JS에서는 이렇게 하면 오류가 발생한다.
function strFunc(string name){
console.log("매개변수에 타입 정의는 오류")
}
function strFunc(name){
console.log("매개변수에 타입 정의는 오류")
}
<나머지 매개변수>
매개변수가 동적으로 갯수를 정할수 없을때, 아래와 같은 형식으로 사용하면 된다.
// 이렇게 전달된 params은 배열로 구성이 된다.
function multiParams(...params) {
console.log(params)
}
// 모든 사항이 인자값으로 사용이 가능하다
multiParams(1,2,3)
multiParams(1,2,3,4,5)
multiParams("가", "나", function a(){console.log("test")}, [1,2,3,4,5])
/*
결과 생성은 Arrays로 구성이 된다.
[ 1, 2, 3 ]
[ 1, 2, 3, 4, 5 ]
[ '가', '나', [Function: a], [ 1, 2, 3, 4, 5 ] ]
*/
// 이렇게 전달된 params은 배열로 구성이 된다.
// 동적 매개변수 말고, 고정된 매개변수를 선언해서 사용할수 있다.
function multiParams(a,b, ...params) {
console.log(a, b, params)
}
// 모든 사항이 인자값으로 사용이 가능하다
multiParams(1,2,3)
multiParams(1,2,3,4,5)
multiParams("가", "나", function a(){console.log("test")}, [1,2,3,4,5])
/*
1 2 [ 3 ]
1 2 [ 3, 4, 5 ]
가 나 [ [Function: a], [ 1, 2, 3, 4, 5 ] ]
*/
가변 매개변수를 사용하면, 해당 요소의 타입을 체크하는게 중요하게 된다.
// string, number, function은 명확하게 조회가 되는데, Array는 object의 typeof 체크가 된다.
console.log(typeof ("AAA")) // string
console.log(typeof (111)) // number
console.log(typeof (function (){})) // function
console.log(typeof ([1,2,3,4,5])) // object
// Array인지 체크하기 위해서는 Array.isArray로 true / false를 체크하자
const arrayData = [1,2,3]
console.log(typeof arrayData) // 결과 : object, Object로 비교하기에는 정확하지 않다.
console.log(Array.isArray(arrayData)) // 결과 : true, 해당 방식으로 사용
<전개 연산자>
배열을 전개해서 함수의 매개변수로 전달해주는 전개연산자를 다양하게 사용 가능
형식 : 함수이름(...배열)
function testParam(...a){
console.log(a)
}
const data = ["가", "나", "다"]
testParam(data) // [ [ '가', '나', '다' ] ], 결국 배열의 배열이 되는 구조이다.
testParam(...data) // [ '가', '나', '다' ], 전개연산자를 통해서 호출하면, 하나하나 전개되어서 제공된다.
<매개변수 기본값>
js에서도 매개변수의 기본값을 설정할수가 있다.
function defaultSetPay(age, pay = 9900, working = 1){
if(age < 20){
console.log("아직 연령대가 맞지 않습니다.")
}else {
console.log(`지급 비용은 ${pay * working}입니다.`)
}
}
defaultSetPay(20) // 9900
<구버전 자바스크립트에서 가변 매개변수 함수 구현하기>
arguments
가변 매개변수 함수를 구현할때 배열 내부에서 사용할수 있는 특수한 변수인 arguements를 활용합니다.
배열과 비슷하지만, 배열은 아닙니다. (callee:f와 Symbal.iterator):f)와 같은 특이한 값이 있습니다.
// 매개변수 값을 arguments는 없다.
// 실제 호출해보면, 전달된 매개변수가 잘 동작한다.
function sample(){
console.log(arguments)
for (let i = 0; i < arguments.length; i++) {
console.log(`${i}번째 요소 : ${arguments[i]}`)
}
}
sample(1,2)
sample(1,2,3)
sample(1,2,3,4)
/*
[Arguments] { '0': 1, '1': 2 }
0번째 요소 : 1
1번째 요소 : 2
[Arguments] { '0': 1, '1': 2, '2': 3 }
0번째 요소 : 1
1번째 요소 : 2
2번째 요소 : 3
[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
0번째 요소 : 1
1번째 요소 : 2
2번째 요소 : 3
3번째 요소 : 4
* */
<구버전JS에서 전개 연산자 구현하기>
전개연산자가 없던 시전에는 apply함수를 사용했다는것을 기억
function sample(...item){
console.log(item) // [ 1, 2, 3, 4 ]
}
// 예전에 호출 방식
const array = [1,2,3,4]
console.log(sample.apply(null, array) // undefined
<구 버전 자바스크립트에서 기본 매개변수 구현하기>
매개변수에 바로 값을 입력하는 기본 매개변수는 최신 자바스크립트에 추가된 기능
undefined을 비교해서 처리하는 방식
// typeof (age) != undefined 이렇게 식별하면, 조건이 안걸림
function test(age, hours) {
age = typeof (age) != "undefined" ? age : 100;
hours = typeof (hours) != "undefined" ? hours : 52;
return age * hours
}
console.log(test())
false또는 false로 변환되는 값(0, 빈문자열등)이 아니라는게 확실하다면, 아래와 같은 조건식을 사용해도 된다.
function test(age, hours) {
age = age || 1000
hours = hours || 20
return age * hours
}
console.log(test()) // 20000
// || 조건이 가능한 이유는, 아래 조건식 때문이다.
console.log(undefined || 3340) // 3340
console.log(true || 3340) // true
console.log(true && 3340) // 3340
console.log(false || 2000) // 2000
console.log(false && 2000) // false
<함수 고급>
자바스크립트에서 함수는 자료이므로, 변수에 할당할수 있고, 함수를 함수의 매개변수로 전달해서 사용할수 있다.
- "함수도 하나의 자료" 라는 개념
- 익명함수 : 람다의 기본분법에 포함
<콜백함수>
함수도 하나의 자료형으로 매개변수로 전달할수 있다.
이러한 것을 "콜백함수" 라고 한다.
// print라는 함수를 매개변수로 사용하는 예제
function callThreeTimes(callback) {
for (let i = 0; i < 3; i++) {
callback(i)
}
}
function print(i){
console.log(`${i}번째 함수 호출`)
}
callThreeTimes(print)
위에 예제를 다른 방식으로 구성하는 예시
function callThreeTimes(callback) {
for (let i = 0; i <3; i++) {
callback(i)
}
}
// 여기서 정의한 function에는 이름이 없다. callback함수의 인자값으로 사용되는 목적
callThreeTimes(function (i) {
console.log(`${i}번째 함수 호출`)
})
<콜백함수를 활용하는 함수 : forEach()>
- 콜백함수를 활용하는 가장 기본적인 함수
- 배열이 갖고 있는 함수(메소드)로써 단순하게 배열 내부의 요소를 사용해서 콜백함수를 호출
- 콜백함수 형태 : function(value, index, array) { }
- forEach에 대해서, callbackfn의 인자값을 잘 살펴보면 좋습니다.
// a, b, c에 대해서 value, index, array 의 항목입니다.
const numbers = ["홍길동","tomcat","node","rust","db"]
numbers.forEach(function (a,b,c) {
console.log(`a=${a} b=${b} c=${c}`)
})
/*
a=홍길동 b=0 c=홍길동,tomcat,node,rust,db
a=tomcat b=1 c=홍길동,tomcat,node,rust,db
a=node b=2 c=홍길동,tomcat,node,rust,db
a=rust b=3 c=홍길동,tomcat,node,rust,db
a=db b=4 c=홍길동,tomcat,node,rust,db
*/
<콜백함수를 활용하는 함수 : map()>
- map()메소드도 배열이 갖고 있는 함수입니다.
- 콜백함수에서 리턴한 값들을 기반으로 새로운 배열을 만드는 함수입니다.
<콜백함수를 활용하는 함수 : filter()>
- filter()메소드도 리턴하는 값이 true인것만 모아서 새로운 배열을 만드는 함수이다
<화살표 함수>
- 콜백함수를 단순하게 만드는 생성 방법!!
- 콜백함수는 이름도 없고 function을 적는게 의미가 없기도 하다
- 내부에서 this 키워드가 지칭하는 대상이 다르다는 미세한 차이가 있다.
// case1 : 여러개 처리
(매개변수) => {
}
// case2 : 처리 1줄
(매개변수) => 리턴값
<콜백함수 Chaining하기>
- map, filter, forEach에 대해서 조합을 할수 있다
map() | 리턴한 값들을 기반으로 새로운 배열을 만드는 함수 |
filter() | 리턴하는 값이 true인 것들만 모아서 새로운 배열을 만드는 함수 |
forEach() | 단순하게 배열 내부의 요소를 사용해서 콜백함수를 호출 |
- 해당 콜백 함수를 사용하는 순서에 대해서 살펴보는것도 흥미로운 부분입니다.
// [정상동작] : filter > map > forEach
let aaa = [0,1,2,3,4,5,6,7,8,9]
aaa.filter((value) => value % 2 ===0)
.map((value) => value* value)
.forEach((value) => {
console.log(value)
})
// [정상동작] : map > filter > forEach
let aaa = [0,1,2,3,4,5,6,7,8,9]
aaa.map((value) => value* value+1)
.filter((value) => value % 2 ===0)
.forEach((value) => {
console.log(value)
})
// [비정상 동작] : forEach > filter
// [비정상 동작] : forEach > map
// 원인은 forEach는 새로운 배열을 return하지 않습니다.
let aaa = [0,1,2,3,4,5,6,7,8,9]
aaa.forEach((value) => {
console.log(value)
})
.filter
map함수에서 if 조건이 가능할까?
결론) map은 처리하는 array에 대해서 갯수 조정없이 그대로 반환한다.
그래서 특정 조건을 처리하는것은 크게 로직을 처리할수는 없다.
let numbers = [10, 2, 3, 4, 239]
numbers = numbers.map( function (value){
if(value > 100){
return value
}
})
console.log(numbers) // [ undefined, undefined, undefined, undefined, 239 ]
numbers = numbers.map( function (value){
return value > 100 ? value : "999"
})
console.log(numbers) // [ '999', '999', '999', '999', 239 ]
<타이머 함수>
- setTimeout(함수, 시간) : 특정 시간 후에 함수를 한번 호출
- setInterval(함수, 시간) : 특정시간마다 반복적으로 호출
// 반복적으로 호출하는지, 한번만 호출하고 끝나는지 여부에 차이가 있음
- clearTimeout(타이머ID)
- clearInterval(타이머ID)
// 코드로 검증하는 2가지 사항
// 체크1) 타이버를 종료하는 id값은 무슨 형태일까?
// 체크2) 종료할때, Interval, timeout을 일치 시켜야 할까?
let id
let count = 0
id = setInterval(() => {
console.log(`1초마다 실행됩니다. (${count}번째)`)
count++
}, 1000)
// [검증2]clear할때, clearInterval, clearTimeout 모두 동작을 합니다.
setTimeout( () =>{
console.log("타이머를 종료합니다.")
console.log(id)
clearInterval(id)
clearTimeout(id)
}, 10 *1000)
// 검증1
// id값을 찍어보면, 예상과 다르게, 객체정보가 나옵니다.
Timeout {
_idleTimeout: 1000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 9024,
_onTimeout: [Function (anonymous)],
_timerArgs: undefined,
_repeat: 1000,
_destroyed: false,
[Symbol(refed)]: true,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 2,
[Symbol(triggerId)]: 1
}
<즉시호출 함수>
- 해당 문법을 반드시 알아두면 좋습니다. 조금은 이상하게 보이지만, 모르면 해석이 안되는 코드 문법입니다.
// 함수 즉시 호출하기
// 익명 함수를 생성하고 바로 즉시 호출하기
(function () {})()
- 여러개의 script를 외부 include하면 동일한 변수명이 선언되어서 사용되는 경우가 충분히 있을수 있습니다.
- 스코프가 같은 단계이면 오류가 발생
변경하는 방법은 2가지 존재
1) 중괄호를 사용해서 블록을 구성
2) 함수를 생성해서 블록을 만드는 방법
- 스코프 테스트
let a = 111
console.log(`step1=${a}`) // step1=111
// 중괄호를 이용해서 스코프 보장
// 동일 변수명이지만, 고유 값 보장!!
{
let a = 222
console.log(`step2=${a}`) // step2=222
}
console.log(`step3=${a}`) // step3=111
// 중괄호를 이용해서 스코프 보장
// 동일 변수명이지만, 고유 값 보장!!
function sample(){
let a = 333
console.log(`step4=${a}`) // step4=333
}
sample()
console.log(`step5=${a}`) // step5=111
- 위에 코드와 동일하지만 let을 var로 변경해서 테스트 하였다.
// 아래 코드를 보면, var코드로 모두 변경을 하였다.
// 결론을 말하면, var에서는 { } 중괄호를 이용한 코드에서는 스코프 유지가 안되었다
// 즉, var 변수에서 { } 은 무용지물 -> 없는것과 같은 의미
// function은 동작을 한다. function{ } 안에 있는 변수는 스코프를 유지함
var a = 111
console.log(`step1=${a}`) // step1=111
{
var a = 222
console.log(`step2=${a}`) // step2=222
}
// var로 변경해서 값이 변경됨!!!
console.log(`step3=${a}`) // step3=111 -> step3=222
function sample(){
var a = 333
console.log(`step4=${a}`) // step4=333
}
sample()
// var로 변경해서 값이 변경됨!!!
console.log(`step5=${a}`) // step5=111 -> step3=222
- 이런 문제를 "즉시 호출함수 문제로 해결합니다."
// var를 사용하는 중괄화 { }에 대해서, 즉시함수 호출 방식
// (funciton(){}) ()을 이용해서 호출하면, { }의 역활이 가능하다.
var a = 111;
// 아래 코드를 활성화 하면 오류 발생
// console.log(`step1=${a}`); // step1=111
// 즉시 함수 호출 방식으로 변경
(function () {
var a = 222
console.log(`step2=${a}`) // step2=222
})()
// var로 변경해서 값이 변경됨!!! -> 다시 변경됨
console.log(`step3=${a}`) // step3=111 -> step3=222 -> step3=111
function sample(){
var a = 333
console.log(`step4=${a}`) // step4=333
}
sample()
// var로 변경해서 값이 변경됨!!! -> 다시 변경됨
console.log(`step5=${a}`) // step5=111 -> step3=222 -> step3=111
<엄격모드>
- 코드의 문법을 체크합니다.
- var를 주로 사용하는 구문에서도 use strict가 선언되어 있으면 체크합니다
// data를 let, const, var 어떤것을 사용해도 됩니다.
'use strict'
var data = "10";
console.log(data); // data is not defined
// 함수 안에서도 개별적으로 사용이 가능합니다.
function f() {
'use strict'
data = 20;
console.log(data);
}
f();
<익명함수와 선언적 함수의 차이>
정답은 없지만, 최근에는 익명 함수를 선호하는 편입니다. (by. p.235)
- while, for 반복문의 차이는?
책에서 정의되어 있는 멘트가 좋아서 기록합니다.
while은 조건을 중심으로 반복할때, for반복문은 횟수를 중심으로 또는 배열을 중심으로 반복할때 사용합니다.
- 아래는 매우 중요한 개념입니다.
1) 익명함수 테스트
익명함수 선언에서도 var를 사용할수는 있습니다.
// 이 코드는 크게 어려움이 없습니다.
// 1. 선언을 위에서 합니다.
// 2. 사용을 선언되고 난 뒤에서 합니다.
let 익명함수;
익명함수 = function (){
console.log("1 익명함수");
}
익명함수 = function (){
console.log("2 익명함수");
}
익명함수() // result : 2 익명함수
// 익명함수가 선언되지 않은 상태에서 호출을 하면, 오류가 발생합니다.
let 익명함수;
익명함수() // [오류발생] TypeError: 익명함수 is not a function
익명함수 = function (){
console.log("1 익명함수");
}
익명함수 = function (){
console.log("2 익명함수");
}
2) 선언적 함수
"순차적으로 코드 실행이 일어나기 전에 생성됩니다.!!!
따라서 선언적인 함수는 같은 블록이라면 어디에서 함수를 호출해도 상관이 없습니다."
아래 예시에서 선언적함수() 를 미리 호출하였는데, 정상적으로 동작합니다.
어디에서 호출하는지 동일한 값을 호출합니다.
추가로 두번째 호출한 선언적함수()도 위에 "1 선언적함수"를 호출하는 것이 아니라, 최종적인 마지막 함수를 호출합니다.
// 매우 중요한 개념입니다.
// 선언적 함수는 어디서 선언을 하던지, 맨 밑에 있는 함수가 호출됩나다.
// 선언적함수를 위에서 선언안했는데, 맨위에 선언적함수()을 선언해서 사용할수 있습니다.
선언적함수() // result : 2 선언적함수
function 선언적함수() {
console.log("1 선언적함수")
}
// 해당 부분도 순차적으로 실행하면, 바로 위에 '1 선언적함수'가 출력될것 같은데,
// 그렇지 않습니다.
선언적함수() // result : 2 선언적함수
function 선언적함수() {
console.log("2 선언적함수")
}
선언적함수() // result : 2 선언적함수
3) 선언적 함수와 익명 함수를 같이 사용하는 경우!!
2가지 사항은 충분히 섞여서 사용될수 있습니다. 예전에는 선언적함수 형태를 많이 사용하였는데, 최근에는 익명함수 형태로 많이 사용을 합니다.
- 결론 : 같이 사용하게 되면, 익명함수가 실제 최종적으로 사용하게 된다.
그래서 혼란이 없도록 익명함수를 사용하는것이 제일 선호되는 방식이다.
// 함수 호출을 어느 시점에 하던지, 최종적으로 사용되는 함수는
// 선언적 함수:X, 익명함수가 사용된다.
// 그 이유는 선언적인 함수는 미리 로딩이 되고, 가장 나중에 익명함수가 사용되기 때문이다.
function 함수(){
console.log("선언적 함수입니다.11");
}
함수 = function () {
console.log("익명 함수입니다.");
}
function 함수(){
console.log("선언적 함수입니다.11");
}
함수()
function 함수(){
console.log("선언적 함수입니다.22");
}
■ 기본미션
function isLeapYear(year) {
return (year % 4 === 0) && (year % 100 !== 0) || (year % 400 === 0)
}
console.log(`2020년은 윤년일까? === ${isLeapYear(2020)}`)
console.log(`2010년은 윤년일까? === ${isLeapYear(2010)}`)
console.log(`2000년은 윤년일까? === ${isLeapYear(2000)}`)
console.log(`1900년은 윤년일까? === ${isLeapYear(1900)}`)
/* result
2020년은 윤년일까? === true
2010년은 윤년일까? === false
2000년은 윤년일까? === true
1900년은 윤년일까? === false
*/
■ 선택미션
p. 240 확인 문제 1번 풀고, 풀이 과정 설명하기
let numbers = [273, 25, 75, 52, 103, 32, 57, 24, 76]
let result = numbers.filter( (value) => value%2 ===1)
.filter(value => value <=100)
.filter(value => value%5 ===0)
console.log(result)
// [ 25, 75 ]