함수지향

Joy·2022년 9월 26일
0

JavaScript

목록 보기
6/15

전역변수와 지역변수

유효범위(Scope)는 변수의 수명을 의미한다.
변수는 유효범위가 존재하는데 전역변수 또는 지역변수로 나뉜다.

1) 전역변수

	var vscope = 'global';
    function fscope(){
    	console.log(vscope);
    }
    fscope();
    // 결과 : 'global'

함수 밖에서 변수를 선언하면 그 변수는 전역변수가 된다.
전역변수는 스크립트 전체에 접근이 가능한 변수이다.
함수 fscope()내에서 vscope을 호출 했을 때
함수 내에서는 해당 변수가 없었기 때문에 거슬러 올라가 밖에서 선언된 vscope의 값 global이 반환된 것이다.

2) 지역변수

	function fscope(){
    	var vscope = 'local';
        console.log(vscope);
    }
    fscope();
    // 결과 : 'local'

vscope 이라는 변수가 함수 내에서 선언되었다.
이는 작성된 지역에서만 사용될 수 있는 변수로 지역변수가 된다.

	function fscope(){
    	var vscope = 'local';
        console.log(vscope);
    }
    console.log(vscope);
    // 결과 : undefined

지역변수는 작성된 함수 내의 지역에서만 접근이 가능하기 때문에 만약 밖에서
vscope의 값을 호출 한다면 undefined가 뜨게 된다.

자바스크립트의 지역변수는 함수에서만 유효하다.

var

var를 쓰는 것과 쓰지 않는 것의 차이

	var vscope = 'global';
    function fscope(){
    	vscope = 'local';
        console.log(vscope);
    }
    fscope();
    console.log(vscope);

결과는 둘 다 local이 된다.

함수 밖에서도 vscope의 값이 local인 이유는 함수 fscope의 지역변수를 선언할 때 var를 사용하지 않았기 때문이다.
var를 사용하지 않은 지역변수는 전역변수가 된다.
따라서 " vscope = 'local'; " 에서 전역변수의 값을 local로 변경하게 된 것이다.

다시말해, 함수 내에서 var를 빼고 작성하게 되면 이미 작성된 변수의 값을 찾아 거슬러 올라가며, 일치하는 전역변수를 찾아 이를 변경한 것이다.

실제로 수많은 변수들이 작성되고, 함수에 따라 같은 변수명이어도 다른 값을 지니거나 필요로 할 수 있기 때문에, 전역변수를 사용해서 계속 활용하고자 할 경우 의도치 않는 변수를 불러올 가능성이 있다.

그래서 어떠한 함수 내에서 변수를 사용하고자 할 때는 이미 밖에서 선언된 것이 있더라도 되도록 지역변수를 선언하는 것으로 혼란을 방지하고자 하는 것이다.

	function a(){
    	var i = 0;
    }
    for(var i = 0; i < 5; i++){
    	a();
        console.log(i);
    } // for문 안에서 i는 전역변수

위의 코드에서는 함수 내에 var를 붙여 변수를 선언하여 지역변수가 되었고 때문에 아래 for문의 i에 영향을 주지 않는다.
그러나 var를 작성하지 않았다면 i=0이라는 전역변수가 되어 for문으로 a()를 실행하면서 i=0으로 만들기 때문에 계속 i<5가 되어 무한히 반복하게 된다.

이렇게 전역변수를 사용할 경우 반복문에서 종료조건을 걸었음에도 무한히 반복되는 오류가 발생할 수 있다.

전역변수 사용

1) 객체의 속성

불가피하게 전역변수를 사용해야 하는 경우 하나의 객체를 전역변수로 만들고
객체의 속성으로 변수를 관리하는 방법이 있다.

	var MyApp = {}
    MyApp.calculator = {
    	'left' : null,
        'right' : null
    }  
    MyApp.calculator.left = 10;
    MyApp.calculator.right = 20;
    function sum(){
    	return MyApp.calculator.left + MyApp.calculator.right;
    }
    console.log(sum());
    // 결과 : 30

2) 익명함수

