Javascript #02 - 함수지향

dldzm·2021년 7월 14일
0

Javascript 기본

목록 보기
2/9

유효범위

유효범위는 변수의 수명

전역변수와 지역변수

변수를 선언할 때는 꼭 var을 붙이는 것을 습관화해야 한다. 전역변수를 사용해야 하는 경우라면 그것을 사용하는 이유를 명확히 알고 있을 때 사용하도록 하자.

var vscope = 'global';
function fscope(){
  var vscope ='local';	//이 한 줄이 없다면 global을,
  alert(vscope);	//local을 보여준다.
}
fscope();

같은 이름의 지역변수와 전역변수가 동시에 정의되어 있다면 지역변수가 우선한다는 것을 알 수 있다.

전역변수의 사용

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

MYAPP = {}
MYAPP.calculator = {
  'left' : null,
  'right' : null
}

MYAPP.coordinate = {
  'left' : null,
  'right' : null
}

MYAPP.calculator.left = 10;
MYAPP.calculator.right = 20;

function sum(){
    return MYAPP.calculator.left + MYAPP.calculator.right;
}

document.write(sum());	//30

만약 전역변수를 사용하고 싶지 않다면 익명함수를 호출한다.
다음 방법은 로직을 모듈화하는 일반적인 방법이다.

(function(){
    var MYAPP = {}
    MYAPP.calculator = {
        'left' : null,
        'right' : null
    }
    MYAPP.coordinate = {
        'left' : null,
        'right' : null
    }
    MYAPP.calculator.left = 10;
    MYAPP.calculator.right = 20;
    function sum(){
        return MYAPP.calculator.left + MYAPP.calculator.right;
    }
    document.write(sum());
}())

(function(){함수 내용})(); 익명함수 이용하기

값으로서의 함수와 콜백

함수의 용도

Javascript에서는 함수도 객체다. 일종의 값으로 생각해야 한다. Javascript의 함수는 값이 될 수 있다.

function a(){}

//이는 다음과 같이 표현할 수 있다.
var a = function(){}

//또는
a = {
  b:function(){	//함수는 객체 안에 저장될 수 있다. 함수가 값이기 때문이다.
  }
};

c++ 때에는 함수를 넘기기 위해 함수 포인터를 사용했으나 여기서는 으로 넘긴다.

function call(func, num){
  return func(num)
}

function increase(num){
  return num+1
}

document.write(call(increase,1));

func[mode]call('plus')(2,1)

function call(mode){
  var funcs = {
    'plus' : function(left, right) {return left + right},
    'minus' : function(left, right) {return left - right}
  }
  return funcs[mode];
}

document.write(call('plus')(2,1));

하고 싶은 말은 배열 안에 함수를 넣을 수 있다. 왜냐면 함수도 값으로 취급되기 때문!

var process = [
  function(input) { return input+10; },
  function(input) { return input*input;},
  function(input) { return input/2}
 ];

var input = 1;
for (var i = 0 ; i < process.length; i++){
  input = process[i](input);
  document.write(input+"<br/>");
}

콜백

함수가 값으로 사용될 수 있는 특성을 이용하면 함수의 인자로 함수를 전달할 수 있다.

var numbers = [20,10,9,8,7,6,5,4,3,2,1];
//numbers.sort(); 	//[1, 10, 2, 20, 3, 4...]가 나온다.

function sortNum(a, b){
  return a-b;	//a-b : 오름차순, b-a : 내림차순
}

console.log(numbers.sort(sortNum));

sort.()는 (내장, 빌트인)메소드인데 이대로 시행하면 유니코드 순서대로 정렬이 된다. 따라서 제대로 정렬하기 위해 여기에 입력인자로 함수 sortNum을 넘겨준다.

여기서 sortNum을 callback함수가 되는 것이다.

참고 - sort 함수

비동기 처리

  • 동기 : 요청과 결과가 동시에 일어나서 요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 한다. 설계가 간단하고 직관적이지만 결과가 주어질 때까지 아무것도 못하고 대기한다.
  • 비동기 처리 : 요청과 결과가 동시에 일어나지 않는다. 동기보다 복잡하지만 결과가 주어지는데 시간이 걸리더라도 그 시간동안 다른 작업을 할 수 있어 효율적인 자원 관리가 가능하다.

참고 - 동기 & 비동기 설명
참고 - Ajax
Ajax - Asynchronous javascript and XML

다음과 같은 datasource.json.js가 존재한다고 하자.

{"title":"JavaScript","author":"egoing"}

콜백 이야기를 하고자 나온 것임. 함수를 인자로 전달한다는 것이 주요 포인트.

<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
</head>
<body>
<script type="text/javascript">
    $.get('./datasource.json.js', function(result){
        console.log(result);
    }, 'json');
</script>
</body>
</html>

클로저

closure는 내부함수가 외부함수의 맥락에 접근할 수 있는 것.

내부함수와 외부함수

