본문 바로가기

study

혼공 JS 4주차

■ 내용정리

 

<익명함수>

함수도 자료형중에 하나입니다.

// 익명함수
// 함수도 하나의 자료형이기 때문에 아래와 같은 형태로 구성이 가능합니다.
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 ]

 

'study' 카테고리의 다른 글

혼공 JS 6주차  (0) 2023.07.04
혼공 JS 5주차  (0) 2023.07.04
혼공 JS 3주차  (0) 2023.07.04
혼공 JS 2주차  (0) 2023.07.04
혼공 JS 1주차  (0) 2023.07.04