Core Javascript #5 Closure

7818 단어 JavaScriptJavaScript

Closure

Clousure의 원리

var outer = function(){
    var a = 1;
    var inner = function(){
        return ++a;
    };
    return inner;
};
var outer2 = outer();
console.log(outer2()); //2
console.log(outer2()); //3

위의 함수의 동작을 자세히보면, 함수 inner가 반환되고
outer2가 inner함수를 받게된다. outer()함수는 이미 실행되고
outer2 함수가 실행될때에는 이미 outer함수는 끝이나서 가비지 컬렉터로
수집되어서 var a = 1이라는것은 나타나지않아야하는데 여기서는 a라는 변수가 그대로 남아있게 된다. 이것은 왜그런것일까???!
가비지 컬렉터는 사용하지 않는 메모리의 데이터를 지워주는 역활을 하는데, 어떤 값을 참조하는 변수가 하나라도 남아있다면 그 값은 수집 대상에 포함시키지 않고 그대로 남기게된다. 위와 같은 경우도 outer2에 inner를 넣음으로써 a라는 변수를 사용할수도 있다는것을 가비지 컬렉터는 알게되기 때문. 즉 클로저는 이러한 원리로 동작하게된다.
클로저라는것을 정리하자면 어떤 함수 A에서 선언한 변수 a를 참조하는
내부함수 B를 외부로 전달할경우 실행 컨택스트가 종료된 이후에도
변수 a가 사라지지않는 현상. 이라고 말할수있다👌

___또한 setInterval/setTimeout, eventListener같은 경우에도
외부 객체인 window, DOM에 함수내에서 지역변수를 참조하게된다.

Clousure 메모리관리

Clousure의 메모리관리는 식별자에 NULL을 입력해주면된다.

Clousure 사용

콜백 함수 내부에서 외부 변수를 참조하기 위한 방법

  1. 콜백함수를 내부함수로 선언해서 외부변수를 직접 참조하는 방법
var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');

fruits.forEach(function (fruit){
    var $li = document.createElement('li');
    $li.innerText = fruit;
    $li.addEventListener('click', function(){
        alert('your choice is ' + fruit);
    });
    $ul.appendChild($li);
});
document.body.appendChild($ul)
  1. bind를 활용하여 값을 직접 넘겨주는 방법(여러가지 제약이 생김)
var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');

var alertFruit = function(fruit){
    alert('your choice is ' + fruit);
}

fruits.forEach(function (fruit){
    var $li = document.createElement('li');
    $li.innerText = fruit;
    $li.addEventListener('click', alertFruit.bind(null, fruist));
    $ul.appendChild($li);
});
document.body.appendChild($ul)
alertFruit(fruits[1]);
  1. 콜백함수를 고차함수로 만들어서 클로저 활용 방법
var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');

var alertFruitBuiler = function(fruit){
    return function(){
        alert('your choice is ' + fruit);
    };
};

fruits.forEach(function (fruit){
    var $li = document.createElement('li');
    $li.innerText = fruit;
    $li.addEventListener('click', alertFruitBuiler(fruit));
    $ul.appendChild($li);
});
document.body.appendChild($ul)
alertFruitBuiler(fruits[1]);

접근권한제어

var outer = function(){
    var a = 1;
    var inner = function(){
        return ++a;
    };
    return inner;
};
var outer2 = outer();
console.log(outer2());
console.log(outer2());

앞에서 봣던 이코드를 다시보면 외부에서는 outer라는 함수에 접근을 할수가없다. 단 inner 함수를 return 해줌으로써 a라는값이 증가되는것을 알수있다. 즉 외부로 알려줄것들은 return으로 알려줄수잇고, 그렇지 않은것들은 return을 사용하지 않으면 된다.

var createCar = function () {
    var fuel = Math.ceil(Math.random() * 10 + 10);
    var power = Math.ceil(Math.random() * 3 + 2);
    var moved = 0;

    var publicMembers = {
        get moved(){
            return moved; 
        },
        run : function(){
            var km = Math.ceil(Math.random() * 6);
            var wasteFuel = km / power;
            if(fuel < wasteFuel){
                console.log('이동불가');
                return;
            }
            fuel -= wasteFuel;
            moved += km;
            console.log(km + 'km 이동 ( 총 ' + moved + 'km) 남은 연료 : ' + fuel);
        }
    }
    Object.freeze(publicMembers);
    return publicMembers;
};

