Понимание $ .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, когда подошел к этому методу.
Адитья Шукла

Ответы:

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контекстом.

user113716
источник
Какова ценность использования, $.proxy(function () {...}, this)а не (function() {...}).call(this)? Есть ли разница?
Джастин Морган
11
@JustinMorgan: с .callвами вызов функции немедленно. С помощью $.proxy, это как Function.prototype.bindгде он возвращает новую функцию. Эта новая функция имеет thisпостоянное значение, поэтому, когда она передается setTimeoutи setTimeoutвызывает функцию позднее, она все равно будет иметь правильное thisзначение.
серое состояние наступает
2
В чем преимущество этой техники, если она есть, перед чем-то вроде этого? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Грег
1
Вам не нужно использовать метод $ .proxy для этого примера. Вместо этого вы можете просто переписать его следующим образом: $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / новый контекст через переменную, объявленную в области действия метода-обработчика $ (that) .addClass ('aNewClass');}, 1000);});
Пол
4
Анонимный пользователь с репутацией 112 тыс., Страшно хорошим знанием JavaScript / jQuery, и его не видели с октября 2011 года ... Возможно, Джон Ресиг?
Кантера
49

Не вдаваясь в подробности (что было бы необходимо, поскольку речь идет о Context в ECMAScript, переменной this context и т. Д.)

В ECMA- / Javascript есть три разных типа «Контекстов»:

  • Глобальный контекст
  • Контекст функции
  • Eval Context

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

Каждый вызов функции входит в контекст выполнения функции. Контекст выполнения функции выглядит следующим образом:

Область
действия объекта активации Цепочка
этого значения

Таким образом, это значение является специальным объектом, который связан с контекстом выполнения. В ECMA- / Javascript есть две функции, которые могут изменять значение this в контексте выполнения функции:

.call()
.apply()

Если у нас есть функция, foobar()мы можем изменить значение this , вызвав:

foobar.call({test: 5});

Теперь мы можем получить доступ foobarк объекту, который мы передали:

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

Это именно то, что jQuery.proxy()делает. Он принимает functionи context(который является ничем иным, как объектом) и связывает функцию, вызывая .call()или .apply()и возвращает эту новую функцию.

JANDY
источник
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); }
}
sgv_test
источник
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 .
Крис Сид