JQuery函数的持久性 [英] Persistence of JQuery Functions

查看:90
本文介绍了JQuery函数的持久性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为HTML设置一个点击回调,导致另一个节点变得可见。在此过程中,我惊讶地发现以下两个陈述不相同:

  $(#title) 。点击($( #内容)进行切换。); 
$(#title)。click(function(){
$(#content)。toggle();
}

第一个语句在最终点击元素时最终导致TypeError,并显示undefined is is a function的消息,我猜测它表示无论我被分配给onclick回调,结果都是未定义的,并且不知何故不会在内存中持续存在。



解决方法很简单(只需使用第二个表单的语句) ),但我真正想要理解的是为什么传递toggle函数作为一个对象在最终被调用时不起作用。我可以看到两者在语义上是不同的:第一个执行 $(#content)绑定事件时调用,另一个在事件发生时执行,但我不明白为什么这应该重要。



如果它与答案相关,则有问题的代码位于函数内部(可能是由函数返回的)用户可以单击任何内容。

解决方案

jQuery函数,就像在这个 - > $(),只是一个函数,把它想象成

  var $ = function(选择器,上下文){
//用选择器做的东西等等
}

这真的很简单,但当你调用 jQuery函数时(如 $()) )使用有效的选择器,它获取DOM节点并返回类似的内容。

  [
0:< div id =title>< / div>,
context:document,
selector:#title,
jquery:1.11.0 ,
.....

]

这是jQuery返回的类似数组的对象,你可以看到 0 是本机DOM节点,这就是我们可以做到的原因 $( '#title')[0] 获取原生DOM节点。



然而,从简单的 console.log 中可以看到一些东西,这就是那些原型化到类数组对象上的方法,我们然而,可以使用 for..in 循环在控制台中查看它们。

  var title = $('#title'); 

for(标题中的var键)
console.log(键)

FIDDLE



这将返回此对象上可用的所有原型和非原型方法的长列表

 获得
每个
地图
第一个
最后
eq
延伸
查找
过滤
不是


最接近
....

请注意,这些是使用 $ .prototype 添加到 $()函数的所有jQuery方法,但是jQuery使用较短的名称, $。fn ,但它也做同样的事情。



所以jQuery函数都是如此我们知道作为属性添加到主 $()函数中,并且 new 关键字在内部用于返回带有那些原型属性的 $()函数的新实例,这就是为什么我们可以使用dot no tation,或者用于 $()函数的方法的括号和链接,就像这样

  $()。find()
//或
$()[find]()

当使用像这样的prototyped属性扩展对象时,这个的值也会在方法中设置,所以现在我们了解它是如何工作的,我们可以重新创建一个非常简单的jQuery版本

  var $ = function(selector,context) ){
if(this instanceof $){

this.context = context ||文献;
this [0] = this.context.querySelector(selector);

返回此;

} else {

返回新的$(选择器,上下文);

}
}

这是简化 a很多来自jQuery的工作方式,但原则上它是一样的,当调用 $()时,它会检查它是否是它自己的一个实例,否则会创建一个使用 new 关键字的新实例再次将其自身调用为新实例。

新实例时,它获取它需要的元素和其他属性,然后返回它们。



如果我们要对该实例的方法进行原型化,我们可以像jQuery一样链接它,所以让我们试试

  $ .prototype.css = function(style,value){
this [0] .style [style] = value;
}

现在我们可以这样做

  $('#title')。css('color','red'); 

我们几乎创建了jQuery,只需10000行代码。



FIDDLE



注意我们如何使用 this [0] 来获取元素,我们不必在jQuery中这样做当我们使用点击这样的东西时,我们可以使用这个,那么它是如何工作的?



让我们简化一下同样,因为理解为什么问题中的代码不起作用至关重要

  $ .prototype.click = function(回调){
var element = this [0]; //我们仍然需要[0]来获取元素

element.addEventListener('click',callback.bind(element),false);
返回此;
}

