다연이네

[days06] 클로저(Closure) (중요! 취업질문 多) 본문

Web/JavaScript

[days06] 클로저(Closure) (중요! 취업질문 多)

 다연  2020. 12. 16. 19:07
반응형

클로저 == 함수 폐쇄

js는 중첩함수를 지원한다. (함수 안에 함수 만들기)


부모함수가 닫힌 후에도 부모 범위에 접근할 수 있는 함수 + 중첩함수

 

<body>
<button onclick="test()">test</button>
<div id="demo"></div>

<script>
var add  = (function() {
	
	var counter =0; //지역변수 (1 함수가 호출이 끝나면 사라져야 함)
	return function() { //얘가 클로저
		counter++;  //그러나 버튼 누르면 계속 증가함 (2 사라지지 않았다 ? )
		return counter; // 3 기본적인 지역변수는 사라지지만 클로저를 통해 접근한 것
	};
	
})();

function test() {
	document.getElementById("demo").innerHTML = add(); //클로저를 사용한 것
	//클로저 == 중첩함수라고 해도 틀린말은 아니나, + 부모함수가 닫혀서 외부에서 접근을 못할때
	// 부모의 멤버에 접근할 수 있는 함수이다.
}
</script>
</body>

버튼을 누를때마다 카운트가 증가한다. (클로저를 통한 접근)

 

 

 

 

 

<body>
<button onclick="add()">test</button>
<div id="demo"></div>
<script>
var counter = 0;
function add() {
	var counter = 0;
	counter++;
	document.getElementById("demo").innerHTML = counter;
}
</script>
</body>

버튼을 눌러도 출력값이 1에서 변하지 않는다.

0에서 1증가해서 1이 되고 함수 호출 끝나면 지역변수 제거되고, 다시 호출되면 다시 0에서 1되고,.. 
버튼 누를때마다 항상 0->1 되기 때문에 1만 반복되는 것이다.
지역변수는 함수를 호출할때마다 새로 만들어진다.

만약 지역변수도 있고 전역변수도 있다면 ? 그래도 1만 출력된다.
=> 똑같은 변수가 있다면 지역변수가 우선시 된다. (지역변수 카운터 먼저 쓰고 있다)

<body>
<button onclick="test()">test</button>
<div id="demo"></div>
<script>
var n = 4;
function test() {
	var n =3;
	document.getElementById("demo").innerHTML = n*n;
}
</script>
</body>

9 출력, 지역변수가 전역변수보다 우선순위가 높다.

 

 

<body>
<button onclick="add()">test</button>
<div id="demo"></div>
<script>

function add() {
	var counter = 0; //지역변수
	
	function plus() { //이 내부 함수에서 외부 함수의 변수에 접근 가능
		counter++;
	}
	
	plus();
	return counter;
}

document.getElementById("demo").innerHTML =add(); //1 출력 
 
</script>
</body>
<script>
//자체 함수 호출
var add  = (function() {
		var counter =0;
		return function() {
			counter++;
			return counter;
		};
	})();
//document.getElementById("demo").innerHTML =add(); //함수 자체 return (리턴값이 함수 자체니까) 
document.getElementById("demo").innerHTML =add(); 
</script>

위 둘처럼 코딩하면 1만 출력된다...

 

 

클로저 사용 예시 1

클릭하면 글자 크기 변하게 하기

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<style>
body{
	font-size: 12px;
}
h1{
	font-size: 1.5em;
}
h2{
	font-size: 1.2em;
}
</style>

</head>
<body>
<p>Lorem ipsum dolor sit amet.</p>
<h1>Lorem ipsum dolor sit amet.</h1>
<h2>Lorem ipsum dolor sit amet.</h2>

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

<script>
	function makeSize(size) {
		//size 지역변수
		//var counter = 0;
		return function() {
			document.body.style.fontSize= size+"px";
		};
	}
	
	var size12 = makeSize(12);
	var size14 = makeSize(14);
	var size16 = makeSize(16);
	
	document.getElementById("size-12").onclick = size12;
	document.getElementById("size-14").onclick = size14;
	document.getElementById("size-16").onclick = size16;
	
</script>
</body>
</html>

 

 

