Math.random(), Math.floor/ceil/round, forEach와map, for반복문과while반복문사용차이, .sort(오름차순정렬) setTimeout(function (){} , 밀리초)

웹 게임을 만들며 배우는 JS : 로또추첨기

Math.random(), Math.floor/ceil/round, forEach와map, for반복문과while반복문사용차이, .sort(오름차순정렬) setTimeout(function (){} , 밀리초)

1. Math.random()

랜덤한 수를 뽑는 매서드이다.

1
2
3
4
5
6
Math.random() 에서 * n을 하면, n전까지의 숫자가 random으로 나온다.
보기쉽게 소수점첫째자리 버림 하는 함수인 Math.floor()와 함께 주로 사용한다.
예를들어

Math.floor(Math.random()*5)
//=> 0,1,2,3,4 숫자들이 랜덤으로 출력됨.

2. Math.floor()와 Math.ceil()와 Math.round()

세가지의 차이점은

1
2
3
Math.floor() : 소수점 버림. 정수로 반환. 
Math.ceil() : 소수점 올림. 정수로 반환.
Math.round() : 소수점 반올림. 정수로 반환.

3. .forEach(function(){})와 .map(function(){})

STEP1. 로또 숫자 만들기(1~45까지)

1
2
3
4
5
6
7
8
9
10
11
12
var 후보군 = Array(45); 
//empty가 45개 생기고 empty의 특징은 foreach반복문 적용 불가.
var 필 = 후보군.fill();
//필을 해줘야 45개의 emtpy에서 undefined가 된다.empty가 아니기에 forEach반복문 실행가능해짐.

//forEach를 사용해서 억지로 숫자를 넣을 수 있다.
필.forEach(function(요소,인덱스) {
필[인덱스] = 인덱스 + 1;
});

console.log(필);
//=> (45) [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, 41, 42, 43, 44, 45]

더 좋은 방법이 있다. 바로 maping이다.
.map(function(){})은 array 요소와 1대1로 mapping이 된다.

1
2
3
4
5
6
7
8
var 후보군 = Array(45); 
var 필 = 후보군.fill();
var 맵 = 필.map(function(요소, 인덱스){
return 인덱스 + 1
});

console.log(맵);
//=> (45) [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, 41, 42, 43, 44, 45]

굳이 변수를 3개씩 많이 지정할 필요가 없다. 위의 전체를 변수 1개로 그리고 단 세 줄로 나타낼 수 있다.

1
2
3
4
5
var 후보군 = Array(45).fill().map(function(요소, 인덱스){
return 인덱스 + 1
});
console.log(후보군);
//=> (45) [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, 41, 42, 43, 44, 45]

세상 깔~끔!

4. for반복문과 while반복문 사용차이

STEP2. 숫자를 랜덤하게 섞고 총7개 숫자를 뽑기

숫자를 랜덤하게 섞은 뒤 7가지의 숫자를 뽑고자 한다면 흔히 for문을 먼저 생각할 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var 셔플 = [];
for (var i=0; i<후보군.length; i+=1){
var 이동값 = 후보군.splice(Math.floor(Math.random()*후보군.length), 1)[0];
셔플.push(이동값);
}
console.log('---')
console.log('후보군길이: '+후보군.length); //=>22개
console.log('셔플길이: '+셔플.length); //=>23개

// 후보군길이 + 셔플길이 = 45 개 된다.
// 즉, 기존 후보군길이 45개에서 셔플로 23개 빠지고 나머지 22개만 남게된다.

console.log('후보군: '+후보군.sort(function(p, c){ return p-c;}));
console.log('셔플숫자: '+셔플.sort(function(p, c){ return p-c;}));

하지만 for반복문을 못 쓰지 못한다.

후보군길이 + 셔플길이 = 45 개 된다. 즉, 기존 후보군길이 45개에서 셔플로 23개 빠지고 나머지 22개만 남게된다.

후보군.length가 45개에서 22개로 줄어들면 우리가 원하는데로 45개에서 랜덤숫자 7개를 뽑을 수 없다.

지금 23개에서 랜덤숫자 7개 뽑는 꼴이 되버림. 따라서 이럴 때는 while을 쓴다.

1
2
3
4
5
6
7
8
9
10
11
12
13
while (후보군.length>0){
var 이동값 = 후보군.splice(Math.floor(Math.random()*후보군.length), 1)[0];
셔플.push(이동값);
}
console.log('---')
console.log('후보군길이: '+후보군.length); //=> 0개
console.log('셔플길이: '+셔플.length); //=> 45개

// 후보군길이 + 셔플길이 = 45 개 된다.
// 즉, 기존 후보군길이 45개에서 셔플로 45개 빠지고 나머지 0개만 남게된다.

console.log('후보군: '+후보군.sort(function(p, c){ return p-c;}));
console.log('셔플숫자: '+셔플.sort(function(p, c){ return p-c;}));

for반복문은 내가 몇번 반복해야할 지 정확히 알 때 사용하면 좋고,

while반복문은 내가 몇번 반복해야할 지 모를 때, 그리고 기준값이 계속 변경될때 사용하면 좋다.

여기서 기준값은 후보군.length다.

이렇게 뽑은 랜덤한 7개의 숫자는 아직 console.log를 통해 볼 수 밖에 없다.

따라서 화면에 구현하는 방법은 html로 결과창태그를 만든 뒤

js상으로 결과창태그를 불러와서 뽑은 랜덤당첨숫자들이 결과창태그의 자식태그로 들어가면 된다.

