JavaScript 함수 - 선언, 호이스팅, 표현식, arguments, call stack, arrow function

1. 함수의 선언

 

함수는 여러 개의 인자를 받아서, 그 결과를 출력한다. 자바스크립트는 파라미터의 개수와 인자의 개수가 일치하지 않아도 오류가 나지 않는다. 만약 , 파라미터 1개가 정의된 함수를 부를 때, 인자의 개수를 0개만 넣어 실행하면, 이미 정의된 파라미터(매개변수)는 undefined 값을 갖게 된다. 이는 변수는 초기화됐지만, 값이 할당되지 않았기 때문이다.

 

// 함수선언문
function printName(firstname) {
    var myname = "jisu";
    return myname + " " +  firstname;
}

// 결과
jisu undefined

 

 

2. 함수 표현식

 

함수는 다음과 같이도 표현할 수도 있다.

 

// 함수표현식
function test() { 
    console.log(printName()); 
    var printName = function() {
        return 'anonymouse';
    }
}
test(); // 타입에러가 발생한다.

 

함수선언문과 달리 선언과 호출 순서에 따라서 정상적으로 실행되지 않을 수 있다. 표현식보단 함수 선언문을 더 자주 사용하지만, 어떤 코딩 컨벤션에서는 표현식을 권장하기도 한다.

 

 

 

3. 표현식과 호이스팅

 

위에 표현식 예제에서 printName이 "printName이 is not defined"이라고 오류가 나오지 않고, function이 아니라고 나온 이유는 printName이 실행되는 순간 'undefined'으로 지정됐기 때문이다.

자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 미리 다 모아서 선언한다.  함수 안에 있는 변수들을 모두 끌어올려서 선언한다고 해서, 이를 hoisting이라고 한다.
(실제로 코드가 끌어올려지는 게 아니라, 자바스크립트 파서 내부적으로 그렇게 끌어올려서 처리한다는 것이다)
따라서 다음 코드 역시 함수를 값으로 가지지만 어쨌든 printName도 변수이므로 끌어올려지고, 값이 할당되기 전에 실행됐으므로 undefined가 할당된 상태이다.

printName(); //아직, printName이 undefined으로 할당된 상태다.  
var printName = function(){}

 

 

 

4. 반환값과 undefined

 

다음 함수의 반환값은 undefined이다.

 

function printName(firstname) {
    var myname = "jisu";
    var result = myname + " " +  firstname;
}

 

자바스크립트 함수는 반드시 return 값이 존재하며, 없을 때는 기본 반환값인 'undefined'가 반환된다.
자바스크립트에서는 다른 언어에서의 void타입이 없다.

 

 

5. arguments객체

 

함수가 실행되면 그 안에는 arguments라는 특별한 지역변수가 자동으로 생성된다.
arguments의 타입은 객체이다. 자바스크립트 함수는 선언한 파라미터보다 더 많은 인자를 보낼 수도 있다.
이때 넘어온 인자를 arguments로 배열의 형태로 하나씩 접근할 수 있다. arguments는 배열이 아니다.
따라서 배열의 메서드를 사용할 수가 없다. 

 

function a() {
 console.log(arguments);
}
a(1,2,3);
// 결과
[Arguments] { '0': 1, '1': 2, '2': 3 }

 

자바스크립트의 가변 인자를 받아서 처리하는 함수를 만들 때 등에서 arguments속성을 유용하게 사용할 수가 있다. 하지만 arguments의 무분별한 사용은 하지 않는 게 좋다.

 

 

6. Arrow function

 

ES2015에는 arrow function이 추가됐다. 하지만 점점 많이 사용되고 있는 syntax이므로 같이 알아두어도 좋을 것 같다.

 

function getName(name) { 
   return "Kim " + name ; 
} 

//위 함수는 아래 arrow함수와 같다. 
var getName = (name) => "Kim " + name;

 

 

7. 함수의 연속적 호출(call stack)

 

