$("#title").click($("#content").toggle); $("#title").click(function() { $("#content").toggle(); }
第一个语句最终导致一个TypeError,当元素最终被点击时,消息“undefined不是一个函数”,我猜测说,无论我被分配给onclick回调最终都是未定义的,不知何故坚持记忆
解决方法很简单(只需使用第二种形式的语句),但是我真正想要了解的是为什么在切换函数最终被调用时,将toggle函数作为对象传递不起作用.我可以看到两者在语义上是不同的:第一个在绑定事件时执行$(“#content”)调用,另一个在事件发生时执行它,但是我不明白为什么这么重要.
解决方法
var $= function(selector,context) { // do stuff with selector etc }
这真的很简单,但是当您使用有效的选择器调用jQuery函数(如$())时,它会获取DOM节点并返回此类.
[ 0 : <div id="title"></div>,context : document,selector : "#title",jquery : "1.11.0",..... etc ]
这是jQuery返回的数组样对象,你可以看到0是本机的DOM节点,这就是为什么我们可以做$(‘#title’)[0]得到本机DOM节点的原因.
然而,有一个真正从一个简单的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
请注意,这些都是使用$.prototype添加到$()函数中的所有jQuery方法,但jQuery使用较短的名称$.fn,但它也是一样的.
所以我们知道的所有jQuery函数都被添加到主要的$()函数作为属性,而new关键字在内部使用返回一个新的实例的$()函数与这些原型属性,这就是为什么我们可以使用点符号,或者对于这个事情,括号符号和链接到$()函数的方法,像这样
$().find() // or $()[find]()
当使用这样的原型属性扩展对象时,它的值也在方法中设置,所以现在我们了解一下它的工作原理,我们可以重新创建一个非常简单的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的工作原理很多,但原则上它是一样的,当$()被调用时,它会检查它是否是一个自身的实例,否则它会使用新的关键字创建一个新的实例并再次调用一个新的实例.
当它是一个新的实例时,它会获取所需的元素和其他属性,并返回它们.
如果我们以某种方式对该实例进行原型化,那么我们可以像jQuery那样将其链接起来,所以让我们尝试一下
$.prototype.css = function(style,value) { this[0].style[style] = value; }
现在我们可以做到这一点
$('#title').css('color','red');
我们几乎创建了jQuery,只有10000行代码去.
注意我们如何使用这个[0]来获取元素,当我们使用像点击这样的东西时,我们不必在jQuery中这样做,我们可以使用这个,那么这样做是如何工作的?
让我们简化这一点,因为了解为什么问题中的代码不起作用至关重要
$.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()来设置回调函数中的值,所以我们不必使用这个[0],我们可以简单的使用这个.
现在这很酷,但现在我们不能再使用我们创建和原型化对象的任何其他方法,因为这不再是对象,它是DOM节点,所以这样做失败
$('#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节点,而不是jQuery对象.
所以最后回答问题.
这样做的原因…
$("#title").click(function() { $("#content").toggle(); });
…是因为你调用了toggle()函数,并且设置了正确的值,在这种情况下,它将是包含#content的jQuery对象作为toggle()没有使用bind()的回调,简单地传递jQuery对象,一个类似于我们可以在这个答案的顶部看到的对象
内部toggle()
$.prototype.toggle = function() { this.animate(); }
看看它如何直接使用它,除了调用另一个jQuery函数之外,还需要它是一个jQuery对象,而不是本机的DOM元素.
让我们重复一遍,toggle()要求在函数内部是一个jQuery对象,它不能是jQuery对象以外的任何东西.
–
现在让我们再次点击,再次点击
$("#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(),当被调用时,它将使用该集合的值被调用到点击函数中的本机DOM元素,它将像这样
$("#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()期待这是一个jQuery对象,而是它获取了点击处理程序中元素的本机DOM节点.
再次阅读,这里面的toggle()函数将是本机的#title元素,这甚至不是正确的元素,因为这是javascript和jQuery的工作原理,请参阅上面关于如何在原型方法中设置的长的解释等等