함수1 안에서만 쓰는 함수2가 있을 때, 함수 1안에 함수2를 선언하여 가독성을 높이고 쉽게 관리할 수 있게 한다.

function outter(){
  function inner(){
    var title = "code";
    alert(title);
  }
  inner();
}

outter();	//code

그런데 다음과 같이 코드가 있다고 하면,

function outter(){
  var title = "code";
  function inner(){
    alert(title);
  }
  inner();
}

outter();	//code

처음에 inner안에 있는 title은 inner안에서 값을 찾지만 관련 변수가 없다는 것을 알고는 외부함수인 outter안에서 값을 찾게 된다.

내부함수에서 외부함수의 지역변수에 접근한 것

closure

내부함수는 외부함수의 지역변수에 접근할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있다는 것이다.

function outter(){
    var title = 'code';  
    return function(){ alert(title); } //내부함수를 리턴하고 있는 것. 이름 없는 내부함수
}
inner = outter(); // inner라고 하는 변수에 리턴받은 내부함수의 내용이 들어가는 것
inner(); 

outter함수가 return하는 순간, 이미 outter 함수의 생명은 끝난다. 죽어버린 것. 하지만 inner 함수를 부르는 순간 return 받은 function(){ alert(title); }가 실행되어야 하는데 여기서의 title은 이미 죽은 outter 함수의 지역변수라 원칙적으로는 가져올 수 없는 것. 하지만 title의 값으로 code 결과를 가져온다. 이것이 closure이다. 죽어도 접근할 수 있다는 것.

function factory(title) {
  return {
    //여기서 title은 factory 함수의 입력인자의 title로 가져온다.
    get_title : function() { return title; },
    set_title : function(_title) {
      if (typeof(_title) === 'String') {
        title = _title;
      }
      else {
        alert ('제목은 문자열이여야 합니다.');
    }
  }
}

fav = factory('The Grnd Bdpst Htl');
matrix = factory('Matrix');

console.log(fav.get_title());		//The Grnd Bdpst Htl
console.log(matrix.get_title());	//Matrix

fav.set_title('공각기동대');
console.log(fav.get_title());		//공각기동대
  1. 클로저는 객체의 메소드에서도 사용할 수 있다. set_title과 get_title은 모두 외부함수인 factory의 인자값으로 전달된 지역변수 title을 사용하고 있다.

  2. 동일한 외부함수 안에서 만들어진 내부함수나 메소드는 외부함수의 지역변수를 공유한다.

  3. javascript는 기본적으로 private한 속성을 지원하지 않는데, 클로저의 이러한 특성은 private한 속성을 이용할 수 있게 한다.

closure 관련 자주 나오는 실수

참고 - youtube link

var arr = []
for(var i = 0; i < 5; i++){
  arr[i] = function(){
    return i;
  }
}

for(var index in arr) {
  console.log(arr[index]());
}

여기서 결과는 5 5 5 5 5이다.
function(){ return i; }의 i값은 외부 변수의 값이 아니기 때문이다. 이 함수를 내부함수로 하고 이 함수를 감싸는 외부함수를 만들어보자.

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(id) {
        return function(){
            return id;
        }
    }(i);	//i를 받아 id로 넣는다. 전달받은 id는 곧바로 return 되어 arr[i]에 저장된다.
}

for(var index in arr) {
    console.log(arr[index]());
}

함수 호출

Function.apply, Function.call

sum();을 이렇게 호출해도 되지만 sum.apply(null); 이렇게 호출해도 된다.

function sum(arg1, arg2){
  return arg1 + arg2;
}

alert(sum.apply(null, [1, 2]))
  1. null : 첫번째 인자는 함수(sum)가 실행될 맥락이다.
  2. [1, 2] :두번째 인자는 배열인데, 이 배열에 담겨있는 원소가 함수의 인자로 순차적으로 대입된다.
function sum() {
  var _sum = 0;
  for (id in this) {
    if (typeof this[name] !== 'function')
      _sum += this[id];
    //document.write("this "+ id + "\n");
    //여기서 id는 val1, v1... 들
  }
  return _sum;
}
o1 = {val1:1, val2:2, val3:3, sum:sum}
o2 = {v1:10, v2:50, v3:100, v4:25, sum:sum}

console.log(sum.apply(o1))	//6
console.log(sum.apply(o2))	//185

sum.apply(o1)에서 o1은 apply의 인자로 들어가면 sum 함수의 this가 된다. (== var this = o1;)

apply의 첫번째 인자는 함수가 실행될 맥락이다. sum.apply(o1)에서는 함수 sum을 객체 o1의 메소드로 만들고 sum을 호출한 후에 sum을 삭제한다

o1.sum = sum; 
console.log(o1.sum());	//결과로 sum함수의 모든 내용이 나온다.
delete o1.sum();

여기서는 함수 sum을 객체 o1의 메소드로 만들고 호출한 후에 sum을 삭제한다.
함수 sum에서 this의 값이 전역객체가 아니라 o1이 된다는 의미다.

profile
🛰️ 2021 fall ~

0개의 댓글