여러 함수들(functions)을 호출하는 스크립트에서 해당 위치를 추적하는 인터프리터 (웹 브라우저의 자바스크립트 인터프리터 같은)를 위한 메커니즘이다. 현재 어떤 함수가 동작하고 있는지, 그 함수 내에서 어떤 함수가 동작하는지, 다음에 어떤 함수가 호출되어야 하는지 등을 제어한다.

 

function printing() {
   // [1] Some codes here
   sayHi();
   // [2] Some codes here
}
function sayHi() {
   return "Hi!";
}

printing();

// [3] Some codes here

 

실행 순서

1) printing() 함수 호출

2) 스택 리스트에 printing() 추가

3) [1] 실행, sayHi() 함수 호출

4) 스택리스트에 sayHi() 추가

5) sayHi() 종료 후, 호출된 곳으로 돌아감

6) 스택 리스트에서 sayHi() 제거

7) printing()의 나머지 코드 실행[2]

8) printing() 종료 후, 호출된 곳으로 돌아감

9) 스택 리스트에서 printing() 제거

10) [3] 코드 실행

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 풀이>

function solution(bridge_length, weight, truck_weights) {
   var answer = 0;
   var queue = []; // 현재 다리 위 트럭 상태
   var que_weight = 0; // 현재 다리 위 무게

   while (truck_weights.length != 0) {
      answer += 1;

      if (queue.length === bridge_length) { // 다리 길이 만큼 요소가 찼다면, 마지막 요소 빼냄 
         que_weight -= queue.pop();
      }

      if (que_weight + truck_weights[0] <= weight) { // 다리 무게가 여유 있을 때
         var next_tk = truck_weights.shift();
         queue.unshift(next_tk); //앞쪽 트럭을 다리에 올림
         que_weight += next_tk; // 다리 위 트럭 무게 더해줌 
      } else {
         queue.unshift(0); // 무게 여유가 없다면 0을 채워 넣음.   
      }

      if (truck_weights.length === 0) { // 마지막 트럭이 들어갔을 때
         answer += bridge_length;      // 초를 다리 길이만큼 더해주고 종료!   
         break;
      }
   }
   return answer;
}

arr.shift()

배열에서 첫 번째 요소를 제거하고, 제거된 요소를 반환한다. 빈 배열의 경우 undefined를 반환한다.

 

arr.unshift()

새로운 요소를 배열의 맨 앞쪽에 추가하고, 새로운 길이를 반환한다.

반환 값으로는 새 요소가 추가된 배열의 길이 값을 반환한다.

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 풀이>

function solution(answers) {
    var answer = [];
    var one = [1,2,3,4,5];
    var two = [2,1,2,3,2,4,2,5];
    var three = [3,3,1,1,2,2,4,4,5,5];
    var cnt = [0,0,0];
    
    for(var i=0; i < answers.length; i++){
        if(one[i % one.length] === answers[i]){
            cnt[0] += 1;
        }
        if(two[i % two.length] === answers[i]){
            cnt[1] += 1;
        }
        if(three[i % three.length] === answers[i]){
            cnt[2] += 1;
        }
    }
    
    cnt.forEach((element, idx) =>{
        if(element === Math.max(...cnt)){
            answer.push(idx + 1);
        } 
    });
 
    return answer;
}

 

1. answers배열 요소만큼 반복할 동안, one, two, three 배열 요소들을 반복적으로 돌면서 answers 요소와 비교한다.

2. 요소가 같다면 카운팅을 1씩 증가한다.

3. forEach를 통해 배열 요소별로 콜백 함수를 실행한다.

4. cnt배열에서 최대값인지 판단해, 맞다면 answer 배열에 푸시한다.

 

 

<다른 풀이>

function solution(answers) {
    var answer = [];
    var a1 = [1, 2, 3, 4, 5];
    var a2 = [2, 1, 2, 3, 2, 4, 2, 5]
    var a3 = [ 3, 3, 1, 1, 2, 2, 4, 4, 5, 5];

    var a1c = answers.filter((a,i)=> a === a1[i%a1.length]).length;
    var a2c = answers.filter((a,i)=> a === a2[i%a2.length]).length;
    var a3c = answers.filter((a,i)=> a === a3[i%a3.length]).length;
    var max = Math.max(a1c,a2c,a3c);

    if (a1c === max) {answer.push(1)};
    if (a2c === max) {answer.push(2)};
    if (a3c === max) {answer.push(3)};


    return answer;
}

 

