유효범위는 변수의 수명
변수를 선언할 때는 꼭 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
함수가 되는 것이다.
참고 - 동기 & 비동기 설명
참고 - 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()); //공각기동대
클로저는 객체의 메소드에서도 사용할 수 있다. set_title과 get_title은 모두 외부함수인 factory의 인자값으로 전달된 지역변수 title을 사용하고 있다.
동일한 외부함수 안에서 만들어진 내부함수나 메소드는 외부함수의 지역변수를 공유한다.
javascript는 기본적으로 private한 속성을 지원하지 않는데, 클로저의 이러한 특성은 private한 속성을 이용할 수 있게 한다.
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]))
null
: 첫번째 인자는 함수(sum)가 실행될 맥락이다.[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이 된다는 의미다.