우클릭이벤트실행, target VS currentTarget, arr.filter(function(v){}), arr1.concat(arr2), 스코프(scope)와 스코프체 렉시컬 스코프, 재귀함수, arr.filter((v)=>!!v)

웹 게임을 만들며 배우는 JS : 지뢰찾기

1. 우클릭이벤트 실행 addEventListener(‘contextmenu’, function(){})

우클릭시 이벤트실행은 contextmenu사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//step5. 우클릭하면 깃발모양만들기
td.addEventListener('contextmenu', function(e){
e.preventDefault();
console.log('우클릭');
//우클릭한 곳이 몇번째줄 몇번째칸인지 알아야하니까 currentTarget 사용
var 부모tr = e.currentTarget.parentNode;
var 부모tbody = e.currentTarget.parentNode.parentNode;
//var 칸 = 부모tbody.children.indexOf(tr) 부모tr이 배열이 아니라서 indexOf적용안됨. 부모tbody는 유사배열임
//indexOf를 쓰고 싶은데 배열이 아닌 애들한테 강제로 쓰는 법은 아래와 같이 Array.prototype.indexOf.call()를 사용
var 칸 = Array.prototype.indexOf.call(부모tr.children, e.currentTarget);
var 줄 = Array.prototype.indexOf.call(부모tbody.children, 부모tr);

console.log( 칸, ':', 줄);

e.currentTarget.textContent = '!';
dataset[줄][칸] = '!';
//데이터와 화면일치를 위해 꼭 필요. 이게 불편해서 react를 많이 씀
});




2. event.target VS event.currentTarget

언뜻보면 이벤트가 일어난 곳을 둘 다 알 수 있는 것 같지만, 차이가 있다.

target은 이벤트리스너가 발생하는 대상(내가 클릭한 td)

currentTarget은 이벤트리스너를 단 대상(내가 클릭한 td가 속해있는 테이블 전체)

1
2
3
4
tbody.addEventListener('contextmenu', function(e){
console.log('타켓', e.target);
console.log('커렌트타켓', e.currentTarget);
});




3. arr.filter(function(v){})

배열에서 v인것을 필터링해주는 함수이다

1
2
3
4
5
6
7
8
9
//step6. 클릭했을때 주변 지뢰 개수 세기
e.currentTarget.textContent =
[
dataset[줄-1][칸-1], dataset[줄-1][칸], dataset[줄-1][칸+1],
dataset[줄][칸-1], dataset[줄][칸+1],
dataset[줄+1][칸-1], dataset[줄+1][칸], dataset[줄+1][칸+1]
].filter(function(v){
return v === 'X' //=> v가 'X'인 배열을 필터링함
}).length;

가장자리는 배열이 마이너스가 나기때문에 오류가 난다.

해결하는 방법은 아래 4번의 concat을 이용해서 배열을 다시 만들어주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
td.addEventListener('click', function(e){
var 부모tr = e.currentTarget.parentNode;
var 부모tbody = e.currentTarget.parentNode.parentNode;
var 칸 = Array.prototype.indexOf.call(부모tr.children, e.currentTarget);
var 줄 = Array.prototype.indexOf.call(부모tbody.children, 부모tr);
if(dataset[줄][칸] === 'X'){
e.currentTarget.textContent = '펑!';
}else{
var 주변 = [dataset[줄][칸-1], dataset[줄][칸+1] ];
if(dataset[줄-1]){
주변 = 주변.concat(dataset[줄-1][칸-1], dataset[줄-1][칸], dataset[줄-1][칸+1]);
}
if(dataset[줄+1]){
주변 = 주변.concat(dataset[줄+1][칸-1], dataset[줄+1][칸], dataset[줄+1][칸+1]);
}
e.currentTarget.textContent = 주변.filter(function(v){ //배열에서 필터링하는 함수
return v === 'X'
}).length;
}
})




4. arr1.concat(arr2)

배열과 배열을 합칠때 push말고 concat이 유용하다

MDN : concat

1
2
3
4
5
6
var 주변 = [ dataset[줄][칸-1], dataset[줄][칸+1] ];
if(dataset[줄-1]){
주변.push(dataset[줄-1][칸-1]);
주변.push(dataset[줄-1][칸]);
주변.push(dataset[줄-1][칸+1]);
}

동일하게 concat으로 나타내면

1
2
3
4
var 주변 = [ dataset[줄][칸-1], dataset[줄][칸+1] ];
if(dataset[줄-1]){
주변 = 주변.concat(dataset[줄-1][칸-1], dataset[줄-1][칸], dataset[줄-1][칸+1]);
}




5. Scope와 Scope Chain과 Lexical Scope

변수는 자신을 감싸고 있는 함수의 바깥으로 빠져나갈 수 없다.

var은 선언한 함수 내부에서만 유효하다 (= function scope)

참조 : 제로초 블로그 - 함수의 범위(scope)




6. 재귀함수

재귀함수란 자신을 불러오는 함수로 반복문 역할로 사용 가능하다

1
2
3
4
function 재귀함수(숫자){
console.log(숫자);
재귀함수(숫자+1);
};

재귀함수(1)을 실행하면

1
2
3
4
5
function 재귀함수(숫자){ //=> 1 > 2 > 3 > 4 ...무한반복
console.log(숫자); //=> 1 > 2 > 3
재귀함수(숫자+1); //=> 2 > 3 > 4
};
재귀함수(1)

무한반복문이 된다.

그걸 방지하기 위해서 if로 숫자 제한을 둔다

1
2
3
4
5
6
function 재귀함수(숫자){
console.log(숫자);
if(숫자<3){
재귀함수(숫자+1)
}
};

재귀함수(1)을 실행하면

1
2
3
4
5
6
7
8
function 재귀함수(숫자){ //=> 1 > 2 > 3
console.log(숫자); //=> 1 > 2 > 3
if(숫자<3){ //=> 1 > 2 > false
재귀함수(숫자+1) //=> 2 > 3
} //=> end
};

재귀함수(1);




7. arr.filter((v)=>!!v)

배열에서 undefined, 0, null, 빈문자열 제거하는 코드로 암기필수이다.

1
2
3
arr.filter(function(v){
return !!v;
});

최신문법으로 나타내면

1
arr.filter((v)=>!!v);

지난번 포스팅에서도 언급했듯이 화살표함수(=>)는 익명함수를 만드는 명령어로 적기귀찮아서 줄여서 쓰는 것이다.