سؤال

I'm trying to use jQuery.fn.extend to override jQuery.fn.init with my own implementation which is going to behave differently from the original implementation but is going to need to call the original implementation at certain places to do the real work.

So, the very first thing would be to actually "proxy" for the original implementation in the new one. That's what I'm attempting to do and I'm observing what seems like awkward behaviour (this seems like one of those JS things).

You can see my latest attempt here. So the problem is. It is only supposed to apply the border to the .parent .child. Not both .parent and .child. That's what it seems to be doing right now. If you remove my jQuery.fn.extend call. You can see the original, correct behavior.

So, the question is what am I doing wrong? Is this the right approach to proxying any JS function and specially the jQuery init function? Is there a better way?

Extra

I saw this old question and the answer there refers to jQuery.sub which has been moved to the jQuery Migrate plugin now. Should I try to use that? Is it any more likely to work than what I'm trying right now? Do I really need to?

هل كانت مفيدة؟

المحلول

Note: you really shouldn't be doing this, as internal jQuery code also calls the constructor, which could bring about very unusual issues if you aren't extremely careful.

Your problem is that you aren't calling oldInit as a constructor - rather as a function, which doesn't really work because anything set inside jQuery.fn.init will go on jQuery.fn, rather than a new jQuery object.

Why doesn't just setting the ThisBinding to {} work then?

Although this can seem somewhat intuitive after learning about the way the "new" operator works, this doesn't actually do the same thing. Why?

function Foo() { this.bar(); }
Foo.prototype.bar = function () { alert(1); };
new Foo; // 1 is alerted
Foo.apply({}); // TypeError: Object #<Object> has no method 'bar'

When new is used, it also gives the instance's __proto__ the constructor's prototype object. When you create an object literal, this is the standard Object.prototype, not the intended prototype object.

What to do then?

If you are attempting to override jQuery.fn.init, you need to use something like this:

var oldInit = jQuery.fn.init;
jQuery.fn.extend(
{ init: function ()
    {   return oldInit.apply(new oldInit, Array.prototype.slice.call(arguments));
    }
});

How does this work?

By calling new oldInit with no arguments, we'll just get back an empty object with __proto__ set to jQuery.fn, exactly what we want. Then, we'll supply the arguments to oldInit, and any arguments will go straight to this empty new object, just like the original.

This works because when you call it, your actual ThisBinding (the value of this inside the function call) will already be the new jQuery object, because you are meant to refer to this to add new properties to an instance of the jQuery.fn.init constructor.

Your original code was the following:

var oldInit = jQuery.fn.init;
jQuery.fn.extend({
    init: function () {
        return oldInit.apply(jQuery.fn, Array.prototype.slice.call(arguments));
    }
});

That would make your constructor think that the new function is meant to be jQuery.fn, which is probably not what you had intended. If you had a standard function, that would work (as that is what the ThisBinding is meant to be), however since the "new" operator changes the ThisBinding, this no longer means the same thing as before.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top