【问题标题】:How to wrap the jQuery function如何包装jQuery函数
【发布时间】:2013-09-22 01:52:18
【问题描述】:

问题

我想知道在保留所有功能的同时包装 jQuery 函数的最佳方式。本质上,我想调用$('#someId'),但通过包装函数、修改参数并将其传递给原始的jQuery函数,让它像$('#' + id + 'someId')一样运行。

动机

我有一段 JS 将重用相同的变量 winId,它被连接并传递给 jQuery。而不是写

$('#' + winId + 'someId').html();
$('#' + winId + 'someOtherId').css();
...
$('#' + winId + 'someThirdId').text();

在整个文件中,我想包装 jQuery 函数,这样我就可以调用了

$('#someId').html();
$('#someOtherId').css();
...
$('#someThirdId').text();

并且在传递给$之前添加了winId


我的尝试

这是我作为包装器的想法:

(function() {
    var fn = $;

    return $ = function() {
       for ( var i = 0; i < arguments.length; i++ ) {
          if ( typeof arguments[i] == 'string') {
             arguments[i] = /* code to add in winId, omitted */
          }
       }
       return fn.apply( this, arguments );
    }
})();

这很好用,但显然没有像 $.ajax 这样的方法可用:

Uncaught TypeError: Object function () {
    for ( var i = 0; i < arguments.length; i++ ) {
       if ( typeof arguments[i] == 'string' ) {
          arguments[i] = /* code to add in winId, omitted */
       }
    }
    return fn.apply( this, arguments );
} has no method 'ajax' 

注意:我知道我可以使用 jQuery.extend($, jQuery) 复制对象,但如果可能的话,我对一个更优雅的解决方案感兴趣。

【问题讨论】:

  • 只是不要命名包装函数$?
  • 作为最后的手段,您可以改为通过exchanging $.fn.init 执行此操作。
  • @Bergi 我可以将包装器命名为 $ 以外的名称,但这不是我正在寻找的解决方案。但是,我认为包装 $.fn.init 是一个很好的解决方案,如果您能发布并回答以便我接受它,我将不胜感激。
  • 对我来说听起来像是 x-y 问题
  • 我不同意这是一个 x-y 问题,因为我的问题定义明确。我的“动机”仅作为一个示例用例提供;这个问题普遍适用且有价值。

标签: javascript jquery


【解决方案1】:

这是一个不同的实现:

DEMO

(jQuery.fn.init = (function (init) {

    return function (selector) {
        if (typeof selector === 'string' && selector[0] === '#') {
            arguments[0] = selector.replace('#', '#prefix_');
        }

        return init.apply(this, arguments);
    };

})(jQuery.fn.init)).prototype = jQuery.fn;


$(function () {
    console.log($('#test').length);
    console.log($.ajax);
});

编辑:后续问题:如何仅在闭包中应用它?例如,在一个对象内。

也许使用允许添加命名装饰器和删除它们的函数,例如:

HTML

<div id="prefix_test"></div>

JS

var decJQ = (function (decJQ, $) {
    var decorators = {},
        init = $.fn.init;

    ($.fn.init = function () {

        for (var k in decorators) {
            if (decorators.hasOwnProperty(k)) {
                arguments = decorators[k].apply(this, arguments);
            }
        }

        return init.apply(this, arguments);
    }).prototype = $.fn;

    return $.extend(decJQ, {
        decorate: function (name, fn) {
            decorators[name] = fn;
        },
        undecorate: function (name) {
            delete decorators[name];
        }
    });

})(window.decJQ || {}, jQuery);

decJQ.decorate('idPrefix', function (selector) {
    if (typeof selector === 'string' && selector[0] === '#') {
        arguments[0] = selector.replace('#', '#prefix_');
    }

    return arguments;
});

$(function () {
    console.log($('#test').length); //1

    decJQ.undecorate('idPrefix');

    console.log($('#test').length); //0
});

编辑 2: 你也可以选择一些非常简单的东西,例如:

(function ($) {
    //use $ which has been wrapped
})(function () {
    //do some manipulations
    return jQuery.apply(this, arguments);
});