익명함수를 이용해 전체를 감싸준 후 뒤에 ()를 붙여 바로 실행시키는 방법이다.
이렇게 하면 MyApp은 익명함수의 지역변수가 되는 것이다.

	(function(){
      var MyApp = {}
      MyApp.calculator = {
          'left' : null,
          'right' : null
      }
      NyApp.coordinate = {
          'left' : null,
          'right' : null
      }
      MyApp.calculator.left = 10;
      MyApp.calculator.right = 20;
      function sum(){
          return MyApp.calculator.left + MyApp.calculator.right;
      }
      console.log(sum());
    // 결과 : 30
  }())

정적 유효범위

자바스크립트는 함수가 선언된 시점에서의 유효범위를 갖는다.
이러한 유효범위의 방식을 정적 유효범위(static scoping)/렉시컬 스코핑(lexical scoping)이라고 한다.

	var i = 5; // 전역변수
    function a(){
    	var i = 10; //지역변수
        b();
    }
    function b(){
    	console.log(i);
    }
    a();

var i=5;는 전역변수이다. a()안의 i는 함수 내 지역변수이다.

코드를 보면 a()함수 안에서 다시 b()가 선언되었다.
이때 함수b()안에서 console.log(i)의 i는 값을 찾아 거슬러 올라간다.
그리고 자신의 블록을 벗어났을 때 가장 먼저 만나는 i는 전역변수 i = 5이기 때문에
b();의 결과는 전역변수 5의 값을 갖는다.

즉, 함수가 호출된 시점(사용될 때)이 아닌 선언된 시점에서 유효범위를 갖는다.

함수의 용도

1) 값으로서의 함수

JavaScipt에서는 함수도 객체이며 일종의 값이다.
JavaScipt에서는 함수가 값이 될 수 있다.

	function a() {}

이것은 a라는 변수에 담긴 함수이다.
따라서 var a = function(){}이라 할 수 있고,
함수 a는 변수 a에 담겨진 값이다.

이처럼 함수는 변수에 담길 수 있을 뿐 아니라 객체의 값으로도 담길 수 있다.

	a = {
    	b : function() {}
        }

b는 key값을 지니면서 변수의 역할을 한다.
이런것을 속성(property)라고 하며
이 속성에 담긴 값이'함수'라면 이것을 "메서드"라고 한다.

객체 안에 들어있는 함수의 값을 "메서드"라고 부른다.

2) 인자로서의 용도

	function cal(func, num) {
    	return func(num)
    }
    function increase(num) {
    	return num+1
    }
    console.log(cal(increase, 1));

다시 해석해 보자면

	function cal(increase, 1) {
    	var func = increase(1) {
        	return 1+1
        }
        // = func(1) => 2
    }

increase라는 함수가 인자로 들어가 func라는 변수에 담겼고 num에 1이 들어가며
func(1)을 호출하는데 func(1)은 increase(1)이므로 값은 2가된다.

3) 배열로서의 함수

	var process = [
    	function(input){ return input + 10;},
        function(input){ return input * input;},
        function(input){ return inpur / 2;}
    ];
    var input = 1;
    for(var i = 0; i < process.length; i++){
    	input = process[i](input);
    }
    console.log(input);

process라는 배열의 index(0,1,2)값에 각각 다른 함수가 값으로 들어가 있다.
for문에서 process.length조건에 따라 i가 0~2까지 진행되며, 각 index의 함수를 순서대로 실행한다.
input의 값은 처음 1로 선언되었고 반복문이 순서대로 실행됨에 따라 input의 값은 변한다.
순서대로 11, 121, 60.5가 되면서 console값은 60.5가 된다.

이렇게 변수, 매개변수, 리턴값 등 다양한 용도로 쓰일 수 있는 데이터를 first-class citizen(object)처럼 부른다.
1급 객체

콜백(callback)

함수의 인자로 함수가 사용되는 경우를 콜백(callback)이라고 한다.

var numbers = [20,10,9,8,7,6,5,4,3,2,1];
var sortfunc = function(a,b){ // sort라는 메소드를 실행될 때 원소들을 비교하는 것
if(a>b){
	return 1;
    } else if(a<b){
    	return -1;
      } else{
      	return 0;
        }
}
console.log(numbers.sort(sortfunc));

위 예제를 보면 sort()라는 메소드의 인자로 sortfunc()라는 함수가 들어간다.

이때 함수의 인자로 쓰인 sortfunc함수가 콜백함수가 되고, 이 콜백의 결과가 인자로 들어가며 sort()의 결과가 결정된다.

비동기 처리

