Function

by chitacan

Contents

  • Scope
  • Closure
  • Module Pattern
  • Curry
  • Memoization

Scope

  • 자바 스크립트는 함수 유효범위만 존재한다.
  • 함수 내에서 정의된 매개변수는 함수 외부에서 접근 불가
  • 내부에서는 자유롭게 접근 가능

Closure

자신의 범위 밖에 있는 변수들에 접근할 수 있는 코드블록

  • 자신을 가지고 있는 함수의 context에 접근할 수 있기 때문
  • 복사본이 아니라 변수자체에 접근할 수 있음

Closure

외부에서 value 값에 접근할 수 없게 만들기


	var myObject = function () {
	  var value = 0;
	  var what = 0;

	  return {
	    increment: function(inc) {
	      value += typeof inc === 'number' ? inc : 1;
	      what += 1;
	    },
	    getValue: function() {
	      return value;
	    }
	  }
	}();
	myObject.increment(3);
	myObject.getValue();
					
Run this code

Closure


	var fade = function(node) {
	    var level = 1;
	    var step = function() {
	        var hex = level.toString(16);
	        node.css('color', '#' + hex + 'FFFF' + hex);
	        if (level < 15) {
	            level += 1;
	            setTimeout(step, 100);
	        }
	    };
	    setTimeout(step, 100);
	}

	fade($('#closure-usage'));
					
Run this code

Closure

나쁜 예제


	var addHandlers = function(nodes) {
	  var i;
	  for (i = 0 ; i < nodes.length ; i += 1) {
	    nodes[i].onclick = function(e) {
	      alert(i);
	    }
	  }
	}

	addHandlers($('#closure-bad').children());
					
Run this code 각각의 이벤트 핸들러에 고유한 i를 할당하려 했지만 마지막 i의 값에 연결됨

Closure

더 나은 예제


	var addHandlers = function(nodes) {
	  var i;
	  for (i = 0 ; i < nodes.length ; i += 1) {
	    nodes[i].onclick = function(i) {
	      return function(e) {
	        alert(i)
	      }
	    }(i);
	  }
	}

	addHandlers($('#closure-good').children());
					
Run this code onclick 에 할당되는 함수를 바로 실행해 스코프를 만들고 내부에 실제 핸들러에 연결되는 클로져 함수를 생성

Module

내부의 상태, 구현을 숨기고 인터페이스만 제공하는 함수, 객체

  • 전역변수를 사용하지 않을 수 있음
  • 함수 유효범위와 클로저의 특성을 활용
  • 클로저를 활용해 인터페이스 제공

How to use

함수 내에서만 사용하고 싶은 객체, 어떻게 저장하면 좋을까??

  • 전역변수 >> 사용하면 안되는 나쁜놈!!
  • 객체를 함수 내에서 정의 >> 비효율적!!

Use case(1)

함수에 사용할 객체를 함수내부에 두고, 클로저를 만들자


Function.prototype.method = function(name, func) {
  this.prototype[name] = func;
  return this;
}

String.method('deentityify', function() {
    var entity = {
        quot: '"',
        lt:   '<',
        qt:   '>'
    };

    return function() {
        return this.replace(/&([^&;]+);/g, function(a, b) {
            var r = entity[b];
            return typeof r === 'string' ? r : a;
            }
        );
    };
}());

'<">'.deentityify();
					
Run this code

Use case(2)

모듈 패턴을 활용한 안전한 객체 생성


var serial_maker = function() {
  var prefix = 0;
  var seq    = 0;

  return {
    set_prefix : function(p) {
      prefix = String(p);
    },
    set_seq : function(s) {
      seq = s;
    },
    gensym : function() {
      var result = prefix + seq;
      seq += 1;
      return result;
    }
  };
};

var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
seqer.gensym();
					
Run this code

Curry

함수와 인수를 결합해 새로운 함수를 만들 수 있는 패턴


Function.prototype.method = function(name, func) {
  this.prototype[name] = func;
  return this;
}

Function.method('curry', function() {
  var slice = Array.prototype.slice;
  var args  = slice.apply(arguments);
  var that  = this;

  return function() {
    return that.apply(null, args.concat(slice.apply(arguments)));
  }
});

var add = function(a, b) {
  return a + b;
}

var add1 = add.curry(1);
add1(7);
				
Run this code

Memoization

이전 연산결과를 저장하는 최적화 기법


	var fibo = function(n) {
	  return n < 2 ? n : fibo(n-1) + fibo(n-2);
	};

	for (var i = 0 ; i <= 10; i += 1) {
	  fibo(i);
	}
					

Memoization

호출횟수가 줄었다는데, 어떻게 확인하지??


	var fibo = function() {
	  var memo = [0,1];
	  var fib = function(n) {
	    var result = memo[n];
	    if (typeof result !== 'number') {
	      result = fib(n - 1) + fib(n - 2);
	      memo[n] = result;
	    }
	    return result;
	  };
	  return fib;
	}();

	for (var i = 0 ; i <= 10; i += 1) {
	  fibo(i);
	}
					

repoll

http://repoll.herokuapp.com

prototype closure inheritance

Q & A