var car = createCar();
console.log(car)

위의 코드는 생성자를 만들때 return 해주는것이 Object.freeze를 해준 객체이다. 즉 객체의 값을 변경할수없이 returnd을 하였기 때문에
외부에서 fuel, power, moved 같은 값을 변경할수가 없다.
만약 var publicMembers로 묶어서 freeze를 해주지 않고 return을 했다면 fuel, power, move에 접근이 가능했을것이다

부분 적용 함수

var add = function(){
    var result = 0;
    for ( var i = 0; i < arguments.length; i++){
        console.log("result",result)
        console.log("arguments",arguments[i]);
        result += arguments[i];
    }
    return result;
};

var addPartial = add.bind(null,1,2,3,4,5);
console.log(addPartial(6,7,8,9,10));
var partical = function(){
    console.log("arguments",arguments)
    var originalParitialArgs = arguments;
    var func = originalParitialArgs[0];
    if(typeof func !== 'function'){
        throw new Error('첫번째 인자가 함수가 아닙니다.');
    }
    return function(){
        var particalArgs = Array.prototype.slice.call(originalParitialArgs, 1);
        console.log("particalArgs",particalArgs);
        var restArgs = Array.prototype.slice.call(arguments);
        console.log("restArgs",restArgs);
        return func.apply(this, particalArgs.concat(restArgs));
    };
};

var add = function(){
    var result = 0;
    for(var i = 0 ; i < arguments.length;i++){
        result += arguments[i];
    }
    return result;
};
var addPartial = partical(add, 1 ,2 ,3 , 4, 5);
console.log(addPartial(6,7,8,9,10));

var dog = {
    name : '강아지',
    greet: partical(function(prefix, suffix){
        return prefix + this.name + suffix;
    }, '왈왈',)
};
console.log(dog.greet('입니다!'));

디바운스

디바운스는 짧은 시간 동안 동일한 이벤트가 많이 발생할경우 이를 전부 처리하지않고 처음 또는 마지막에 발생한 이벤트에 대해 한번만 처리하는것을 의미하며, 프론트엔트 성능 최적화에 큰 도움을 주는 기능중 하나.
scroll, wheel, mousemove, resize 등에 적용하기 좋다.

var debounce = function(eventName, func, wait){
    var timeoutId = null;
    return function(event){
        var self = this;
        console.log(eventName, 'event 발생');
        clearTimeout(timeoutId);
        timeoutId = setTimeout(func.bind(self, event), wait);
    };

    var moveHandler = function(e){
        console.log('move event 처리');
    }

    var wheelHandler = function(e){
        console.log('wheel event 처리');
    }
};

document.body.addEventListener('mousemove', debounce('move', moveHandler, 500));
document.body.addEventListener('mousewheel', debounce('wheel', wheelHandler, 700));

커링 함수

커링함수란 여러개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출할수있게 체인형식으로 만들어주는것,
커링은 한번에 하나의 인자만 전달하는것을 원칙으로한다.

var curry3 = function(func){
    console.log("func",func)
    return function (a){
        console.log("a",a)
        return function (b){
            console.log("b",b)
            return func(a, b);
        };
    };
};

var getMaxWith10 = curry3(Math.max)(10);
console.log(getMaxWith10(8));
console.log(getMaxWith10(25));

//func [Function: max]
//a 10
//b 8
//10
//b 25
//25
var curry5 = function(func){
    return function(a){
        return function(b){
            return function(c){
                return function(d){
                    return function(e){
                        return func(a, b, c, d, e);
                    }
                }
            }
        }
    }
}
var getMax = curry5(Math.max);
console.log(getMax(1)(2)(3)(4)(5));
----------------------------------------------
var curry5 = func => a => b => c => d => e => func(a, b, c, d, e);
var getMax = curry5(Math.max);
console.log(getMax(1)(2)(3)(4)(5));

ES6 부터 화살표 함수를 사용하여 위와 같이 사용이 가능하다

var getInformation = function(baseUrl){
    return function(path){
        return function(id){
            return fetch(baseUrl + path + '/' + id);
        };
    };
}
//ES6
var getInformation = baseUrl => path => id => fetch(baseUrl + path + '/' + id);

최근은 프레임워크나 라이브러리에서 커링이 광범위하게 사용되고있다.

!!이내용은 코어 자바스크립트(정재남지음) 책을보고 정리한 내용입니다

좋은 웹페이지 즐겨찾기