0%

201130_TIL(스코프, 전역 변수의 문제점)

오늘 한 것

함수

함수 호출문은 언제나 표현식이다. → 언제나 return 값이 있기 때문이다.(return 값이 없어도 자동으로 undefined를 반환한다.

함수 몸체는 함수가 선언됐을 때가 아니라 함수가 실행됐을 때 해석된다.

함수는 정의될 때 상위 스코프를 기억한

스코프

스코프 체인은 단방향 linked list로 구현되어 있다.

양방향이 되면 탐색 시 계속 순회한다. 스코프의 탐색 방향은 하위에서 상위로 올라가며 탐색한다.

대부분의 언어는 렉시컬 스코프 방식으로 작동한다. 함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정된다. 함수 정의가 실행되어 생성된 함수 객체는 이렇게 결정된 상위 스코프를 기억한다. 함수가 호출될 때마다 함수의 상위 스코프를 참조할 필요가 있기 때문이다.

실행 중인 전역스코프, 지역스코프는 스택에 차례로 쌓이며 보관된다. 처음 실행되는 전역스코프부터 시작해서 함수가 실행되면 지역스코프가 스택에 쌓이고 종료되면 스택을 빠져나간다. 이러한 과정에서 중간에 변수를 참조할 경우 해당 스택 최상위 스코프를 보고 어느 스코프부터 검색할지 결정한다.

전역 변수의 문제점

1
2
3
4
5
6
7
8
function foo() {
var x = "local";
console.log(x); // local
return x;
}

foo(); // var x는 함수 호출이 종료되면 죽는다.
console.log(x); // ReferenceError: x is not defined

var x는 함수가 호출됐을 때 생성된다. → 변수 호이스팅은 변수 선언문이 해당 스코프의 최상위로 끌어올려지는 것

참조하는 식별자가 없을 때 가비지 컬렉터의 대상이된다. 종료된 함수의 스코프도 그 대상이다. 하지만 함수는 종료됐는데 함수 내부 변수가 소멸되지 않고 살아있는 경우가 있다. 이것을 클로저(closure)라 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Counter = (function () {
// private 변수
var num = 0;

// 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
return {
// 객체를 반환한다.
increase() {
return ++num;
},
decrease() {
return --num;
},
};
})();

// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined

console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0

num은 즉시 실행함수가 끝나면 소멸되어야하지만 Counter 변수가 즉시실행함수를 상위 스코프로 두고있는 increase(), decrease() 함수를 참조하고있다. 또한 함수들은 num을 참조하고있음으로 num은 소멸되지 않는다.

공부한 javaScript 코딩 문제

1.크레인 인형뽑기 게임

문제 설명
N x N 크기의 정사각 격자이며 위쪽에는 크레인이 있고 오른쪽에는 바구니가 있습니다. 각 격자 칸에는 다양한 인형이 들어 있으며 인형이 없는 칸은 빈칸입니다. 모든 인형은 1 x 1 크기의 격자 한 칸을 차지하며 격자의 가장 아래 칸부터 차곡차곡 쌓여 있습니다. 게임 사용자는 크레인을 좌우로 움직여서 멈춘 위치에서 가장 위에 있는 인형을 집어 올릴 수 있습니다. 집어 올린 인형은 바구니에 쌓이게 되는 데, 이때 바구니의 가장 아래 칸부터 인형이 순서대로 쌓이게 됩니다.
만약 같은 모양의 인형 두 개가 바구니에 연속해서 쌓이게 되면 두 인형은 터뜨려지면서 바구니에서 사라지게 됩니다.
크레인 작동 시 인형이 집어지지 않는 경우는 없으나 만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 또한 바구니는 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정합니다.

게임 화면의 격자의 상태가 담긴 2차원 배열 board와 인형을 집기 위해 크레인을 작동시킨 위치가 담긴 배열 moves가 매개변수로 주어질 때, 크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항
board 배열은 2차원 배열로 크기는 5 x 5 이상 30 x 30 이하입니다.
board의 각 칸에는 0 이상 100 이하인 정수가 담겨있습니다.
0은 빈 칸을 나타냅니다.
1 ~ 100의 각 숫자는 각기 다른 인형의 모양을 의미하며 같은 숫자는 같은 모양의 인형을 나타냅니다.
moves 배열의 크기는 1 이상 1,000 이하입니다.
moves 배열 각 원소들의 값은 1 이상이며 board 배열의 가로 크기 이하인 자연수입니다.

입출력 예
board moves result
[[0,0,0,0,0],[0,0,1,0,3],[0,2,5,0,1],[4,2,4,4,2],[3,5,1,3,1]] [1,5,3,5,1,2,1,4] 4

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function solution(board, moves) {
var answer = 0;
var newBoard = [];
var pick = [];
var test = 0;
var x = 0;

function isZero(value) {
return value !== 0;
}

for (var cnt = 0; cnt < board.length; cnt++) {
newBoard.push([]);
}

for (var i = 0; i < board.length; i++) {
for (var j = 0; j < board.length; j++) {
newBoard[i][j] = board[j][i];
}
}

for (var i = 0; i < newBoard.length; i++) {
newBoard[i] = newBoard[i].filter(isZero);
}

for (var k = 0; k < moves.length; k++) {
if (newBoard[moves[k] - 1][0]) {
test = newBoard[moves[k] - 1].shift();
pick[x] = test;
if (pick[x - 1] === test) {
pick.pop();
pick.pop();
x -= 2;
answer += 2;
}
x++;
}
}
return answer;
}

감탄한 타인의 답안

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function solution(board, moves) {
var count = 0;
var stack = [];

for (var i = 0; i < moves.length; i++) {
var now = moves[i] - 1;
for (var j = 0; j < board.length; j++) {
if (board[j][now] != 0) {
if (stack[stack.length - 1] === board[j][now]) {
stack.pop();
count += 2;
} else {
stack.push(board[j][now]);
}
board[j][now] = 0;
break;
}
}
}
console.log(stack);
return count;
}
  • 멀고도 험하다.

Nyong’s GitHub