Понимание $.proxy() в jQuery

Из документов я понимаю, что .proxy() изменит область действия функции, переданной как аргумент. Может кто-нибудь, пожалуйста, объясните мне это лучше? Зачем нам это делать?

Ответ 1

В конечном итоге это гарантирует, что значение this в функции будет желаемым значением.

Общим примером является setTimeout, который выполняется внутри обработчика click.

Возьмите это:

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

Намерение достаточно простое. При нажатии myElement он должен получить класс aNewClass. Внутри обработчика this отображается элемент, который был нажат.

Но что, если мы хотим короткую задержку перед добавлением класса? Мы могли бы использовать 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.

Ответ 2

Не вдаваясь в подробности (что необходимо, потому что это примерно Контекст в ECMAScript, эту переменную контекста и т.д.)

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

  • Глобальный контекст
  • Функциональный контекст
  • контекст eval

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

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

Объект активации
Целевая группа
это значение

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

.call()
.apply()

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

foobar.call({test: 5});

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

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

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

Ответ 3

Я написал эту функцию:

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); }
}

Ответ 4

Та же цель может быть достигнута с помощью "Сразу вызываемого Function 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>