Понимание $.proxy() в jQuery
Из документов я понимаю, что .proxy()
изменил бы область действия функции, передаваемой в качестве аргумента. Может кто-нибудь объяснить мне это лучше? Почему мы должны это делать?
4 ответа
Что он в конечном итоге делает, так это гарантирует, что ценность 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
контекст.
Не вдаваясь в подробности (что было бы необходимо, поскольку речь идет о Context в ECMAScript, переменной this context и т. Д.)
В ECMA-/Javascript есть три разных типа "Контекстов":
- Глобальный контекст
- Контекст функции
- Eval Context
Каждый код выполняется в контексте выполнения. Существует один глобальный контекст, и может быть много экземпляров функциональных (и eval) контекстов. Теперь интересная часть:
Каждый вызов функции входит в контекст выполнения функции. Контекст выполнения функции выглядит так:
Объект Активации
Цепочка прицела
это значение
Таким образом, это значение является специальным объектом, который связан с контекстом выполнения. В ECMA-/Javascript есть две функции, которые могут изменять значение this в контексте выполнения функции:
.call()
.apply()
Если у нас есть функция foobar()
мы можем изменить это значение, вызвав:
foobar.call({test: 5});
Теперь мы можем получить доступ в foobar
объект, который мы передали:
function foobar() {
this.test // === 5
}
Это именно то, что jQuery.proxy()
делает. Требуется function
а также context
(который является ничем иным, как объектом) и связывает функцию, вызывая .call()
или же .apply()
и возвращает эту новую функцию.
Я написал эту функцию:
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); }
}
Та же цель может быть достигнута с помощью самозапускающейся функции:
$('#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>