Ответ 1
Функция jQuery, как в этом → $()
, является просто функцией, думайте об этом как
var $ = function(selector, context) {
// do stuff with selector etc
}
Это действительно упрощено, но когда вы вызываете функцию jQuery (как в $()
) с допустимым селектором, он получает DOM node и возвращает что-то вроде этого.
[
0 : <div id="title"></div>,
context : document,
selector : "#title",
jquery : "1.11.0",
.....
etc
]
это массив, похожий на объект jQuery, и, как вы можете видеть, 0
является родным DOM node, и это причина, по которой мы можем сделать $('#title')[0]
, чтобы получить собственный DOM node.
Однако есть что-то, что действительно невозможно увидеть из простого console.log
, и что методы, прототипированные на этот объект, похожий на массив, мы могли бы использовать цикл for..in
, чтобы увидеть их в консоли.
var title = $('#title');
for (var key in title)
console.log(key)
Это вернет длинный список всех прототипированных и не прототипированных методов, доступных на этом объекте
get
each
map
first
last
eq
extend
find
filter
not
is
has
closest
....
etc
Обратите внимание, что это все методы jQuery, добавленные в функцию $()
с помощью $.prototype
, но jQuery использует более короткое имя $.fn
, но делает то же самое.
Итак, все функции jQuery, которые мы знаем, добавлены к основной функции $()
в качестве свойств, а ключевое слово new
используется внутренне, чтобы вернуть новый экземпляр функции $()
с этими прототипированными свойствами, и поэтому мы можем использовать точечную нотацию или, в этом случае, обозначение и цепочку скобок по методам функции $()
, как этот
$().find()
// or
$()[find]()
Когда объекты расширены с помощью прототипированных свойств, подобных этому, в методах также устанавливается значение this
, поэтому теперь, когда мы немного понимаем, как это работает, мы можем воссоздать действительно простую версию jQuery
var $ = function(selector, context) {
if (this instanceof $) {
this.context = context || document;
this[0] = this.context.querySelector(selector);
return this;
}else{
return new $(selector, context);
}
}
Это упрощает много из того, как работает jQuery, но в принципе это то же самое, когда вызывается $()
, он проверяет, является ли он экземпляром самого себя, иначе он создает новый экземпляр с ключевое слово new
и снова вызывает себя как новый экземпляр.
Когда это новый экземпляр, он получает элемент и другие необходимые ему свойства и возвращает их.
Если бы мы использовали прототип метода для этого экземпляра, мы могли бы связать его так, как это делает jQuery, поэтому попробуйте
$.prototype.css = function(style, value) {
this[0].style[style] = value;
}
и теперь мы можем это сделать
$('#title').css('color', 'red');
мы почти создали jQuery, только 10000 строк кода.
Обратите внимание, как нам нужно использовать this[0]
для получения элемента, нам не нужно делать это в jQuery, когда мы используем что-то вроде щелчка, мы можем просто использовать this
, так как это работает?
Давайте также упростим это, так как важно понять, почему код в вопросе не работает
$.prototype.click = function(callback) {
var element = this[0]; // we still need [0] to get the element
element.addEventListener('click', callback.bind(element), false);
return this;
}
Мы использовали bind()
для установки значения this
внутри функции обратного вызова, поэтому нам не нужно использовать this[0]
, мы можем просто использовать this
.
Теперь, когда классный, но теперь мы больше не можем использовать какие-либо другие методы, которые мы создали и прототипировали для объекта, поскольку this
больше не является объектом, это DOM node, поэтому это не удается
$('#element').click(function() {
this.css('color', 'red'); // error, <div id="element".. has no css()
// however this would work, as we now have the DOM node
this.style.color = 'red';
});
Причина, по которой это не удается, состоит в том, что теперь у нас есть собственный DOM node, а не объект jQuery.
Итак, наконец, чтобы ответить на заданный вопрос.
Причина, по которой это работает...
$("#title").click(function() {
$("#content").toggle();
});
... заключается в том, что вы вызываете функцию toggle()
, и задано правильное значение this
, в этом случае это будет объект jQuery, содержащий #content
, поскольку toggle()
не имеет обратного вызова, который использует bind()
, он просто передает объект jQuery, объект, аналогичный тому, что мы видим в верхней части этого ответа.
Внутренне toggle()
делает
$.prototype.toggle = function() {
this.animate();
}
посмотрите, как он использует this
напрямую, не выполняя ничего, кроме вызова другой функции jQuery, требует, что this
- это объект jQuery, не собственный DOM элемент.
Давайте повторим, что toggle()
требует, чтобы this
внутри функции был объектом jQuery, он не может быть чем-то другим, кроме объекта jQuery.
-
Теперь вернемся к click
снова, когда вы делаете
$("#title").click(function() {
console.log(this)
});
консоль покажет собственный элемент DOM, что-то вроде <div id="title"></div>
Теперь мы можем ссылаться на именованную функцию вместо
$("#title").click(myClickHandler);
function myClickHandler() {
console.log(this)
});
и результат будет точно таким же, мы бы получили собственный DOM-элемент в консоли → <div id="title"></div>
, что не удивительно, так как это точно такое же, как и выше, с использованием анонимной функции.
Что вы делаете, ссылаясь на функцию toggle()
, такую как
$("#title").click($("#content").toggle);
Это точно так же, как в примере выше, но теперь вы ссылаетесь на toggle()
, а при вызове он будет вызываться со значением this
, установленным в собственный элемент DOM в функции click, это будет похоже на это
$("#title").click($("#content").toggle);
$.prototype.toggle = function() {
console.log(this); // would still be <div id="title"></div>
this.animate(); // fails as <div id="title"></div> has no animate()
}
Это то, что происходит, toggle()
ожидает, что this
будет объектом jQuery, но вместо этого он получает собственный DOM node для элемента в обработчике кликов.
Снова прочитайте, что this
внутри функции toggle()
будет нативным #title
элементом, что даже не является правильным элементом, как это работает с javascript и jQuery. длинное объяснение выше для того, как this
задано в прототипированных методах и т.д.