모듈 패턴 : private 함수와 변수에 접근하는 public 함수를 정의하기 위해 클로저 사용

- java에서는 private 메소드()를 외부에선 접근할 수 없었다.
- js는 private 메소드()가 없기 때문에 -> 클로저(Closure)를 이용해서 private method 처리해야한다.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>

var counter = (function() {
	//부모 함수의 멤버들
	var privateCounter = 0; //이 변수 외부에서 접근 못함 
	function changeBy(val) { //이 함수 외부에서 접근 못함 (클로저 통해 접근해야함)
		privateCounter+=val;
	}
	return {
		increment:function(){
			changeBy(1);
		},
		decrement:function(){
			changeBy(-1);
		},
		value:function(){
			return privateCounter;
		}
		
	};
})();

counter.increment();
counter.increment();
counter.increment();
document.write(counter.value()+"<br>"); //3 value는 속성이 아니라 함수니까 ()
counter.decrement();
document.write(counter.value()+"<br>"); //2 
</script>
</body>
</html>

 

 

클로저 사용 예시2

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<style>
.myProgress{
	width: 100%;
	background: #ddd;
	border: solid 1px gray;
}

.myBar{
	width: 1%;
	height: 30px;
	background: #4CAF50;
	text-align: right;
	color: red;
}
</style>

</head>
<body>
<h3>Progress Bar</h3>

<div id="myprogress1" class="myProgress">
	<div id="myBar1" class="myBar"></div>
	<button id="btn1">진행</button>
</div>
<div id="myprogress2" class="myProgress">
	<div id="myBar2" class="myBar"></div>
	<button id="btn2">진행</button>
</div>
<div id="myprogress3" class="myProgress">
	<div id="myBar3" class="myBar"></div>
	<button id="btn3">진행</button>
</div>
<div id="myprogress4" class="myProgress">
	<div id="myBar4" class="myBar"></div>
	<button id="btn4">진행</button>
</div>
<div id="myprogress5" class="myProgress">
	<div id="myBar5" class="myBar"></div>
	<button id="btn6">진행</button>
</div>
 <!-- 위에 div도 동적으로 만들어보기 -->
<script>
/* 
	var bar = document.getElementById("myBar1");
	document.getElementById("btn1").onclick = progressbar;
	
	var width = 1;
	var timer ;
	function progressbar() {
		if(width<100){
			width++;
			bar.style.width = width+"%";
			bar.innerHTML = width+"%";
			timer = setTimeout(progressbar, 30);
		}else{
			clearTimeout(timer);
		}
		
	} 
	*/
</script>


<script>
//막대그래프가 1개가 아니라 5개면 그 개수만큼 width 변수, timer 변수 필요
//이럴때 클로저 사용
var btns = document.getElementsByTagName("button"); //버튼 다가져오기
var myBars = document.getElementsByClassName("myBar");  //막대바 다가져오기

for (var i = 0; i <btns.length; i++) { //각각 버튼 클릭할때 함수 등록
	btns[i].onclick = function() { //부모함수
		//alert("dd");
		var width = 1;
		var btn = event.srcElement; //클릭한 버튼
		var timer = setInterval(function() { //클로저
			if(width<100){
				width++;
				btn.previousElementSibling.style.width = width+"%"; //jquery: prev()
				btn.previousElementSibling.innerHTML = width+"%";
				
			}else{
				clearInterval(timer);
			}
		}, 30); 
	}
}//클로저를 쓰고 나면 각각 호출할때 자기자신 변수들이 버튼마다 자기꺼 갖고 있다
//-> 코딩이 엄청 편리해짐
//클로저 취업질문 참 많이한다..


</script>
</body>
</html>

 

jQuery로 수정

<script>

$("button").click(function() {	
	var $bar = $(this).prev(); //변수앞에$를 붙히면 항상 jQuery객체가 된다.
	var btn = event.srcElement;
	var width =1;
	var timer = setInterval(function() {
		if(width<100){
			width++;
			$bar.css("width", width+"%");
			$bar.html(width+"%");
			
		}else{
			clearInterval(timer);
		}
	}, 30);
});

</script>

 

반응형
Comments