代理jQuery.init。尴尬的行为

时间:2013-08-17 02:30:29

标签: javascript jquery jquery-plugins extending

我正在尝试使用jQuery.fn.extend覆盖jQuery.fn.init我自己的实现,这将与原始实现的行为不同,但需要在某些地方调用原始实现来执行真正的工作。

因此,第一件事就是在新版本中实际“代理”原始实现。这就是我正在尝试做的事情,我正在观察看似尴尬的行为(这似乎是那些JS事情之一)。

您可以看到我的最新尝试here。所以问题是。它只应用于.parent .child的边界。不 .parent.child。这就是它现在似乎正在做的事情。如果您删除了我的jQuery.fn.extend电话。你可以看到原始的,正确的行为。

所以,问题是我做错了什么?这是代理任何JS函数,特别是jQuery init函数的正确方法吗?还有更好的方法吗?

附加

我看到了this old question,而那里的答案提到jQuery.sub现在已经moved to the jQuery Migrate plugin了。我应该尝试使用它吗?它比我现在正在尝试的更有可能工作吗?我真的需要吗?

1 个答案:

答案 0 :(得分:1)

注意:你真的不应该这样做,因为内部jQuery代码也会调用构造函数,如果你非常小心,这可能会带来非常不寻常的问题。

你的问题是你没有把oldInit称为构造函数 - 而是作为一个函数,它不会真正工作,因为jQuery.fn.init中设置的任何东西都会去在jQuery.fn上,而不是一个新的jQuery对象。

为什么不将ThisBinding设置为{}呢?

虽然在了解“新”操作符的工作方式后,这似乎有点直观,但这并不是实际做同样的事情。为什么呢?

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

使用new时,它还会为实例的__proto__提供构造函数的prototype对象。创建对象文字时,这是标准的Object.prototype,而不是预期的原型对象。

该怎么做?

如果您试图覆盖jQuery.fn.init,则需要使用以下内容:

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

这是如何运作的?

通过不带参数调用new oldInit,我们只会将__proto__设置为jQuery.fn的空对象取回,这正是我们想要的。然后,我们将向oldInit提供参数,并且任何参数将直接转到这个空的新对象,就像原始对象一样。

这是有效的,因为当你调用它时,你的实际ThisBinding(函数调用中的这个值)将已经是新的jQuery对象,因为你要引用this来添加新属性。 jQuery.fn.init构造函数的实例。

您的原始代码如下:

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

这会让你的构造函数认为新函数是jQuery.fn,这可能不是你想要的。 如果你有一个标准函数,那就可以了(因为这就是ThisBinding的意思),但是由于“new”运算符改变了ThisBinding,这不再意味着和以前一样