【讨论】:

  • 这正是我所需要的。我不知道为什么我没有想到只覆盖 jQuery.prototype 初始化函数。
  • 你为什么不做init.apply(this, arguments)
  • 后续问题:如何仅在闭包中应用它?例如,在一个对象内。
  • @RickHanlonII:你不能,这是一项全球行动。这就是为什么我建议使用一个具有不同名称的简单包装函数,它可以是本地范围的。甚至可能隐藏 $ 标识符,但随后您将需要使用 jQuery 作为命名空间。
  • @Bergi,是的,这是个大问题,已修复。
【解决方案2】:

按照 Bergi 的建议和他链接到 here 的帖子,这是一种方法:

$.fn.extend({
    initCore: $.fn.init,
    init: function (selector, context, rootjQuery) {
        if (typeof selector === 'string' && selector[0] === '#') {
            selector = selector.replace('#', '#' + winId);
        }
        return $.fn.initCore(selector, context, rootjQuery);
    }
});

$.fn.init.prototype = $.fn;

我测试过$('#foo') 会找到一个以id 为前缀的div,例如&lt;div id="1foo"&gt;&lt;/div&gt;

例如:http://jsfiddle.net/MfdJS/1/

【讨论】:

  • 这很好,但我接受了@plalx,因为来自 jQuery 文档:“jQuery.fn.extend() 方法扩展了 jQuery 原型 ($.fn) 对象以提供 新方法 可以链接到 jQuery() 函数。"
  • 你可能不明白的是.init()来自jQuery.prototype链。 Martin 用 extend 覆盖了原来的 jQuery.fn.init 函数。 Plalx 做同样的事情,只是没有.extend()
  • 我了解 .init() 的来源。我只是不喜欢使用 .extend() 来 wrap 一个方法,而 jQuery 规范说它是提供 new 方法。 这是一个微妙的观点,但答案是基本相同,因此细微差别为我提示。
  • @JoeSimmons,但是它将initCore 作为垃圾添加到prototype
  • @plalx:我知道。这不是最好的解决方案,但可以实现与您相同的目标。
【解决方案3】:

将 class="winID" 添加到您的元素中。

使用 $(".winID").find('#someId").css(...) 访问特定元素的 CSS 属性。

使用 $(".winID").css(...) 访问所有带有 winID 标记的元素的 CSS 属性。

【讨论】:

    【解决方案4】:

    好吧,我刚刚测试过

    $('.con'+'tainer')
    

    $('d'+'iv');
    

    var s = 't';
    $('.con'+s+'ainer');
    

    控制台返回正确的值

    我相信您正在调用带有字符串参数的函数 jQuery(),因此只要您使用正常语法来构建/附加/构造带有加号的字符串,我认为您很出色。我很高兴你问了这个问题,因为我现在也知道了

    【讨论】:

    • 对不起,我看到你已经知道如何做到这一点,我想我不明白这个问题。如果我是你,我会构造像 var someID = stringBase + stringParameters 这样的字符串;所以你可以像 $(someID) 这样调用 jQuery
    • 你完全误解了这个问题。
    • 我实际上是在尝试将重复的串联抽象化,这样我只需要做一次,但它适用于范围内的所有调用。
    【解决方案5】:

    这是一件很奇怪的事情。为什么不直接为 winId 创建一个 CSS 选择器字符串并将其保存为变量?

    var foo = '#' + winId;
    

    现在你可以这样做了:

    $(foo + ', #bar').html("add some content"); 
    

    当他们使用 $('#bar') 并且它实际上选择了 #foo 和 #bar 时,你打算做的事情会让任何从事这个项目的程序员——包括六个月后的你——完全糊涂。

    【讨论】:

    • 你误解了这个问题。而且你的代码是错误的。您正在尝试选择一个“foo”元素。
    • 我可以这样做,但我想编写 $('#bar') 并将其应用为 $(foo +,', #bar') ,而不会每次都产生额外的开销。
    • @JoeSimmons 你是对的。我确实在语法上有一些错误。感谢您指出这一点。
    • 不,它不会让他们感到困惑,因为它只适用于特定函数的闭包内。
    • 很公平。有趣的问题。让我们知道您最终选择的解决方案。干杯--n
    猜你喜欢
    • 1970-01-01
    • 2015-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2012-04-23
    • 1970-01-01
    • 2014-07-15
    相关资源
    最近更新 更多