프론트엔드/Javascript

자바스크립트 문법 12 - 함수(function)의 기본

syleemomo 2021. 10. 9. 11:43
728x90

 

함수 참고문서 1

 

함수 - JavaScript | MDN

보통 함수란 자신의 외부(재귀 함수의 경우 스스로) 코드가 '호출'할 수 있는 하위 프로그램입니다. 프로그램과 마찬가지로, 함수 역시 명령문의 시퀀스로 구성된 함수 본문을 가집니다. 함수에

developer.mozilla.org

 

* 함수의 개념

함수는 특정 기능을 수행하는 코드 블럭이다. 함수는 입력값을 가지거나, 출력값을 가질수 있다. 자바스크립트에서의 함수는 속성과 메서드를 가질수 있는 객체이다. 

function add(a, b){
    return a + b
}

console.dir(add)

함수도 객체이다

함수도 객체이므로 console 객체의 dir 메서드로 함수를 출력해보면 위와 같은 속성(프로퍼티)을 가진다. arguments, caller, length, name, prototype 객체 등이다. 

function add(a, b){
    return a + b
}

add['description'] = 'add two numbers'
add['parameter-info'] = function(){
    console.log(`parameters: ${add.name} function needs ${add.length} parameters`)
}

console.dir(add)

console.log('--- function information ---')
console.log('description: ', add['description'])
add["parameter-info"]()

함수도 객체이므로 프로퍼티와 메서드를 가질수 있다. 위 코드는 add 함수에 description 프로퍼티와 parameter-info 라는 메서드를 추가한다. add 함수의 name 프로퍼티는 함수의 이름을 의미하며, length 프로퍼티는 함수의 파라미터 갯수를 의미한다. 맨 아래에서 해당 함수에 대한 정보를 출력하고 있다. add["parameter-info"] 는 add 함수에 정의된 메서드이므로 괄호를 이용하여 메서드를 실행하고 있다. 

 

* 함수를 사용하는 이유

함수를 사용하는 이유는 코드의 재사용성이다. 효율적인 코드를 작성하려면 코드의 중복은 최대한 피해야 한다. 그러므로 반복되는 코드블럭은 따로 빼서 함수로 정의하는 것이 코드의 간결함과 가독성에도 도움이 된다. 또한, 함수를 사용하는 또다른 이유는 코드의 유지보수 측면이다. 함수를 사용함으로써 프로그램에서 함수가 사용되는 부분에 오류가 있거나 개선할 점이 있을때 함수 내부만 수정하면 된다. 

const a = 3
const b = 5

if(a < b){
    console.log(b - a)
}else{
    console.log(a - b)
}

위 코드에서 a - b 나 b - a 는 코드 중복이다. b - a 에서 순서만 변경하고 마이너스를 앞으로 빼내면 아래와 같다. 

const a = 3
const b = 5

if(a < b){
    console.log(-(a - b))
}else{
    console.log(a - b)
}

뺄셈에 관련된 중복코드를 함수로 만들면 아래와 같다. 

const a = 3
const b = 5

function subtract(a, b){
    return a - b
}

if(a < b){
    console.log(-subtract(a, b))
}else{
    console.log(subtract(a, b))
}

또는 아래와 같이 삼항연사자를 사용하여 조건문 전체를 함수화 할수도 있다. 

const a = 3
const b = 5

function subtract(a, b){
    return a < b ? b - a : a - b
}

console.log(subtract(a, b))

이렇듯 함수를 사용함으로써 프로그램을 좀 더 간결하고 유지보수하기 쉽도록 만들수 있다. 

 

* 함수를 정의하는 방식 (함수선언, 함수 표현식, 화살표 함수)

함수선언 

function 함수이름(파라미터1, 파라미터2, ...){
	코드블럭
    
    return 반환값
}

