Core Javascript #5 Closure


Closure


Closureの原理

var outer = function(){
    var a = 1;
    var inner = function(){
        return ++a;
    };
    return inner;
};
var outer2 = outer();
console.log(outer2()); //2
console.log(outer2()); //3
上の関数の動作をよく見ると、関数の内部に戻ります.
outher 2は内部関数を受け入れます.outer()関数が実行されました.
outher 2関数が実行されると、outher関数は終了し、ゴミ収集器として使用されます.
収集後にvara=1が現れるべきではありませんが、ここではaという変数が保持されます.なんでだろう???
ゴミ収集器は、使用されていないメモリのデータを消去する責任を負います.値を参照する変数がまだ存在する場合、その値は収集オブジェクトに含まれず、保持されます.Gavyコレクタは、上記の場合、innerをouther 2に入れることでaという変数を使用することもできることを知っているからである.つまり、エンクロージャはこのような原理で動作します.
まとめると、ある関数Aで宣言された変数aが参照する
内部関数Bを外部に渡すと、実行呼び出しの終了後でも、
変数aが消えない現象.言える👌
Interval/settimeout,eventListenerを設定することもできます.
外部オブジェクトwindow、DOMでは、関数で領域変数を参照します.

Cloudメモリの管理


Closureのメモリ管理は識別子にNULLを入力するだけです.

Closureの使用


コールバック関数で外部変数を参照する方法


内部関数として
  • コールバック関数を宣言することによって外部変数を直接参照する方法
  • .
    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)
  • 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]);
    
  • コールバック関数を高次関数とすることにより、エンクロージャを用いる方法
  • .
    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という関数に外部からアクセスできません.しかし、内部関数を返すことで、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)
    上のコードオブジェクトは、ジェネレータの作成時に返されます.フリーズされたオブジェクト.つまり、返されるオブジェクトの値は変更できません.
    fuel、power、movedなどの値を外部から変更できません.
    varpublicMembersバインドでフリーズせずに戻る場合は、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('입니다!'));

    返品


    同じイベントが短時間で多数発生する場合は、すべてのイベントを処理するのではなく、最初のイベントまたは最後のイベントのみを処理することを意味します.これは、フロントエンドのパフォーマンスの最適化に役立つ機能の1つです.
    巻物、ローラー、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));

    コリンかんすう


    曲線関数とは、複数のパラメータを受信した関数を、1つのパラメータのみを受信した関数に分けてチェーン形式で順次呼び出すことである.
    カードリングの原則は、一度に1つの因子だけを伝達することです.
    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));
    ES 6から矢印関数を使用すると、以下のようになります.
    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);
    最近、クロストークはフレームワークやライブラリで広く使われています.
    !!伊内龍は『Core JavaScript』(鄭在南)という本を読んで整理した.