Розуміння $ .proxy () в jQuery


167

З документів я розумію, що .proxy()може змінити область функції, переданої як аргумент. Може хтось, будь ласка, пояснить мені це краще? Навіщо нам це робити?


1
Згідно з документацією, "Цей метод найбільш корисний для приєднання обробників подій до елемента, де контекст вказує на інший об'єкт. Крім того, jQuery гарантує, що навіть якщо ви прив'язуєте функцію, повернуту з jQuery.proxy (), вона буде все-таки від’єднати правильну функцію, якщо вона передана оригіналу ". Чи є щось особливе у тому, що вам не вистачає?
bzlm

1
Це неясно , тут Крім того, JQuery переконується , що навіть якщо ви зв'яжете функція повертається з jQuery.proxy () він все одно буде відв'язати правильна функція, якщо він буде прийнятий оригінал ».Що мається на увазі під оригінал?
Адітья Шукла

Оригінал - це те, для чого було створено проксі. Але оскільки ви не повністю розумієте цей матеріал, ви впевнені, що вам потрібно його використовувати?
bzlm

1
Ось чудовий відео-посібник від nettuts, який показує, як працює $ .proxy. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Хусейн

1
@bzlm, я читав документацію jquery, коли я придумав цей метод.
Aditya Shukla

Відповіді:


381

Що в кінцевому підсумку це - це гарантувати, що значення thisфункції буде тією цінністю, яку ви бажаєте.

Поширений приклад - це setTimeoutте, що відбувається всередині clickобробника.

Прийняти це:

$('#myElement').click(function() {
        // In this function, "this" is our DOM element.
    $(this).addClass('aNewClass');
});

Намір досить простий. При myElementнатисканні класу він повинен отримати клас aNewClass. Всередині обробник thisпредставляє елемент, на який натиснули.

Але що робити, якщо ми хотіли короткої затримки перед додаванням класу? Ми можемо використовувати a setTimeoutдля його виконання, але біда полягає в тому, що яку б функцію ми не надавали setTimeout, значення thisвсередині цієї функції буде windowзамість нашого елемента.

$('#myElement').click(function() {
    setTimeout(function() {
          // Problem! In this function "this" is not our element!
        $(this).addClass('aNewClass');
    }, 1000);
});

Тож, що ми можемо зробити замість цього - це зателефонувати $.proxy() , передаючи йому функцію та значення, яке ми хочемо призначити this, і воно поверне функцію, яка збереже це значення.

$('#myElement').click(function() {
   // ------------------v--------give $.proxy our function,
    setTimeout($.proxy(function() {
        $(this).addClass('aNewClass');  // Now "this" is again our element
    }, this), 1000);
   // ---^--------------and tell it that we want our DOM element to be the
   //                      value of "this" in the function
});

Тож після того, як ми дали $.proxy() функцію і потрібне нам значення this, вона повернула функцію, яка забезпечить thisправильне встановлення.

Як це робиться? Він просто повертає анонімну функцію, яка викликає нашу функцію за допомогою .apply()методу, який дозволяє їй явно встановити значення this.

Спрощений погляд на функцію, що повертається, може виглядати так:

function() {
    // v--------func is the function we gave to $.proxy
    func.apply( ctx );
    // ----------^------ ctx is the value we wanted for "this" (our DOM element)
}

Таким чином, ця анонімна функція віддається setTimeout, і все, що вона робить, - це виконувати нашу оригінальну функцію з відповідним thisконтекстом.


Яка цінність використання, $.proxy(function () {...}, this)а не (function() {...}).call(this)? Чи є різниця?
Джастін Морган

11
@JustinMorgan: з .callтобою викликаєш функцію негайно. З $.proxy, це як би Function.prototype.bindтам, де він повертає нову функцію. Ця нова функція має thisзначення, незмінно пов'язане, так що коли вона буде передана setTimeoutта setTimeoutвикличе функцію пізніше, вона все одно матиме правильне thisзначення.
сірий стан приходить

2
Яка перевага цієї техніки, якщо така є, перед чимось подібним? $ ('# myElement'). click (функція () {var el = $ (це); setTimeout (функція () {el.addClass ('aNewClass');}, 1000);});
Грег

1
Вам не потрібно використовувати метод $ .proxy для цього прикладу. Замість цього ви можете просто переписати його так, як цей $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / новий контекст через змінну, оголошену в області обробника методом $ (that) .addClass ('aNewClass');}, 1000);});
Павло

4
Анонімний користувач, який має 112 тис. Реп., Страшно хороших знань JavaScript / jQuery, і його не бачили з жовтня 2011 року ... Можливо, Джон Резіг?
cantera

49

Не вникаючи в більш детальну інформацію (що було б необхідно, оскільки мова йде про контекст у ECMAScript, про цю змінну контексту тощо)

У ECMA- / Javascript є три різні типи "Контекстів":

  • Глобальний контекст
  • Контекст функції
  • евал контекст

Кожен код виконується в контексті його виконання . Є один глобальний контекст, і може бути багато випадків функціональних (і eval) контекстів. Тепер цікава частина:

Кожен виклик функції входить у контекст виконання функції. Контекст виконання функції виглядає так:

Це значення має
ланцюг об єктів активації

Отже, це значення є спеціальним об'єктом, який пов'язаний з контекстом виконання. У ECMA- / JavaScript є дві функції, які можуть змінювати це значення в контексті виконання функції:

.call()
.apply()

Якщо у нас є функція, foobar()ми можемо змінити це значення, зателефонувавши:

foobar.call({test: 5});

Тепер ми могли отримати доступ foobarдо об'єкта, який ми передавали:

function foobar() { 
    this.test // === 5
}

Це саме те, що jQuery.proxy()робить. Він займає a functionі context(що є не що інше, як об'єкт) і пов'язує функцію, викликаючи .call()або .apply()повертаючи цю нову функцію.


1
Відмінне пояснення, простіший / кращий, ніж офіційні документи jQuery для функції
higuaro

4

Я написав цю функцію:

function my_proxy (func,obj)
{
    if (typeof(func)!="function")
        return;

    // If obj is empty or another set another object 
    if (!obj) obj=this;

    return function () { return func.apply(obj,arguments); }
}

1

Та ж мета може бути досягнута з допомогою «Відразу-Викликається функція Expression, короткий: IIFE» функція , що виконується сама :

    $('#myElement').click(function() {  
      (function(el){
         setTimeout(function() {
              // Problem! In this function "this" is not our element!
            el.addClass('colorme');
        }, 1000);
      })($(this)); // self executing function   
    });
.colorme{
  color:red;
  font-size:20px;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

  <div id="myElement">Click me</div>
</body>
</html>


2
Зазвичай це називається "Виконання негайно викликаних функцій" (IIFE), а не "функція самовиконання", див. En.wikipedia.org/wiki/Immediately-invoked_function_expression .
Кріс Сід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.