array.filter(callbackFunc(처리할 현재 요소, [처리할 현재 요소의 인덱스], [filter를 호출한 배열]))

: 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다.

인자로 들어간 콜백함수는 true를 반환하면 요소를 유지하고, false를 반환하면 버린다.

 

분석

1. filter(a, i) 인자로 처리할 요소와 해당 요소 인덱스를 담을 인자를 넣었다.

2. 현재 처리할 요소와 a1, a2, a3 배열의 각 요소가 같다면, 요소(a)를 유지하고 아니면 버린다.

3. 필터링 된 요소들의 수를 각각 a1c, a2c, a3c에 담는다.

4. Math.max()를 이용해 최댓값을 구한다.

5. 각 최대값인지 따져서 answer배열에 푸시한다. 여기서 전부 다 최댓값으로 같다면 오름차순으로 정렬해야 하기에 if문을 1부터 3까지 차례대로 실행되게 해 따로 정렬할 필요를 줄였다.

 

이 문제를 통해 filter함수, Math.max(), 화살표 함수 사용에 공부하게 됐다.

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 풀이>

function solution(clothes) {
    var answer = 1;
    var obj = {};
    
    for(var i=0; i < clothes.length; i++){
        if(obj[clothes[i][1]] >= 1){
            obj[clothes[i][1]] += 1; // 객체에 이미 같은 키값이 있다면 +1을 해준다.
        }else{
            obj[clothes[i][1]] = 1; // 새로운 종류의 옷(키값)이라면 1로 만든다.
        }
    }
    for (var key in obj){
        answer *= (obj[key]+1); // 각 옷 종류마다 안입는 경우를 포함
    }
    return answer - 1; // 전체 경우의 수에서 모두 안입는 경우 제외
}

 

이 문제에서 각 옷 종류마다 안 입는 경우가 있기에, +1씩 한 값들을 곱해주면 모든 경우의 수가 만들어진다.

여기서 answer-1을 하는 이유는 문제에서 옷을 무조건 하나는 입고 있다고 나오기 때문에, 모든 경우의 수에서 아무것도 입지 않는 경우의 수 1을 빼주는 것이다.

 

 

<다른 풀이>

function solution(clothes) {
    return Object.values(clothes.reduce((obj, t)=> {
        obj[t[1]] = obj[t[1]] ? obj[t[1]] + 1 : 1;
        return obj;
    } , {})).reduce((a,b)=> a*(b+1), 1)-1;    
} // reduce()의 초기값으로 빈 객체 {}를 설정해줌.

해석:

  1. clothes.reduce(function,{}) 최초 값을 빈 객체로 생성하여 clothes를 순차적으로 키값(t [1]) 검색

  2. undefined는 논리연산에서 false이므로 해당 키의 값이 undefined이면 해당 키값에 1을 넣어줌.

    키의 값이 존재한다면 해당 값에 +1을 해줌.

  3. 이렇게 생성된 Key와 Value를 Object.values를 통해 값만 불러오도록 함.

  4. reduce(function,1) 최초 값을 1로 설정하고 배열을 순차적으로 돌며 이전 값에 (해당 옷 종류의 개수)+1(옷을 입지 않은 경우)를 곱함.

  5. 모든 옷을 입지 않은 경우 제외 -1 후 return.

 

 

reduce() 함수

 

사용법 : arr.reduce(callback[, initialValue])

 

reduce()는 빈 요소를 제외하고 배열 내에 존재하는 각 요소에 대해 callback 함수를 한 번씩 실행하는데, 콜백 함수는 다음의 네 인수를 받는다.

  • accumulator

  • currentValue

  • currentIndex

  • array