1
2
3
<div id='결과창'>
<div>여기에 각 당첨숫자태크가 갯수만큼 자동으로 생겨야함<div> //그렇다면 for문이겠지?
</div>
1
2
3
4
5
6
7
8
9
//1.html상의 결과창태그를 js로 가져오는 방법은 두가지이며 아래 5번에서 자세히 설명함!
var 결과창 = document.getElementById('결과창');

//2.뽑힌 숫자들을 결과창태그의 자식태그로 넣기
for(let i=0; i<당첨숫자들.length; i+=1){
var 공 = document.createElement('div'); //i번만큼의 div태크 만들고
공.textContent = 당첨숫자들[i]; //div태크안에는 뽑힌 당첨숫자들을 하나씩 넣고
결과창.appendChild(공); //결과창태크의 자식태그로 넣는다
}

5. JS에서 html태그를 불러오는 2가지 방법

첫번째, getElementById 와 getElementsByClassName 를 이용하기

두번째, querySelector 와 querySelectorAll를 이용하기(권장)

1
2
3
4
5
6
7
<body>
<div>당첨 숫자는? ㄷㄱㄷㄱㄷㄱ</div>
<div id='결과창'></div>
<div>보너스숫자</div>
<div class='보너스'></div>
<script src = "로또추첨기.js"></script>
</body>

이 태그들 중 결과창과 보너스를 js로 가져오는 방법은

1
2
3
4
5
6
7
//첫번째 방법사용
var 결과창 = document.getElementById('결과창');
var 칸 = document.getElementsByClassName('보너스')[0]; //class의 경우 여러개 사용이 가능해서 꼭 뒤에 [n] 몇번째 클래스인지 적어줘야함

//두번째 방법사용
var 결과창 = document.querySelector('#결과창'); //id일 경우 #
var 칸 = document.querySelector('.보너스'); //class일 경우 .

끝!
이렇게 가지고 와서 원하는 방향으로 사용하면 된다!

6. .sort와 .sort(function(p, c){ return p-c;})

1
2
3
.sort() : 숫자 정렬
예를들어 [2,15,4,7,27].sort();
//=> [15,2,27,4,7] .

뭔가 이상한데?

첫째자리 수를 기준으로 숫자 오름차순이라서 그렇다.

15에 1이 먼저 있기때문에 15가 제일 첫번째로 정렬되었다.

우리가 원하는 오름차순은 아래와 같다. 내림차순은 덤으로 공부!

1
2
3
4
5
6
7
.sort(function(p, c){ return p-c;}) : 숫자오름차순 정렬
예를들어 [2,15,4,7,27].sort(function(p, c){ return p-c;})
//=> [2,4,7,15,27]

.sort(function(p, c){ return c-p;}) : 숫자내름차순 정렬
예를들어 [2,15,4,7,27].sort(function(p, c){ return c-p;})
//=> [27,15,7,4,2]

어떠한 원리로 정렬이 되는 걸까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[25,1,12].sort(function(p, c){ return p-c;})
//=> p-c가 0보다 크면 둘의 정렬순서를 바꿔주는 원리.

p - c
25 - 1 = 24 //=>이건 0보다 크므로 [1,25,12]로 정렬순서가 바뀐다.
// 그리고 바뀐 정렬순서에서 다시 실행.

p - c
25 - 12 = 13 //=>이건 0보다 크므로 [1,12,25]로 정렬순서가 바뀐다.
//그리고 처음부터 다시 실행.

p - c
1 - 12 = -11 //=>이건 0보다 작으므로 순서를 바꾸지 않는다
//그 다음 계속 실행

p - c
12 - 25 = -13 //=>이건 0보다 작으므로 순서를 바꾸지 않는다
//다 돌았으므로 정렬 끝 = [1,12,25]

숫자뿐만 아니라 문자도 비슷한 원리로 정렬이 된다고 한다.

7. setTimeout(function (){} , 밀리초)

STEP3. 화면에 로또숫자를 긴장감있게 나타내기

랜덤 숫자들이 화면에 한꺼번에 보여지는 게 아니라 시간차를 두고 나타나게 하고 싶다면 setTimeout(function (){} , 밀리초) 을 사용하면 된다.

1
2
3
4
5
setTimeout(function(){
var 공 = document.createElement('div');
공.textContent = 당첨숫자들[0]; //for문사용시 클로저문제가 발생하므로 당첨숫자들[0] ~ 당첨숫자들[5]까지 각각 적어준다.
결과창.appendChild(공);
}, 1000); //밀리초라고해서 1000 = 1초임

for문사용시 클로저문제가 발생하므로 당첨숫자들[0] ~ 당첨숫자들[5]까지 각각 적어준다.

나는 쪼랩이라 아직 클로저문제 해결법을 안 알려준다고 한다ㅋㅋㅋㅋ 나중에 중급강의에 알려준다고 하니 그때 해야지.

그럼 어떻게 하느냐? for문을 안 쓰면 된다

당첨숫자들[0] ~ 당첨숫자들[5]까지 각각 적어준다.

만약 클로저를 이용해서 푼다면 아래와 같다

1
2
3
4
5
6
7
8
for(var i=0; i<당첨숫자들.length; i+=1){
function 클로저(j){
setTimeout(function(){
공색칠하기(당첨숫자들[j], 결과창);
},(j+1) * 1000);
}
클로저(i);
}

이걸 더 짧게 줄일 수가 있는데 바로 즉시실행코드를 이용하면 된다

function을 괄호로 감싸면 즉시실행코드가 된다

1
2
3
4
5
6
7
for(var i=0; i<당첨숫자들.length; i+=1){
(function 클로저(j){
setTimeout(function(){
공색칠하기(당첨숫자들[j], 결과창);
},(j+1) * 1000);
})(i);
}