Змінна змінна доступна із закриття. Як я можу це виправити?


75

Я використовую Typeahead за допомогою Twitter. Я стикаюся з цим попередженням від Intellij. Це призводить до того, що "window.location.href" для кожного посилання є останнім елементом у моєму списку елементів.

Як я можу виправити свій код?

Нижче мій код:

AutoSuggest.prototype.config = function () {
    var me = this;
    var comp, options;
    var gotoUrl = "/{0}/{1}";
    var imgurl = '<img src="/icon/{0}.gif"/>';
    var target;

    for (var i = 0; i < me.targets.length; i++) {
        target = me.targets[i];
        if ($("#" + target.inputId).length != 0) {
            options = {
                source: function (query, process) { // where to get the data
                    process(me.results);
                },

                // set max results to display
                items: 10,

                matcher: function (item) { // how to make sure the result select is correct/matching
                    // we check the query against the ticker then the company name
                    comp = me.map[item];
                    var symbol = comp.s.toLowerCase();
                    return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
                        comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
                },

                highlighter: function (item) { // how to show the data
                    comp = me.map[item];
                    if (typeof comp === 'undefined') {
                        return "<span>No Match Found.</span>";
                    }

                    if (comp.t == 0) {
                        imgurl = comp.v;
                    } else if (comp.t == -1) {
                        imgurl = me.format(imgurl, "empty");
                    } else {
                        imgurl = me.format(imgurl, comp.t);
                    }

                    return "\n<span id='compVenue'>" + imgurl + "</span>" +
                        "\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
                        "\n<span id='compName'>" + comp.c + "</span>";
                },

                sorter: function (items) { // sort our results
                    if (items.length == 0) {
                        items.push(Object());
                    }

                    return items;
                },
// the problem starts here when i start using target inside the functions
                updater: function (item) { // what to do when item is selected
                    comp = me.map[item];
                    if (typeof comp === 'undefined') {
                        return this.query;
                    }

                    window.location.href = me.format(gotoUrl, comp.s, target.destination);

                    return item;
                }
            };

            $("#" + target.inputId).typeahead(options);

            // lastly, set up the functions for the buttons
            $("#" + target.buttonId).click(function () {
                window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
            });
        }
    }
};

За допомогою @ cdhowie, ще трохи коду: я оновлю оновлення, а також href для клацання ()

updater: (function (inner_target) { // what to do when item is selected
    return function (item) {
        comp = me.map[item];
        if (typeof comp === 'undefined') {
            return this.query;
        }

        window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
        return item;
}}(target))};

Відповіді:


61

Тут потрібно вкласти дві функції, створивши нове закриття, яке фіксує значення змінної (замість самої змінної) на момент створення закриття . Ви можете зробити це, використовуючи аргументи зовнішньої функції, що викликається негайно. Замініть цей вираз:

function (item) { // what to do when item is selected
    comp = me.map[item];
    if (typeof comp === 'undefined') {
        return this.query;
    }

    window.location.href = me.format(gotoUrl, comp.s, target.destination);

    return item;
}

З цим:

(function (inner_target) {
    return function (item) { // what to do when item is selected
        comp = me.map[item];
        if (typeof comp === 'undefined') {
            return this.query;
        }

        window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);

        return item;
    }
}(target))

Зверніть увагу, що ми переходимо targetдо зовнішньої функції, яка стає аргументом inner_target, ефективно фіксуючи значення targetв момент виклику зовнішньої функції. Зовнішня функція повертає внутрішню функцію, яка використовує inner_targetзамість targetі inner_targetне буде змінюватися.

(Зверніть увагу, що ви можете перейменувати inner_targetна, targetі у вас все буде добре - targetбуде використано найближчий , що буде параметром функції. Однак наявність двох змінних з однаковим іменем у такому вузькому діапазоні може бути дуже заплутаним, і тому я назвав їх по-різному в моєму прикладі, щоб ви могли бачити, що відбувається.)


а друга частина? де я встановлюю href для кнопки? такий спосіб робити мені здається надзвичайно дивним = /
iCodeLikeImDrunk

3
@yaojiang Застосуйте ту саму техніку. Якщо вам потрібна допомога, повідомте мене, і ми зможемо перейти до чату. Але спочатку спробуйте зрозуміти, що відбувається в моєму прикладі, і спробуйте зробити це самостійно. (Концепція буде дотримуватися краще, якщо ви зробите це самостійно!)
cdhowie

2
Важливо зазначити, що анонімні функції замикаються навколо зовнішніх змінних за посиланням . Якщо ви хочете закрити їх за значенням, тоді вам потрібно ввести нову змінну, яка має значення, яке ви хочете захопити, і не буде змінюватися - що, завдяки тому, що JavaScript не має області блоків, найпростіше реалізується за допомогою параметрів функції.
cdhowie

2
@yaojiang Це не техніка, унікальна для JavaScript, швидше вона широко використовується у функціональних мовах, які мають функціональну область. Це здається дивним, але також неймовірно потужним. Дайте поняттям (зокрема закриттям) трохи часу, щоб вони заглибились, і ви будете кращим програмістом для цього. (Я не усвідомлював, що анонімні методи C # були закриттями, лише після того, як зробив велику роботу з JavaScript, і коли я повернувся до C #, зрозумів, що втратив неймовірно потужний інструмент.)
cdhowie

Це дало мені поштовх у вивченні подальшого javascript, включаючи закриття, дякую за цей фрагмент !!!
Titus Shoats

147

Мені сподобався абзац « Закриття всередині петель з Javascript Garden»

Це пояснює три способи зробити це.

Неправильний спосіб використання застібки всередині петлі

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}

Рішення 1 з анонімною обгорткою

for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);
}

Рішення 2 - повернення функції із закриття

for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

Рішення 3 , моє улюблене, де, на мою думку, нарешті зрозумів bind- ага! зв’язати FTW!

for(var i = 0; i < 10; i++) {
    setTimeout(console.log.bind(console, i), 1000);
}

Я настійно рекомендую Javascript garden - він показав мені це та багато інших примх Javascript (і змусив мене подобатися JS ще більше).

ps, якщо твій мозок не розплавився, тобі цього дня не вистачило Javascript.



2

Оскільки єдиним обсягом, який має JavaScript, є область функцій , ви можете просто перенести закриття на зовнішню функцію, за межі області дії, в якій ви перебуваєте.


0

Просто щоб пояснити відповідь @BogdanRuzhitskiy (оскільки я не міг зрозуміти, як додати код у коментарі), ідея використання let дозволяє створити локальну змінну всередині блоку for:

for(var i = 0; i < 10; i++) {
    let captureI = i;
    setTimeout(function() {
       console.log(captureI);  
    }, 1000);
}

Це буде працювати практично в будь-якому сучасному браузері, крім IE11.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.