2013-11-02 2 views
3

Я пытается создать создать собственную небольшую jQuery-подобную библиотеку, но у меня очень тяжелое время с созданием этого шаблона цепочки. В основном у меня есть один класс с кучей методов, которые облегчают манипулирование документом. Вот примерJavascript: object return self a.k.a. chaining

function MF(selector){ 
    var DO; // Stands for DocumentObject 
    this.select = function(selector){ 
     return document.getElementById(selector); 
    } 
    if(typeof selector === 'string'){ 
     DO = this.select(selector); 
    }else if(selector instanceof HTMLElement){ 
     DO = selector; 
    } 

    this.children = function children(selector){ 
     return DO.getElementsByClassName(selector); 
    } 
    return { 
     MF: ??? 
    } 
}(null); 

Я мог бы быть неправильно в своих размышлениях, но то, что я пришел, чтобы выяснить, что для того, чтобы иметь дополнительные методы для объекта документа (HTML элемент) I необходимо либо продлить HTMLElement прототип или передать элемент вместе с моим классом. Я выбрал второй вариант. Я просто не могу понять, что вернуться в мой класс, чтобы я мог цеплять. То, что я просто стремиться, ради этого примера, чтобы иметь возможность написать следующую строку кода:

MF('someDiv').children('someClass'); 

В отчаянной попытке я попытался возвращающая новый экземпляр MF, который не должен иметь экземпляры, по умолчанию и привело к бесконечному циклу. Я действительно не могу понять, что я должен туда вернуться. Любая помощь очень ценится!

+0

Подсказка: 'MF ('someDiv')' должен возвращать объект и методы, которые не возвращают ничего другого (в отличие от '.children'), должны возвращать' this'. –

ответ

2

Похоже, что вы также пытались использовать шаблон модуля, и не воспользовались прототипов ..

Если вы хотите что-то цепи, вам необходимо либо вернуть себе (this) в связанные с цепью, или вернуть экземпляр new вашего Объект. В приведенном ниже примере я добился цепочки с использованием новых экземпляров (ну, детей выглядел так, как будто это был список, поэтому я сделал Array из них).

var MF = (function() { // module pattern start 
    function MF(selector) { 
     if (!(this instanceof MF)) return new MF(selector); // always construct 
     this.node = null; // expose your DO 
     if (typeof selector === 'string') { 
      this.node = document.getElementById(selector); 
     } else if (selector instanceof HTMLElement) { 
      this.node = selector; 
     } else { 
      throw new TypeError('Illegal invocation'); 
     } 
    } 
    MF.prototype = {}; // set up any inheritance 
    MF.prototype.select = function (selector) { 
     return new MF(document.getElementById(selector)); // returns new instance 
    }; 
    MF.prototype.children = function (selector) { 
     var MFs = [], 
      nodes = this.node.getElementsByClassName(selector), 
      i; 
     for (i = 0; i < nodes.length; ++i) { 
      MFs[i] = new MF(nodes[i]); 
     } 
     return MFs; // array of items of new instances 
    }; 
    return MF; // pass refence out 
}()); // module pattern end 

Тогда, к примеру, вы можете цепи, как ..

MF(document.body).children('answer')[0].children('post-text')[0].node; 
+0

Просто чистая удивительность. Единственное, что я не понимаю, в чем разница между использованием «прототипа» и определением функции как «this.function», потому что, очевидно, есть. –

+0

Я думаю, что было бы чище, если бы функция-конструктор просто вернула новый экземпляр, а не проверила бы «этот экземпляр MF» или нет. Что-то вроде функции 'MF (selector) {return new API (selector); } '. Вероятно, хорошая идея также разрешить 'MF (mfObject)' – plalx

+0

отличный ответ, upvoted! – dmp

2

return this; позволит получить доступ к методам конструктора. Сделайте это в самом низу конструктора и самом низу внутри каждого метода, который принадлежит ему, если методу не нужно возвращать другое значение.