쉽게 얘기하자면 동기처리는 매 작업을 처리할 때 항상 첫단계부터 실행하는 것이고,
비동기처리는 필요한 단계에서만 처리를 실행하는 것이다.

그 예시로 홈페이지의 알림창을 보면 된다.
우리에게 알림이 +9가 떴다고 하자. 우린 이걸 확인하기 위해 알림창을 누른다. 그러고 나면 알림은 다시 0이 된다.

이때 홈페이지는 새로고침(reload) 되지 않고
알림창만 떴다가 닫힌다. 이것이 비동기 방식인 것이다.
동기방식이었다면 우리가 알림창을 눌렀을 때 홈페이지가 처음부터 불려오며 리로드되고,
알림창을 닫았을 때 또 다시 처음부터 불려왔을 것이다.

그러나 비동기 방식을 사용했기 때문에 홈페이지 전체를 불러올 필요없이 필요한 부분의 정보만이 처리되었다.

Ajax

JS에서는 Ajax를 쓸 때 비동기 처리를 할 수 있다.
Ajax

아래 예시에서는 jQuery를 이용한 문법으로 Ajax를 이용했다.

$.get();	// jQuery에서의 Ajax사용 문법

클로저(closure)

내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다.

1) 내부함수 / 외부함수

function outter() {
  var title = 'coding everybody'; // 외부함수에 정의 되어 있는 지역변수
  function inner() {
    console.log(title);
  }
  inner();
}

outter라는 함수 안에 inner라는 함수가 작성되었다.
이때 inner를 내부함수, outter를 외부함수라고 한다.

inner함수 내용을 보면 console문이 title을 불러오는데 inner내에는 존재하지 않는다.
그러면 그 바깥으로 건너가 찾게되고, var title을 발견하면 이를 찾아불러온다.

2) 클로저

이렇게 내부함수에서 외부함수에 있는 지역변수에 접근하는 것을 클로저(closure)라고 한다.

여기서 더 나아가 외부함수의 실행이 끝나고 소멸한 뒤에도 내부함수가 외부함수의 변수에 접근할 수 있는 이것이 클로저인 것이다.

function outter() {
  var title = 'coding everybody';
  return function() {
    console.log(title);
  }
}
var inner = outter();
inner()

외부함수의 outter내에 return 값으로 내부함수를 작성하였다.
return을 하였기 때문에 이는 실행이 종료된 상태라 할 수 있다.

그런데 위 예문을 실행해보면 'coding everybody'가 뜬다.

이것은 클로저의 특성으로,
클로저는 내부함수가 외부함수의 값에 접근할 수 있을 뿐 아니라 외부함수가 종료된 이후에도 내부함수를 통해 접근할 수 있는 것이다.

3) 클로저의 응용

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(){
        return i;
    }
}
for(var index in arr) {
    console.log(arr[index]());
}

위 예문을 보면 arr라는 배열에 첫번째 for문을 돌려서 i가 0~4인동안 반복하고,
해당 원소에는 return i를 하는 함수를 담았다.

그리고 그 아래 for문에서 arr 배열의 index만큼 반복시켜 해당 index에 해당하는 원소의 함수를 실행시켰다. 그렇다면 우리가 원하는 결과는 이전 return i에 따라 console창에 0~4이 출력되어야 할 것이다.

하지만 원하는 결과가 나오지 않는다.

이유는 첫번째 for문에서의 함수가 return하고자 하는 i는 외부함수의 지역변수가 아니기 때문이다.
즉, 접근이 불가하다.

이를 원하는 결과가 나오도록 수정

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(id) {
        return function(){
            return id;
        }
    }(i);
}
for(var index in arr) {
    console.log(arr[index]());	// 결과 : 0, 1, 2, 4
}

위 코드를 보면 첫번째 for문에서 return i를 하던 함수 바깥에 외부함수를 만들고 바로 실행을 시켰다.
그리고 i를 받아 매개변수 id에 들어갈 수 있도록 하였다.

function(id) { ~~~ }(i);

이렇게 하면 외부함수의 id=i가 되었고, 그 덕에 내부함수에서 매개변수 id에 접근해 원하던 대로 i(=id)를 return할 수 있게 됐다.

이처럼 원래는 접근하지 못하던 값에 클로저를 이용하면 접근할 수 있게 된다.

profile
🐣

0개의 댓글