만약 reduce() 함수 호출에서 initialValue를 제공한 경우, accumulatorinitialValue와 같고 currentValue는 배열의 첫 번째 값과 같다. initialValue를 제공하지 않았다면, accumulator는 배열의 첫 번째 값과 같고 currentValue는 두 번째와 같다.

 

<출처>

 

Array.prototype.reduce()

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.

developer.mozilla.org

 

<reduce 작동 방식 : 초기값을 설정 하지 않은 경우>

 

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
	return accumulator + currentValue; 
});

// 화살표 함수로 표현
[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );

콜백은 4번 호출된다. 각 호출의 인수와 반환값은 다음과 같다.

callback

accumulator

currentValue

currentIndex

array

반환 값

1번째 호출

0

1

1

[0, 1, 2, 3, 4]

1

2번째 호출

1

2

2

[0, 1, 2, 3, 4]

3

3번째 호출

3

3

3

[0, 1, 2, 3, 4]

6

4번째 호출

6

4

4

[0, 1, 2, 3, 4]

10

reduce()가 반환하는 값으로는 마지막 콜백 호출의 반환 값(10)을 사용한다.

 

 

<reduce 작동 방식 : 초기값을 설정 해주는 경우>

 

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
}, 10);

 

callback

accumulator

currentValue

currentIndex

array

반환 값

1번째 호출

10

0

0

[0, 1, 2, 3, 4]

10

2번째 호출

10

1

1

[0, 1, 2, 3, 4]

11

3번째 호출

11

2

2

[0, 1, 2, 3, 4]

13

4번째 호출

13

3

3

[0, 1, 2, 3, 4]

16

5번째 호출

16

4

4

[0, 1, 2, 3, 4]

20

reduce()가 결과로 반환하는 값은 20이다.

같은 배열에 대한 reduce() 사용이지만 초기값 설정 유무에 따라서 콜백 함수의 호출 수가 달라지고,

또 현재 값과 현재 값의 인덱스가 달라진다.

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 풀이>

function solution(numbers) {
    
    var answer = numbers.sort((a,b) => (b.toString() + a.toString())-(a.toString() + b.toString())).join('');
    return answer[0] === '0' ? '0': answer;
}

 

reuturn 값으로 numbers 배열이 0으로만 구성되어 있을 경우 '0'만 출력한다. 여기서 answer[0]으로 준 이유는

값이 모두 '문자열'이기에 내가 원하는 '0'이 아닌 '0000'과 같은 경우가 나와 '0'으로 나오게 하고자 이런 방식을 취했다.

 

arr.sort([compareFunction])

: 인자로 정렬 순서를 정의하는 함수를 넣는다. 생략하면 배열은 각 요소의 문자열 변환에 따라 각 문자의 유니 코드 코드 포인트 값에 따라 정렬된다.

반환값으로 새로운 배열 값을 만드는 것이 아닌, 원 배열의 정렬된 배열 값이 나온다.

 

  • compareFunction(a, b)이 0보다 작은 경우 a를 b보다 낮은 색인(인덱스)으로 정렬한다.. 즉, a가 먼저 온다.

  • compareFunction(a, b)이 0을 반환하면 a와 b를 서로에 대해 변경하지 않고 모든 다른 요소에 대해 정렬한다.

  • compareFunction(a, b)이 0보다 큰 경우, b를 a보다 낮은 인덱스로 정렬한다.

  • compareFunction(a, b)은 요소 a와 b의 특정 쌍이 두 개의 인수로 주어질 때 항상 동일한 값을 반환해야 한다. 일치하지 않는 결과가 반환되면 정렬 순서는 정의되지 않는다.

 

<다른 풀이>

function solution(numbers) {
    
    var answer = numbers.map(c=> c + '').
    				sort((a,b) => (b+a) - (a+b)).join('');
    
    return answer[0]==='0'? '0' : answer;
}

 

numbers.map(c => c + ' ')

