【问题标题】:javascript: How to make a module to behave like and object and a function simultaneously?javascript:如何使模块同时表现得像对象和函数?
【发布时间】:2011-06-01 04:32:03
【问题描述】:

我正在尝试为自己构建一个小助手库。首先,用于学习目的,然后我可以扩展它,以便在项目中派上用场。

我对原型引用、闭包和作用域有所了解。我还故意使用模块化模式来制作它,这样我的toolbox 就不会污染全局命名空间。 我也知道我们可以将原型分配给构造函数,因此构造的对象将保存这些方法。

这是toolbox.js的内容

(function (window) { 
var toolbox = (function () { 
    var toolbox = function(it){
            return new pick(it);
    }
    var pick = function (it){
        var craft = document.getElementsByTagName(it);
        craft = Array.prototype.slice.call(craft, 0);
        return Array.prototype.push.apply(this, craft);
    }

    pick.prototype = toolbox.prototype = {
        raw: function(){
            return Array.prototype.slice.call(this, 0);
        }, 
        tell: function(secret){
            return secret;
        }
    }

    return toolbox;
}());
window.toolbox = toolbox;
})(window);

并致电toolbox

toolbox("div"); //returns the desired object with the div collection
toolbox("div").raw(); //returns a raw array with the divs
toolbox().tell("A secret"); //returns "A secret"
toolbox.tell("It's a secret"); //type error: the function has no method like tell (like hell it does...should)

但是像这样修改上面的代码:

var toolbox = (function () { 
    var toolbox = function(it){
    return new pick(it);
}
     ...
    toolbox.tell(secret){ return secret }
return toolbox;
}());

会起作用。

所以我的问题是为什么toolbox.prototype = {} 不能解决问题,而pick.prototype = {} 会让pick 继承定义的方法?

我希望实现toolbox.tell("something");toolbox("div").raw(); 成为可能,而无需直接将方法原型化到模块中。

请帮忙!我已经在谷歌上搜索了好几天来学习这些,现在我被困住了。非常感谢您的帮助!

更新

简而言之,jQuery 是如何做到这一点的:

(function( window, undefined ) {
    var jQuery = (function() {
        // Define a local copy of jQuery
        var jQuery = function( selector, context ) {
                // The jQuery object is actually just the init constructor 'enhanced'
                return new jQuery.fn.init( selector, context, rootjQuery );
            }   

        jQuery.fn = jQuery.prototype = {
            constructor: jQuery,
            init: function( selector, context, rootjQuery ) {
                //init stuff
            }
        };

        // Give the init function the jQuery prototype for later instantiation
        jQuery.fn.init.prototype = jQuery.fn;

        jQuery.extend = jQuery.fn.extend = function() {
            //extend stuff
        };

        jQuery.extend({
            //extend stuff
        });

    // Expose jQuery to the global object
    return jQuery;

    })();
window.jQuery = window.$ = jQuery;
})(window);

【问题讨论】:

  • prototype 属性仅在构造函数中“应用”(当它成为新对象的[[prototype]] 时)。这里没有toolbox 的构造函数。考虑属性的手动副本:toolbox.tell = toolbox().tell
  • 可能想看看 jQuery 是如何做到这一点的。
  • @pst 感谢您的更正,我怀疑问题是工具箱没有构造函数。但是,我正在寻找一种使其自动化的解决方案。是的。像 jQuery。我可以编辑我的问题以添加一个 jQuery 示例。
  • 抱歉,我不得不编辑 jQuery 示例。数百行代码难以压缩。但它现在是一个恰当的例子。
  • 我不明白你对 toolbox() 的任何调用是如何工作的,因为你所有的 toolbox.js 代码都包装在一个匿名的自执行函数中(是的 - 我知道'self -executing' 不是一个完全准确的描述,但大多数人都知道是什么意思),那么这是否意味着在该函数中声明的内容对包含 toolbox.js 的页面上的代码是隐藏的?也许我只是累了。

标签: closures javascript prototype-programming scoping


【解决方案1】:

原型中的东西和对象本身的东西是不同的。

考虑以下示例:

var foo = function() { return 'I am foo'; }

foo.prototype.lie = function() { return 'I am not foo'; }
foo.lie(); //error, lie does not exist in foo

var bar = new foo;
bar.lie(); //it works!

prototype 基本上是父母和孩子之间的中间。原型拥有父级的所有内容,以及您附加到它的所有内容 - 但是,父级没有自己的原型。而且,更令人困惑的是,如果您将内容分配给父对象而不是其原型,则额外的内容不会被复制给子对象。很奇怪吧?

解释最后一条语句(使用前面的例子):

foo.makeAPoem = function() { return 'Roses are red, violets are blue, coffe milk eggs'; }
var bar = new foo;
foo.makeAPoem(); //works
bar.makeAPoem(); //nope!

这正是为什么你没有 String.split 的原因,但是 'abcd'.split - 这些方法存在于原型中。

如果您想让某些东西在对象和原型中都有效,请将其分配给两者!

foo.jedi = foo.prototype.jedi = function() { return 'This is not the function you are looking for'; }
foo.jedi(); //works
bar.jedi(); //works!

所以现在,您可以选择继承哪些内容以及保留在父项中的内容。如果您希望它仅对父级可用,则仅将其附加到父级。但是,如果您希望它仅在孩子上可用(例如,您没有 jQuery.andSelf,但您确实有 jQuery().andSelf)将其附加到原型中。如果两者都需要,请将其附加到两者。

【讨论】:

  • 哇,谢谢!你解释得很到位!这就是我一直想念的:)
【解决方案2】:

你可以定义一个幂构造函数(我不确定它真的是这样调用的=)),比如:

var toolbox = (function(){
 // some private methods
   var test_1 = function(){ alert("test_1"); };
   var test_2 = function(){ alert("test_1"); };
 // return needed methods and props into the global namespace: 
   return {
    "test_1" : test_1,
    "test_2" : test_2
   }
})();

toolbox.test_1();

【讨论】:

  • 电源构造函数。我喜欢!它类似于他们称之为“显示模式”的模式,它仅显示您在return{} 中选择的方法。谢谢,我会调查一下,你刚刚给了我一个想法。
【解决方案3】:

原因是 toolbox 从来没有像 pick 那样被实例化为构造函数。简单的解决方案。

代替

pick.prototype = toolbox.prototype = {
....
...
return toolbox;

只使用

pick.prototype = {
....
....
for(var attr in pick.prototype) {
  toolbox[attr] = pick.prototype[i];
}

这会将方法直接添加到工具箱中

【讨论】:

  • 是的,很好的洞察力! jQuery 使用jQuery.merge(); 以更复杂的方式做到这一点,他们(和插件编写者)使用它来扩展命名空间。我认为这完成了我所需要的,谢谢你的回答,希望我能投票!
  • 我的意思是jQuery.extend();。对不起。不过,你的想法很棒,谢谢!
猜你喜欢
  • 2010-10-27
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
相关资源
最近更新 更多