我们在那里做的是使用 bind()在回调函数中设置这个的值,这样我们就不必使用这个[0] ,我们可以简单地使用这个



FIDDLE



现在很酷,但现在我们可以不再使用我们创建的任何其他方法并将其原型化为对象,因为不再是对象,它是DOM节点,因此失败

  $('#element')。click(function(){
this.css('color','red' ); //错误,< div id =element..没有css()

//然而这会起作用,因为我们现在有了DOM节点
this.style .color ='red';
});

它失败的原因是因为我们现在拥有本机DOM节点,而不是jQuery对象。 / p>

所以最后回答问题。

这个有效的原因......

  $(#title)。click(function(){
$(#content)。toggle();
});

...是因为你正在调用 toggle()函数,并且设置了这个的正确值,在这种情况下,它将是包含 #content as toggle()没有使用 bind()的回调,它只是传递jQuery对象,类似于我们在此答案顶部看到的对象



内部 toggle()是否

  $ .prototype.toggle = function(){
this.animate();
}

查看它如何使用除了调用另一个jQuery函数之外直接执行任何操作, 需要 是一个jQuery对象,原生DOM元素。



让我们再说一遍, toggle()要求<函数中的code> this 是一个jQuery对象,它不能是jQuery对象以外的任何东西。



-



现在让我们再次回到再次点击

  $(#title)。click(function(){
console.log(this)
});

控制台会显示本机DOM元素,类似于< div id =title>< / div>



现在我们可以引用命名函数

  $(#title)。click(myClickHandler); 

函数myClickHandler(){
console.log(this)
});

并且结果完全相同,我们将在控制台中获取原生DOM元素 - > < div id =title>< / div> ,这并不令人惊讶,因为这与上面使用匿名函数完全相同。