: 먼저 numbers 배열의 요소들을 문자열로 바꾼다. 여기서 map() 함수는 배열 내의 모든 요소 각각에 대하여 인자로 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환해준다. 따라서 내 풀이에서 toString()을 일일이 해줄 필요가 없다.

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 풀이>

function solution(array, commands) {
    var answer = [];

    commands.forEach(function(element){
        
        var sliceArr = array.slice(element[0] - 1, element[1]);
        sliceArr.sort((a,b)=>{return a-b});
        
        answer.push(sliceArr[element[2] - 1]);

    });
    
    return answer;
}

 

forEach(callback함수) : 인자로 callback 함수를 넣는다. 인자로 주어진 함수를 배열 요소 각각에 대해 실행하고, undifined를 반환한다.

 

sort(compareFunction) : 인자로 정렬 순서를 정의하는 함수를 넣는다. 생략하면 배열은 각 요소의 문자열 변환에 따라 각 문자의 유니 코드 코드 포인트 값에 따라 정렬된다. 반환값으로 복사본이 만들어지는 것이 아닌, 원래 배열의 정렬된 배열이  반환값이다.

numbers.sort(function(a, b) {
  return a - b;
});
// 오름차순으로 정렬시킨다.

 

slice(begin, end) : 배열을 인덱스 begin 부터 end 앞까지 ,즉 인덱스 end-1까지 잘라낸다. 반환값으로 잘라내 추출한 배열을 반환한다.

반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<내 풀이>

function solution(heights) {
    var answer = []

    for(var i = heights.length - 1; i > 0; i--){
        for(var j = i - 1; j>=0; j--){
            if(heights[i] < heights[j]){
                answer.unshift(j+1);
                break;
            }else if(j === 0){
                answer.unshift(0);
            }
        }
        
    }
    answer.unshift(0); 
    return answer;
}

 

<다른 풀이>

function solution(heights) {
    return heights.map((v, i) => {
        while (i) {
            i--;
            if (heights[i] > v) {
                return i + 1;
            }
        }
        return 0;
    });
}

 

<공부>

 

map() : 인자로 주어진 함수(여기선 화살표 함수)에 모든 각각의 요소를 적용한 값으로 이루어진 배열을 반환합니다

Arrow function(화살표 함수) : 화살표 함수는 항상 익명이다. 메서드 함수가 아닌 곳에서 적합하며, 그래서 생성자로 사용을 못 한다.

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

// {} 블럭이 없는 경우(한 줄로 표현이 되는 경우)
var add = (a,b) => a+b;

// {} 블럭이 필요한 경우 --> 반드시 return 키워드를 사용해야함
var something = (a,b) => {
	//do something more
    return a * b;
};
반응형

 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

<내 코드>

def solution(heights):
    answer = []
    re_ans = []
    re_heights = list(reversed(heights))
    pk = 0
    
    while(pk != len(heights)):
        tmp = re_heights[pk]
        if(pk == len(heights)-1):
            re_ans.append(0)
            break
        
        for i in range(pk + 1, len(heights)):
            if(tmp < re_heights[i]):
                re_ans.append(len(heights) - i)
                break
            else:
                if(i == len(heights)-1):
                    re_ans.append(0)
        pk += 1
        
    answer = list(reversed(re_ans))
    return answer

reverse() 함수는 단순히 해당 리스트의 값을 뒤집고, 어떤 값을 리턴하지 않는다.

reversed는 리스트의 함수가 아닌 파이썬 자체의 내장 함수로, 리턴으로 객체를 보내기에 리스트로 값을 얻기 위해 list() 함수를 사용했다.

 

 

<다른 풀이>

def solution(h):
    ans = [0] * len(h)
    for i in range(len(h)-1, 0, -1):
        for j in range(i-1, -1, -1):
            if h[i] < h[j]:
                ans[i] = j+1
                break
    return ans

진짜 이렇게 간단하게 해결을 할 수 있다니...정말..경이롭다...

여기서 range를 시작, 끝, 스텝을 이용해 -1씩 감소하게 해 reverse, reversed 없이 가능한 걸 알게 됐다.

 

 

 

반응형

+ Recent posts