function MF(selector){ 
    var doc = document; 
    this.select = function(selector){ 
    return doc.getElementById(selector); 
    } 
    // there are problems with some of your code 
    this.someMethod = function(){ 
    /* do stuff - If you want to access an Element then 
     var thisIsNowGlobal = this.select('someId'); 
     thisIsNowGlobal.innerHTML = 'someText'; 
     Note, the keyword this is global not var 
     If you wrote this.select('someId').innerHTML the property would not exist 

     When a property of an Object is assigned to a variable or argument 
     the this value changes to the global context. 
    */ 
    return this; 
    } 
    return this; 
} 
+1

Функции конструктора в JavaScript возвращают 'this' по умолчанию, если вы явно не возвращаете объект. Я думаю, вы хотели «вернуть это» из методов, которые не возвращают ничего другого. См. Мой комментарий выше: http://stackoverflow.com/questions/19747998/javascript-object-return-itself-aka-chaining#comment29344898_19747998 –

+0

Я не понимаю, если я верну 'this' в методе' select', как я должен захватить элемент HTML? –

+0

@php_nub_qq: вам нужно сделать что-то вроде jQuery и помещает их как свойство результата вызова 'MF'. –

1

Вы можете сделать это довольно легко. Имейте в виду, что когда вы return this, то если this имеет определенные на нем методы, вы можете их последовательно вызвать.

var MyUtilThing = function(){}; 
MyUtilThing.prototype.doStuff = function doStuff(){ // give it a name, helps in debugging 
    // do your thing 
    console.log('doing stuff'); 
    return this; // this is your instance of MyUtilThing 
} 

var thing = new MyUtilThing(); 
thing.doStuff().doStuff().doStuff(); // etc 

Один из способов создания явно экземпляров - это сделать это в вашем конструкторе.

var MyUtilThing = function(selector){ 
    var F = function(){}; 
    F.prototype = MyUtilThing.prototype; 
    var toReturn = new F(); 
    toReturn.initialize(selector); 
    return toReturn; 
}; 

MyUtilThing.prototype.initialize = function initialize(selector){ 
    this.selector = selector; 
}; 

MyUtilThing.prototype.doStuff = function doStuff(){ // give it a name, helps in debugging 
    // do your thing 
    console.log('doing stuff to', this.selector); 
    return this; // this is your instance created in the constructor (the blank function with the same prototype as MyUtilThing) 
} 

var thing = MyUtilThing('div'); // no use of new here! 
thing.doStuff().doStuff().doStuff(); // etc 

Но, попадая на немного тяжелую территорию. Лучшая ставка - просто попытаться понять, как именно this используется в JS, и вы пройдете долгий путь.

1

Традиционно способ jQuery позволяет связывать, создавая объект-обертку для каждого типа возвращаемого значения.Например, в вашем случае он платит, чтобы создать свою собственную оболочку для HTMLElement:

function HTMLElementWrapper(element) { 
    if (element instanceof HTMLElementWrapper) return element; 
    this.element = element; 
} 

Теперь, когда у вас есть HTMLElementWrapper вы можете реорганизовать MF функцию следующим образом:

function MF(selector) { 
    return new HTMLElementWrapper(typeof selector === "string" ? 
     document.getElementById(selector) : selector); 
} 

MF функция теперь возвращает HTMLElementWrapper объект, который имеет два метода select и children:

HTMLElementWrapper.prototype.select = function (selector) { 
    return new HTMLElementWrapper(this.element.getElementById(selector)); 
}; 

HTMLElementWrapper.prototype.children = function (selector) { 
    return new NodeListWrapper(this.element.getElementsByClassName(selector)); 
}; 

Ofcourse для функции children работать вам необходимо создать NodeListWrapper конструктор:

function NodeListWrapper(list) { 
    if (list instanceof NodeListWrapper) return list; 
    this.list = list; 
} 

Теперь вы можете методы цепи следующим образом:

MF("someDiv").select("something").children("someClass"); 

Для методов цепи после того, как .children("someClass") вам нужно добавить эти методы до NodeListWrapper.prototype.

+0

Большое спасибо за подробное объяснение. Итак, вы хотите сказать мне, что jquery имеет обертки для всех объектов DOM? 'O.o' –

+0

@php_nub_qq Я точно не знаю, что такое jQuery, но я знаю, что он обматывает HTML-элементы в пользовательских объектах. –