함수를 정의하는 첫번째 방법은 위와 같이 function 키워드를 이용한 함수선언 방식이다. function 키워드 다음에는 함수이름이 따라오고, 함수 내부에서 사용할 파라미터 값(지역변수)이 괄호 안에 콤마로 구분되어 설정된다. 중괄호({}) 안에는 함수에서 실행될 코드블럭이 정의된다. 마지막으로 return 키워드를 이용하여 반환값을 설정할 수 있다. 파라미터와 return 키워드는 생략 가능하다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

// 배열 복사하기
function copyArr(arr){
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr(launguage)
console.log('check array is same: ', launguage === copiedArr) // 두 배열의 참조값이 같은지 검사
console.log(copiedArr)

위 코드는 배열을 복사하는 함수 copyArr 를 정의한다. arr 은 함수 내부에서 사용하는 파라미터(지역변수)이다. copiedArr 는 함수의 반환값이다. copiedArr 는 지역변수로써 빈 배열로 초기화한 다음 arr 로 전달된 배열의 모든 요소들을 해당 배열에 복사한다. 출력부분에서는 language 배열과 copiedArr 배열의 참조값이 같은지 검사한다. 결과는 false 이다. 

 

함수 표현식 

const 함수이름 = function (파라미터1, 파라미터2, ...){
	코드블럭
    
    return 반환값
}

함수 표현식은 함수 선언과 유사하지만 함수를 변수에 할당하는 형태를 가진다. 할당할 변수이름은 함수이름이 된다. 또한, function 키워드로 정의한 우변의 함수는 함수이름이 없으므로 익명함수라고 부른다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copyArr = function(arr){
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr(launguage)
console.log('check array is same: ', launguage === copiedArr)
console.log(copiedArr)

함수선언에서 정의한 copyArr 함수를 함수 표현식으로 다시 정의하면 위와 같다. 

 

함수선언과 함수 표현식의 차이점

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copiedArr = copyArr(launguage)
console.log(copiedArr)

function copyArr(arr){
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

함수선언으로 정의한 copyArr 함수는 function 키워드로 정의하기 전에 호출이 가능하다. 이를 함수 호이스팅(hoisting)이라고 한다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copiedArr = copyArr(launguage)
console.log(copiedArr)

const copyArr = function(arr){
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

이에 비해 함수 표현식으로 정의한 copyArr 함수는 function 키워드로 정의하기 전에 호출하면 아래와 같은 에러를 발생시킨다. 즉, 함수 호이스팅이 불가능하다.

함수표현식은 호이스팅이 불가능하다

 

화살표 함수

const 함수이름 = (파라미터1, 파라미터2, ...) => {
	코드블럭
    
    return 반환값
}

화살표 함수는 function 키워드 대신에 화살표(=>)를 사용하여 함수를 정의한다. 함수선언이나 함수 표현식에 비하여 좀 더 간결하게 함수를 정의할 수 있어서 간단한 코드블럭을 가진 함수 정의에 많이 사용된다. 물론, 복잡한 코드블럭도 화살표 함수로 정의할 수 있다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copyArr = (arr) => {
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr(launguage)
console.log(copiedArr)

위 코드는 copyArr 함수를 화살표 함수로 정의한다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copyArr = arr => {
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr(launguage)
console.log(copiedArr)

파라미터가 1개인 경우에는 위와 같이 괄호를 생략할 수 있다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copyArr = arr => {
    return arr
}

const copiedArr = copyArr(launguage)

console.log(launguage === copiedArr)
console.log(copiedArr)

위 코드는 전달받은 arr 배열을 그대로 반환한다. 코드블럭이 한줄이면 아래와 같이 중괄호({})와 return 키워드는 생략이 가능하다. 

const launguage = ['korean', 'japanese', 'english', 'chinese', 'russian']

const copyArr = arr => arr

const copiedArr = copyArr(launguage)

console.log(launguage === copiedArr)
console.log(copiedArr)

 

함수선언과 화살표 함수의 차이점 (this, 생성자함수로 사용가능 여부, arguments)

const music = {
    name: '금요일에 만나요',
    artist: 'IU',
    release: '2013-12-20',
    info(){
        console.log(`${this.name} - ${this.artist} 는 ${this.release} 에 발매되었다.`)
    }
}

music.info()

함수선언 내부의 this 는 해당 함수를 감싸고 있는 객체를 가리킨다. 여기서는 this 와 music 객체는 동일한 의미이다. 

const music = {
    name: '금요일에 만나요',
    artist: 'IU',
    release: '2013-12-20',
    info: () => {
        console.log(`${this.name} - ${this.artist} 는 ${this.release} 에 발매되었다.`)
    }
}

music.info()

하지만 함수 표현식 내부의 this 는 자신을 감싸고 있는 객체를 가리키지 않는다. 여기서는 this 와 music 객체가 동일하지 않다. 

그렇다면 화살표 함수에서 this 는 무엇을 가리킬까?

const music = {
    name: '금요일에 만나요',
    artist: 'IU',
    release: '2013-12-20',
    info: () => {
        console.log(this)
    }
}

music.info()

화살표 함수 내부에서 this 를 출력해보면 아래와 같이 윈도우(window) 객체를 가리킨다. 

화살표 함수 내부의 this 의 의미 - 윈도우 객체

윈도우(window) 객체에는 name, artist, release 프로퍼티가 존재하지 않으므로 info 메서드를 실행하면 undefined 가 출력되는 것이다. 

this.name = '보고 싶다'
this.artist = '김범수'
this.release = '2002-12-17'

const music = {
    name: '금요일에 만나요',
    artist: 'IU',
    release: '2013-12-20',
    info: () => {
        console.log(`${this.name} - ${this.artist} 는 ${this.release} 에 발매되었다.`)
    }
}

music.info()

위 코드의 info 메서드는 제대로된 음원 정보를 출력한다. music 객체 외부에 존재하는 this 는 브라우저에서는 윈도우(window) 객체를 가리키며, 해당 객체에 name, artist, release 프로퍼티를 추가하였다. 즉, 위 코드에서 this 는 music 을 가리키는 것이 아니라 윈도우(window) 객체를 가리킨다. 

function Music(){
    this.name = '보고 싶다'
    this.artist = '김범수'
    this.release = '2002-12-17'

    const music = {
        name: '금요일에 만나요',
        artist: 'IU',
        release: '2013-12-20',
        info: () => {
            console.log(this)
            console.log(`${this.name} - ${this.artist} 는 ${this.release} 에 발매되었다.`)
        }
    }
    return music
}

const music = new Music()
music.info()

위 코드는 Music 생성자 함수로 music 객체를 생성하고 info 메서드를 호출한다. 이때 info 메서드는 화살표 함수로 정의하였다. info 메서드 내부의 this 를 출력해보면 아래와 같이 생성자 함수(Music)를 가리킨다. 

화살표 함수 내부의 this 의 의미 - Music 생성자 함수

 

결론적으로 정리하면 함수선언으로 정의한 this 는 함수를 감싸고 있는 객체를 가리키지만, 화살표 함수로 정의한 this 는 함수를 감싸고 있는 객체보다 상위에 존재하는 this 를 가리킨다. 

 

function Music(){
    this.name = '보고 싶다'
    this.artist = '김범수'
    this.release = '2002-12-17'
}

const music = new Music()

두번째 차이점은 함수선언은 위와 같이 생성자 함수를 정의할 수 있지만, 화살표 함수는 아래와 같이 생성자 함수를 정의하지 못한다. 

const Music = () => {
    this.name = '보고 싶다'
    this.artist = '김범수'
    this.release = '2002-12-17'
}

const music = new Music()

위 코드는 아래와 같은 에러를 발생시킨다. 

화살표 함수는 생성자를 정의하지 못한다

 

function add(a, b){
    console.log(arguments) // arguments 객체
}

add("apple", 'banana')

세번째 차이점은 함수선언으로 정의한 함수는 arguments 객체를 사용할 수 있다. arguments 객체는 유사배열이며 객체와 배열의 중간단계라고 할 수 있다. 배열처럼 배열요소에 접근할 수 있으며, 객체처럼 프로퍼티에도 접근 가능하다. 

arguments 객체

 

function add(a, b){
    console.log(arguments) // arguments 객체
    
    console.log(arguments[0]) // 함수의 첫번째 인자
    console.log(arguments[1]) // 함수의 두번째 인자
    console.log(arguments.length) // 함수 인자의 갯수
    
    console.log(arguments.callee) // add 함수 자체
}

add("apple", 'banana')

arguments 객체의 배열요소들은 함수의 입력값으로 전달된 각각의 인자를 가리킨다. 여기서는 "apple" 과 "banana" 이다. 즉, arguments[0] 는 "apple", arguments[1] 은 "banana" 이다. arguments.length 는 add 함수의 인자 갯수이다. arguments.callee 는 add 함수 자체를 가리킨다. 

const add = (a, b) => {
    console.log(arguments) // arguments 객체
}

add("apple", 'banana')

하지만 화살표 함수에는 arguments 객체가 존재하지 않는다. 그러므로 아래와 같이 에러를 발생시킨다. 

화살표 함수에는 arguments 객체가 존재하지 않는다

 

* 함수 이름 짓는 요령

함수 이름은 동사로 시작하는 것이 좋다. 동사 뒤에는 카멜 케이스나 언더바 표기법으로 목적어를 적어주는 것이 좋다. 함수 이름은 최대한 간결하게 짓고, 다른 사람이 봤을때 해당 함수가 무슨 역할을 하는지 알 수 있도록 짓는 것이 좋다.

// 뭔가를 생성하는 함수
create ... 
build ...

// 뭔가를 조회하는 함수
get ...
read ...

// 뭔가를 변경하는 함수
set ...
write ...
update ...

// 뭔가를 검사하는 함수
check ...
validate ...

// 뭔가를 보여주는 함수
show ...
display ...

아래는 위의 동사 목록으로 작성된 함수이름의 예시이다. 

// 뭔가를 생성하는 함수
createData
buildData

// 뭔가를 조회하는 함수
getData
readData

// 뭔가를 변경하는 함수
setData
writeData
updateData

// 뭔가를 검사하는 함수
checkData
validateData

// 뭔가를 보여주는 함수
showData
displayData

함수는 함수이름에 명시된 대로 정확히 하나의 기능을 수행하는 것이 좋다. 아래 subtract 함수는 인자로 주어진 값을 이용하여 뺄셈만 수행해야 하지만, 인자로 주어진 값을 비교하는 기능도 포함되어 있다. 이러한 경우에는 비교함수를 따로 만들어주는 것이 좋다. 

function subtract(a, b){
    if(a > b) return a - b
    else return b - a
}

아래 compare 함수는 인자로 주어진 두 개의 값을 비교하는 기능만 수행한다. 또한, subtract 함수는 compare 함수의 반환값에 따라 서로 다른 방법으로 뺄셈만 수행한다. 

function compare(a, b){
    if(a > b) return true
    else return false
}

function subtract(a, b){
    return compare(a, b) ?  (a - b) : (b - a)
}

 

* 함수의 파라미터 기본값 설정하기 (.feat 타입체크)

function add(a, b){
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // NaN
console.log(add()) // NaN

위 코드의 add 함수는 두 개의 인자가 모두 설정된 경우에만 덧셈을 수행한다. 만약 인자가 하나만 주어지거나 인자가 없는 경우에는 NaN(Not a Number)를 출력한다. 이러한 경우에는 함수 파라미터에 기본값을 설정하여 예상하지 못한 코드 실행을 미연에 방지할 수 있다. 

function add(a, b){
    if(typeof a === 'undefined') a = 0
    if(typeof b === 'undefined') b = 0
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0

함수 파라미터가 주어지지 않으면 자바스크립트에서는 해당 파라미터를 undefined 로 설정한다. 그러므로 두번째와 세번째 케이스가 제대로 동작하기 위해서는 add 함수의 파라미터 a, b 각각이 undefined 인 경우에 기본값을 0 으로 정해주는 것이다. 이렇게 하면 예상한 대로 동작한다. 

function add(a, b){
    a = typeof a !== 'undefined' ? a : 0
    b = typeof b !== 'undefined' ? b : 0
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0

직전 코드와 정확히 동일한 동작을 삼항연산자로 다시 작성하면 위와 같다. 

function add(a, b){
    a = a || 0
    b = b || 0
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0

위 코드는 OR 연산자(||)를 사용하여 함수의 파라미터 기본값을 설정한다. a, b 각각의 값이 truthy 값이면 인자로 주어진 값으로 설정되고, falsy 값이면 기본값을 0 으로 설정한다. 이전 코드와 정확히 동일하게 동작하지는 않는다.

function add(a, b){
    if(typeof a === 'undefined') a = 0
    if(typeof b === 'undefined') b = 0
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', null)) // null

네번째 케이스를 보면 add 함수의 인자로 빈 문자열('')과 null 값이 주어진다. 이 경우에 빈 문자열과 null 값은 undefined 와 동일하지 않으므로 기본값이 0 으로 설정되지 않고 그대로 빈 문자열과 null 값을 더하게 된다. 

function add(a, b){
    a = a || 0
    b = b || 0
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', null)) // 0

하지만 OR 연산자로 기본값을 설정한 경우 네번째 케이스로 주어진 인자인 빈 문자열과 null 값은 falsy 값이므로 기본값을 0 으로 설정한다. 그래서 add 함수는 예상한 대로 동작한다. 

function add(a = 0, b = 0){
    return a + b
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', null)) // null

자바스크립트 ES5 에서는 위와 같이 조금 더 쉽게 함수 파라미터의 기본값을 설정할 수 있다. a, b 각각의 값이 undefined 인 경우 기본값 0 으로 설정한다. 하지만 네번째 케이스의 인자로 주어진 빈 문자열과 null 값은 undefined 가 아니므로 기본값 0 으로 설정되지 않고 않고 그대로 빈 문자열과 null 값을 더한다. 

function add(a = 0, b = 0, c = a + b){
    return a + b + c
}

console.log(add(3, 5, 7)) // 15
console.log(add(3, 2)) // 10
console.log(add()) // 0
console.log(add('', null)) // nullnull

ES5 에서는 기본값을 설정할때 앞쪽(왼쪽)에 설정된 파라미터 값을 뒤쪽(오른쪽)에 설정된 파라미터 기본값으로 사용할 수 있다. 위 코드에서는 add 함수의 파라미터 a, b 에 설정된 값을 c 의 기본값으로 설정하고 있다. 즉, c 의 값이 주어지면 일반적인 덧셈이 되고, c 의 값이 주어지지 않으면 a + b 의 두배를 계산한다. 네번째 케이스는 빈 문자열과 null 값을 더해서 null 값의 타입이 문자열로 변경된다. 그런 다음 다시 null 을 더하므로 문자열 연결이 된다. 

function isValid(a, b){
    return a > b
}

function add(a = 'apple', b = false, c = a + b, d = isValid(a, b), e = 3, f = {name: 'sun', age: 23}, g = [3, 4, 5]){
    return a + b + c + d + e + f + g
}

console.log(add(3, 5, 7, 9, 11, 13, 15)) 
console.log(add(3, 2)) 
console.log(add()) 
console.log(add('', null))

ES5 에서 함수 파라미터 기본값을 설정할때 다양한 타입의 값이 설정될 수 있다. 문자열, 숫자, 논리형, 객체, 배열, 함수 등이다. 출력 결과는 아래와 같다.

함수 기본값 설정 후 테스트 케이스 출력 결과

 

function add(a, b){
    a = a ?? 0
    b = b ?? 0
    return a + b 
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', null)) // 0

함수 파라미터 기본값을 설정하는 마지막 방법은 널 병합 연산자(Nullish coalescing operator)를 이용하는 것이다. 이는 a, b 각각의 값이 null 또는 undefined 인 경우 기본값 0 으로 설정한다. 네번째 케이스는 null 값은 0 으로 변경되지만, 빈 문자열은 0 으로 변경되지 않으므로 빈 문자열과 0 이 더해져서 문자열 0 이 된다. 

function add(a, b){
    a = a || 0
    b = b || 0
    return a + b 
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', '')) // 0

OR 연산자로 기본값을 설정한 경우 null 과 undefined 이외에 falsy 값(빈 문자열 포함)도 기본값으로 설정된다. 네번째 케이스는 빈 문자열이 falsy 값이므로 모두 기본값 0 으로 설정되어 더해진다. 

function add(a, b){
    a = a ?? 0
    b = b ?? 0
    return a + b 
}

console.log(add(3, 5)) // 8
console.log(add(3)) // 3
console.log(add()) // 0
console.log(add('', '')) // 빈 문자열

하지만 널 병합 연산자(Nullish coalescing operator)로 기본값을 설정한 경우 네번째 케이스의 빈 문자열은 null 이나 undefined 가 아니므로 기본값 0 으로 설정되지 않고 빈 문자열이 더해진다. 

 

* 함수 파라미터 유효성 검증하기 (.feat 타입체크)

// 배열 복사하기
function copyArr(arr){
    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr()
console.log(copiedArr)

위 코드는 copyArr 함수의 인자로 아무것도 전달하지 않는다. 이렇게 되면 copyArr 함수의 파라미터 arr 는 undefined 로 설정된다. arr 는 배열이 아니기 때문에 반복문을 사용하려고 하면 아래와 같은 에러를 발생시킨다. 

copyArr 함수의 기본값을 설정하지 않은 경우

// 배열 복사하기
function copyArr(arr){
    if(!Array.isArray(arr)) return // 유효성 검증하기

    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr = copyArr()
console.log(copiedArr)

위 코드는 copyArr 함수의 인자로 아무것도 전달하지 않더라도 에러는 발생하지 않는다. 함수 실행 초반에 인자로 전달된 arr 값의 타입을 검사해서 배열이 아니면 더이상의 함수 블럭은 실행하지 않는다. 

// 배열 복사하기
function copyArr(arr){
    if(!Array.isArray(arr)) return

    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

// 다양한 타입으로 함수 테스트 하기
const copiedArr1 = copyArr(3)
const copiedArr2 = copyArr('apple')
const copiedArr3 = copyArr(false)
const copiedArr4 = copyArr([])
const copiedArr5 = copyArr({})
const copiedArr6 = copyArr(null)

console.log(copiedArr1)
console.log(copiedArr2)
console.log(copiedArr3)
console.log(copiedArr4)
console.log(copiedArr5)
console.log(copiedArr6)

함수의 인자로 다양한 타입의 데이터를 주입하여 함수 테스트를 진행하였다. 아래 결과는 배열을 제외한 나머지 타입은 데이터 유효성 검증을 통과하지 못하므로 배열 복사가 수행되지 않고 곧바로 undefined 를 반환한다. 

copyArr 함수의 파라미터 유효성 테스트

 

// 배열 복사하기
function copyArr(arr){
    if(!Array.isArray(arr)) return
    if(arr.length === 0) return // 빈 배열이면 배열복사는 수행하지 않음

    const copiedArr = []

    for(let element of arr){
        copiedArr.push(element)
    }

    return copiedArr
}

const copiedArr1 = copyArr(3)
const copiedArr2 = copyArr('apple')
const copiedArr3 = copyArr(false)
const copiedArr4 = copyArr([])
const copiedArr5 = copyArr({})
const copiedArr6 = copyArr(null)

console.log(copiedArr1)
console.log(copiedArr2)
console.log(copiedArr3)
console.log(copiedArr4)
console.log(copiedArr5)
console.log(copiedArr6)

위 코드는 배열이더라도 배열길이가 0 (빈 배열) 이면 복사는 수행하지 않도록 한다. 

 

* 나머지 매개변수를 이용한 가변항 함수 만들기

function add(...args){
    console.log(args)
    
    let sum  = 0
    for(let arg of args){
        sum += arg
    }
    return sum
}

console.log(add()) 
console.log(add(1))
console.log(add(1, 2))
console.log(add(1, 2, 3))
console.log(add(1, 2, 3, 4))
console.log(add(1, 2, 3, 4, 5))

나머지 매개변수(...)을 이용하면 인자 갯수에 상관없이 함수에 주어진 모든 인자들을 배열로 처리할 수 있다. 위 코드의 add 함수는 인자가 몇개이든 args 라는 배열로 인자들을 하나로 묶어준다. args 배열을 이용하여 인자들을 모두 더한다.

function sortByOrder(flag, ...args){
    args.sort( (a, b) => flag * (a - b))
    return args
}

console.log(sortByOrder(1, 5, 2, 3, 1, 4)) // 오름차순 정렬
console.log(sortByOrder(-1, 5, 2, 3, 1, 4)) // 내림차순 정렬

나머지 매개변수는 항상 마지막에 위치해야 한다. 위 코드는 첫번째 인자는 flag 에 복사되고, 나머지 인자들은 args 배열로 묶어준다. 즉, sortByOrder 함수는 flag 가 1 이면 오름차순 정렬로 동작하고, -1 이면 내림차순 정렬로 동작하는 함수이다. 

 

* 함수인자 전개하기

인자가 객체인 경우

function printPersonInfo({ name, age, friends }){
    console.log('name: ', name)
    console.log('age: ', age)
    console.log('friends: ', friends.join(' '))
}

const person = {
    name: 'sunrise',
    age: 21,
    friends: ['victoria', 'smith', 'mark']
}

printPersonInfo(person)

위 코드는 person 객체를 printPersonInfo 함수에 전달할때 구조분해(destructuring assignment)를 사용하여 프로퍼티 각각의 값을 풀어서 복사한다. 주의할 점은 함수 파라미터로 설정한 name, age, friends 변수 (지역변수)는 반드시 인자로 전달된 person 객체의 프로퍼티명과 동일해야 한다. 그렇지 않으면 예상한 대로 동작하지 않는다.

인자가 배열인 경우

function printCircleInfo(x, y, radius){
    console.log('원의 중심: ', x, ',', y)
    console.log('반지름: ', radius)
}

printCircleInfo(...[3, 4, 5])

위 코드는 스프레드 연산자(...)를 이용하여 배열의 각 요소를 전개한 다음 printCircleInfo 함수의 각 파라미터에 복사한다. 배열요소를 함수 각각의 파라미터에 따로 전달해야 하는 경우 유용하게 사용할 수 있다. 

 

* 함수의 반환값

function print(value){
    console.log(value)
}

const printReturned = print(3)
console.log(printReturned)

자바스크립트에서 함수의 반환값은 기본적으로 undefined 이다. 즉, 아무것도 반환하지 않으면 undefined 를 반환한다.

function print(value){
    console.log(value)
    return
}

const printReturned = print(3)
console.log(printReturned)

return 키워드 뒤에 아무런 값도 반환하지 않아도 undefined 를 반환한다. 또한, return 키워드를 만나면 그 즉시 함수는 종료된다.

 

 

* 연습문제 1

아래 코드의 테스트 케이스 출력 결과가 캡쳐화면과 같도록 주어진 인자들 중에서 최대값을 구하는 함수를 완성해보자!

function findMaxValue(...args){
    // 구현하기
}

// 테스트 케이스 
console.log(findMaxValue(-3, 7, -345, 41, 9, 137, 69))
console.log(findMaxValue(-31, 8, null, -26, false, 92, {}, 284, 923, [], "2045.8", 'zip', 54, "1024"))

테스트 케이스 출력 결과

 

* 연습과제 2

다음은 Movie 생성자 함수를 이용하여 영화 정보를 출력하는 코드이다. 하지만 영화 정보는 캡쳐화면과 같이 제대로 출력되지 않는다. getInfo 함수는 제거하지 말고, 영화 정보가 제대로 출력되게 해보자!

function Movie(title, author, release){
    this.title = title
    this.author = author
    this.release = release

    this.printMovieInfo = () => {
        function getInfo(){
            return `${this.title}-${this.author}는 ${this.release}에 발매되었다.`
        }
        console.log(getInfo()) 
    }
}

const movie = new Movie("해리포터", "조앤K롤링", "2003 년 3월 23일")
movie.printMovieInfo()

영화 정보가 제대로 출력되지 않는다

 

* 연습과제 3

아래는 피타고라스 정리를 이용하여 두 점 사이의 거리를 계산하는 코드의 일부이다. getDistance 함수를 구현해보자! 주의할 점은 함수의 인자로 하나의 점만 주어진 경우 원점(0, 0)과의 거리를 계산해야 한다. 또한, 자바스크립트에서 제곱근(루트)를 구하는 방법은 Math 객체의 sqrt 메서드를 이용하는 것이다. 

// getDistance 함수 구현하기

 
 // 테스트 케이스
 console.log(getDistance({x: 3, y: 2}, {x: 8, y: 14}))
 console.log(getDistance({x: 3, y: 4}))

테스트 케이스 출력 결과

 

* 연습과제 4

아래는 첫번째 인자로 주어진 키워드(cat)이 나머지 인자들에서 몇번 반복되는지 알아내는 함수이다. countDuplication 함수를 구현해보자! 아래 테스트 케이스 결과는 4이다. 즉, cat 이 4번 반복된다. 

// countDuplication 함수 구현하기 


// 테스트 케이스 
console.log(countDuplication('cat', 'apple', 'cat', 'tiger', 'cat', 'water', 'computer', 'cat', 'lion', 'pear', 'cat')) // 4

 

* 연습과제 5

아래는 add 함수의 일부이다. add 함수는 주어진 인자가 숫자(문자열로 된 숫자 포함)인 경우에 더한다. 아래 테스트 케이스 결과가 75.9 가 되도록 add 함수를 완성해보자! 

function add(...args){
    console.log(args)
    
    // 구현하기
}

// 테스트 케이스
console.log(add(3, null, 19, false, '9', [], 7, {}, '', 34, 'earth', '3.9')) // 75.9

힌트는 isNaN 함수를 이용하면 된다. isNaN 함수는 인자로 주어진 값이 NaN(Not a Number)인지 판별한다. 

isNaN(인자)

 

* 연습과제 6

아래는 첫번째 인자로 주어진 값으로 나머지 인자들을 나누는 함수이다. 단, 첫번째 인자로 주어진 값이 0 이면 분모가 0 이 되므로 나눗셈은 불가능하다. 그러므로 이 경우에는 캡쳐화면처럼 나머지 인자들을 그냥 배열로 반환한다. 앞에서 설명한 divider 함수를 구현해보자!

// divider 함수 구현하기


// 테스트 케이스 
console.log(divider(2, 39, 4, 7, 28, 62, 28))
console.log(divider(0, 39, 4, 7, 28, 62, 28))

테스트 케이스 출력 결과

 

* 연습과제 7

아래 코드는 배열요소를 뒤섞는 shuffle 함수의 일부분이다. shuffle 함수는 배열요소를 순차적으로 조회하면서 배열에서 랜덤으로 뽑은 요소와 위치를 맞바꾼다. 예를 들어 현재 조회중인 요소가 1번 인덱스 값이고, 랜덤으로 뽑은 요소가 4번 인덱스 값이면 두 값의 위치를 맞바꾸는 식이다. shuffle 함수를 완성해보자!

const numbers = [121, 23, 345, 43, 59]

function pickIndex(len){
    return Math.floor(Math.random() * len)
}
function shuffle(arr){
    // 구현하기
}

console.log(shuffle(numbers))
728x90