javascript: Как заставить модуль вести себя как объект и функция одновременно? - PullRequest
0 голосов
/ 01 июня 2011

Я пытаюсь создать себе небольшую вспомогательную библиотеку.во-первых, для целей обучения, а затем, чтобы потом я мог расширить его, чтобы он мог пригодиться в проектах.

Я немного понимаю ссылки на прототипы, замыкания и область видимости.Я также намеренно сделал это, используя модульный шаблон, поэтому мой 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);

Ответы [ 3 ]

1 голос
/ 01 июня 2011

Есть разница, когда что-то находится в прототипе и когда оно находится на самом объекте.

Рассмотрим следующий пример:

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), добавьте его к прототипу. Если вы хотите оба, добавьте их к обоим.

0 голосов
/ 01 июня 2011

Причина этого в том, что набор инструментов никогда не создавался как конструктор, как выборка.Простое решение.

Вместо

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

используйте просто

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

Это добавит методы непосредственно в панель инструментов

0 голосов
/ 01 июня 2011

вы можете определить конструктор power (я не уверен, что он действительно так называется =)), что-то вроде:

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();
...