你正在做的是引用 toggle()这样的函数

  $(#title)。click($(#content)。toggle); 

这与上面的示例完全相同,但现在您引用 toggle(),当调用时,将调用的值将此设置为click函数中的本机DOM元素,它会像这样

  $(#title)。click($(#content) .toggle); 

$ .prototype.toggle = function(){
console.log(this); //仍然是< div id =title>< / div>

this.animate(); //失败为< div id =title>< / div>没有动画()
}

这就是发生的事情, toggle()期望这个成为一个jQuery对象,但它获取了click处理程序中元素的本机DOM节点。 / p>

toggle() $ c>函数将是本机 #title 元素,它甚至不是正确的元素,因为这是javascript和jQuery的工作原理,请参阅详细说明以上是如何在原型方法等中设置这个


I am trying to set up an on-click callback for an HTML that causes another node to become visible. Along the way, I was surprised to find out that the following two statements are not equivalent:

$("#title").click($("#content").toggle);
$("#title").click(function() {
    $("#content").toggle();
}

The first statement ultimately results in a TypeError when the element is finally clicked, with a message of "undefined is not a function," which I surmised to indicate that whatever I was assigned to the onclick callback ended up being undefined and somehow doesn't persist in memory.

The workaround is simple (just use the statement of the second form), but what I really want to understand is why passing the toggle function as an object doesn't work when it finally gets called. I can see that the two are semantically different: the first executes the $("#content") call when binding the event and the other executes it when the event occurs, but I don't understand why that should matter.

In case it is relevant to the answer, the code in question is located inside of a function (that has presumably returned by the time the user can click anything).

解决方案

The jQuery function, as in this -> $(), is just a function, think of it as

var $ = function(selector, context) {
   // do stuff with selector etc
}

That's really simplified, but when you're calling the jQuery function (as in $()) with a valid selector, it gets the DOM node and returns something like this.

[
    0        : <div id="title"></div>, 
    context  : document, 
    selector : "#title", 
    jquery   : "1.11.0",
    .....
    etc
]

this is the array-like object jQuery returns, and as you can see 0 is the native DOM node, and it's the reason we can do $('#title')[0] to get the native DOM node.

There is however something that one really can't see from a simple console.log, and that's the methods that are prototyped onto that array-like object, we could however use a for..in loop to see them in the console.

var title = $('#title');

for (var key in title) 
    console.log(key)

FIDDLE

This would return a long list of all the prototyped and non-prototyped methods available on this object

get
each
map
first
last
eq
extend
find
filter
not
is
has
closest
....
etc

Notice that these are all the jQuery methods added to the $() function with $.prototype, but jQuery uses a shorter name, $.fn, but it does the same thing.

So all the jQuery functions we know are added to the main $() function as properties, and the new keyword is used internally to return a new instance of the $() function with those prototyped properties, and that's why we can use dot notation, or for that matter bracket notation and chain on methods to the $() function, like this

$().find()
// or
$()[find]()

When objects are extended with prototyped properties like this, the value of this is also set inside the methods, so now that we understand a little bit about how it works, we can recreate a really simple jQuery version

var $ = function(selector, context) {
    if (this instanceof $) {

        this.context = context || document;
        this[0]      = this.context.querySelector(selector);

        return this;

    }else{

        return new $(selector, context);

    }
}

This is simplified a lot from how jQuery works, but in principle it's the same, when $() is called, it checks if it's an instance of itself, otherwise it creates a new instance with the new keyword and calls itself again as a new instance.
When it is a new instance, it gets the element and the other properties it needs, and returns those.

If we were to prototype on a method to that instance, we could chain it like jQuery does, so lets try that

$.prototype.css = function(style, value) {
    this[0].style[style] = value;
}

and now we can do this

$('#title').css('color', 'red');

we've almost created jQuery, only 10000 lines of code to go.

FIDDLE

Notice how we have to use this[0] to get the element, we don't have to do that in jQuery when we use something like click, we can just use this, so how does that work ?

Lets simplify that as well, as it's crucial to understand why the code in the question doesn't work

$.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;
}

What we did there was use bind() to set the value of this inside the callback function so we don't have to use this[0], we can simply use this.

FIDDLE

Now that's cool, but now we can no longer use any of the other methods we've created and prototyped to the object, as this is no longer the object, it's the DOM node, so this fails

 $('#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';
 });

The reason it fails is because we now have the native DOM node, and not the jQuery object.

So finally to answer the question asked.
The reason this works ...

$("#title").click(function() {
    $("#content").toggle();
});

... is because you're calling the toggle() function, and the correct value of this is set, in this case it would be the jQuery object containing #content as toggle() has no callback that uses bind(), it simply passes the jQuery object, an object similar to what we can see at the top of this answer

Internally toggle() does

$.prototype.toggle = function() {
    this.animate();
}

see how it uses this directly whithout doing anything other than calling another jQuery function, it requires that this is a jQuery object, not a native DOM element.

Lets repeat that, toggle() requires that this inside the function is a jQuery object, it can not be anything other than a jQuery object.

-

Now lets move on back to click again, when you do

$("#title").click(function() {
     console.log(this)
});

the console would show the native DOM element, something like <div id="title"></div>

Now we can reference a named function instead

$("#title").click(myClickHandler);

function myClickHandler() {
     console.log(this)
});

and the result would be exactly the same, we would get the native DOM element in the console -> <div id="title"></div>, which is not suprising as this is exactly the same as the one above using an anonymous function.

What you're doing is referencing the toggle() function like this

$("#title").click($("#content").toggle);

It's exactly the same as the example above, but now you're referencing toggle(), and when called it will be called with the value of this set to the native DOM element in the click function, it would go like this

    $("#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()
    }

This is what is happening, toggle() is expecting this to be a jQuery object, but instead it gets the native DOM node for the element in the click handler.

Read that again, this inside the toggle() function would be the native #title element, which isn't even the correct element, as that's how javascript and jQuery works, see the long explanation above for how this is set in the prototyped methods etc.

这篇关于JQuery函数的持久性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