jQuery like architecture - javascript

I'm trying to create a jQuery like architecture in my app but i could not get my expected results.
In jQuery "The jQuery object is actually just the init constructor 'enhanced'".
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context, rootjQuery );
},
That means when you initiate the jQuery object with;
$('selector')
jQuery returns the
new jQuery.fn.init( selector, context, rootjQuery );
jQuery prototype is defines as;
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
...
return jQuery.makeArray( selector, this ); // An array
}
...
size: function() {
return this.length;
},
...
all the usefull properties and methods of jQuery (like hide(), show() exct.) are hold by prototype of the jQuery object.
And the prototype of the init method assigns as the prototype of the jQuery;
jQuery.fn.init.prototype = jQuery.fn;
Well!. My problem is I tried to use this architecture but I could not get the properties and methods of the returned value.
Here is my code;
(function() {
Metinler = function(MetinKodu){
return new Metinler.sub.baslat( MetinKodu );
}
Metinler.sub = Metinler.prototype = {
metinKodlari: [],
constructor: Metinler,
topla: function(){
return this.metinKodlari[0] + this.metinKodlari[1];
},
baslat: function(MetinKodu) {
if($.isArray(MetinKodu) && MetinKodu.length > 0) {
this.metinKodlari = MetinKodu;
}else{
this.metinKodlari = (MetinKodu) ? [MetinKodu] : [''];
}
return this.metinKodlari;
}
}
Metinler.sub.baslat.prototype = Metinler.sub;
window.Metinler = Metinler;
})()
Code

The problem you are seeing, is that your constructor is returning a specific value:
return this.metinKodlari;
... which returns your internal array (well, you array of an array).
Remove this line and you'll return your baslat instance.

Your baslat function is faulty:
You misspelled length in MetinKodu.lenght > 0.
You're returning the metinKodlari array, whereas you should return the newly created this context. jQuery uses makeArray on this to store the passed DOM elements in the array-like jQuery object. In your case however, you simply want to store the passed elements in the metinKodlari array instead of on the this object itself.
This should work:
baslat: function(MetinKodu) {
if ($.isArray(MetinKodu) && MetinKodu.length > 0) {
this.metinKodlari = MetinKodu;
} else {
this.metinKodlari = (MetinKodu) ? [MetinKodu] : [''];
}
return this;
}

Related

How does jQuery clone its methods so quickly?

I am trying to return a query like object, in my first try I try the Object.create method
var ElementArray = {
someMethod : myMethod,
....
}
var addMethods = function(elements) {
var obj = Object.create(ElementArray);
obj[0] = elements;
return obj;
};
var getId = function( query ) {
return addMethods( doc.getElementById(query) );
};
(jsperf)
I immediately found that this was slower then jQuery(sizzle), especially on firefox. The issues with firefox where probably to do with cross-compartment wrappers (see bug here), but I was still quite dissatisfied.
I also tried using prototype instead
var ElementArray = function(){};
ElementArray.prototype.someMethod = someMethod;
....
var addMethods = function(elements) {
var obj = new ElementArray();
....
};
Slightly better on Chome, but still very slow on firefox.
So my question is, how does jQuery(sizzle), and other libraries do it || Whats the fastest way to return a object with a 1-2 instance properties? (Everything else can just be references)
So my question is, how does jQuery(sizzle), and other libraries do it
jQuery uses the prototype. It kind of hides that fact by aliasing the prototype to .fn, but it is still the prototype. Here's the jQuery function.
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
And, here's the aliasing:
jQuery.fn = jQuery.prototype
And, the implementation of the actual jQuery constructor:
init = jQuery.fn.init = function( selector, context, root ) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
root = root || rootjQuery;
.....
And, the round-about assignment of the `.init.prototype':
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

Returning element on function without losing prototype

Anybody know how to create a function that returns an element without losing its prototype?
I am trying to create function to create a new element and return it as an element. This function will have a method to manipulate that element. When I return that element, the prototype won't work. If I don't use return in that function, the prototype works properly, but the function returns the this object. Look at this code:
function ObjEl(tagName, id) {
'use strict';
this.node = document.createElement(tagName);
if (typeof id === 'string' && id.match(/^[a-zA-Z\d\_\-]+$/)) {
this.node.setAttribute('id', id);
}
// return this.node;
}
ObjEl.prototype.atr = function (key, val) {
'use strict';
this.node.setAttribute(key, val);
return this;
};
If I uncomment return this.node;, when I call test = ObjEl('div', 'test'); it's returning <div id="test"></div>, but this code won't work:
test = ObjEl('div', 'test').remove();
Seems wierd, but does this acheive what you're trying to do?
var ObjEl=function ObjEl(tagName, id) {
'use strict';
this.node = document.createElement(tagName);
if (typeof id === 'string' && id.match(/^[a-zA-Z\d\_\-]+$/)) {
this.node.setAttribute('id', id);
}
// return this.node;
}
ObjEl.prototype.atr = function (key, val) {
'use strict';
this.node.setAttribute(key, val);
return this;
};
var oldObjEl=ObjEl;
ObjEl=function (){
return new oldObjEl(arguments[0],arguments[1]);
}
Possible options
The two best ways (I know of) to achieve this kind of behaviour with JavaScript is to either create a wrapping object that manages your dealings with the element(s) in question internally i.e. something similar to jQuery (see below); or to extend each element DOM Node directly with your desired extras. e.g:
var extend = function( elm ){
elm.attr = extend.attr;
};
extend.attr = function(){
// your code here
};
var elm = extend(document.getElementById('div'));
You can also add to the prototype of certain element's source constructors, i.e. HTMLElement but this will not work cross-browser and may cause problems for other code. It is generally frowned upon to mess with base constructors.
The way I chose
In the past I have used the following rough snippet to combine a number of different interfaces — selectively — together into the same object. It is not seasoned code however, and could benefit greatly from proper setup and tear down management — to avoid the risk of memory leaks — because it is basically just a bundle of closures.
var harmony = function(){
if( !(this instanceof harmony) ) { return new harmony(); }
};
harmony.prototype.borrow = function( owner, descriptor ){
var i, dlen = descriptor.length, item;
for ( i=0; i<dlen; i++ ) {
if ( (item = owner[descriptor[i]]) && item.apply ){
this.borrowMethod( owner, descriptor[i] );
}
else {
this.borrowAttribute( owner, descriptor[i] );
}
}
return this;
};
harmony.prototype.add = function( owner, methods ){
for ( var i in methods ) { this.addMethod( owner, i, methods[i] ); }
return this;
};
harmony.prototype.addMethod = function( owner, name, method ){
this[name] = function(){ return method.apply( owner, arguments ); };
return this;
};
harmony.prototype.borrowMethod = function( owner, name ){
this[name] = function(){ return owner[name].apply( owner, arguments ); };
return this;
};
harmony.prototype.borrowAttribute = function( owner, attributeName ){
var self = this; self[attributeName] = function(){
if ( arguments.length ) { owner[attributeName] = arguments[0]; return self; }
return owner[attributeName];
};
return self;
};
Usage
The above can be used to wrap an element, borrowing methods from the native object, and extending with your own ones. Here's a rough idea of what I mean, this is by no means finalised code.
var ElementHarmony = function(elm){
var self = harmony()
.borrow( elm, ['innerHTML', 'getElementsByTagName'] )
.add( elm, {
getElement: function(){
return elm;
},
attr: function( name, value ){
if ( arguments.length > 1 ) {
this.setAttribute( name, value );
return self;
}
else {
return this.getAttribute( name );
}
}
})
;
return self;
}
/// create our example element and wrap with a harmony instance
var elm = ElementHarmony(document.createElement('span'))
.innerHTML('<b>hello</b>')
.attr('style', 'border: 1px solid red')
;
/// find the bold tag inside, and modify it's style
var bold = ElementHarmony(elm.getElementsByTagName('b')[0])
.attr('style', 'background: orange')
;
/// use getElement() to get back to the original element,
/// when passing to native methods.
(document.body||document.documentElement).appendChild(elm.getElement());
With the harmony object you can borrow both methods and attributes from an owner object, however the attributes are converted to methods i.e. innerHTML(). If you know that your code is only going to be executed on modern browsers you could change this behaviour to utilise getters and setters... but then, that is another story.
Hey diddle diddle
Here's the fiddle.
http://jsfiddle.net/K2Xux/

jQuery prototype

According to this StackOverflow answer What does jQuery.fn mean?, the fn property in jQuery.fn.jquery is an alias to the prototype property. I assume that this would be the same in these two methods whose full code is below
$.fn.map = function() and $.fn.tweets = function()
My question then, is, if, for example, $.fn.tweets uses the prototype to create a tweets method, would this code with $('tweets').tweets be calling it...
var $tweets = $('#tweets').tweets({
query: buildQuery(approxLocation),
template: '#tweet-template'
});
and, if so, how might it trigger that method. For example, does the mere creation of the variable on file loading trigger that function, which has other methods inside of it, namely query? Thanks for your help
Full code of methods
$.fn.map = function(method) {
console.trace();
console.log(method);
if (method == 'getInstance') {
console.log("fn.map");
return this.data('map');
}
return this.each(function() {
var $this = $(this);
var map = $this.data('map');
if (map && MyMap.prototype[method]) {
map[method] (Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
var options = method;
$this.data('map', new MyMap( this, options ));
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.map' );
}
});
}
$.fn.tweets = function(method) {
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tweets' );
}
}
variables that call those methods?
var $tweets = $('#tweets').tweets({
query: buildQuery(approxLocation),
template: '#tweet-template'
});
var $map = $('#map').map({
initialLocation: approxLocation,
radius: 1000,
locationChanged: function(location) {
$tweets.tweets('setQuery', buildQuery(location));
}
});
Firstly, prototypes are just objects. In this case, yes:
jQuery.prototype === jQuery.fn
So saying jQuery.fn.map = function() {} is like saying jQuery.prototype.map = function() {}
When you instantiate a new jquery object with $(selector | dom node | ...) you are returning a jQuery object which automatically inherits all the prototype methods, including map, tweet, etc. Research Javascript's prototypal inheritence model and how object prototypes work in regard to new
$ is just a reference to jQuery which returns a specially modified new object. $ is a function which returns a new object reference. Here's a simplified example (but you really should research more about prototypal inheritence, it has been answered many times repeatedly):
var A = function() {
};
A.prototype.doThing = function() {
};
var newObj = new A();
newObj.doThing // this new object has this method because it's on A's prototype
so newObj.doThing is just like $(selector).tweet
Also feel free to read the source of jQuery and trace what happens when a new object is created. You can see near the top exactly what is happening under the comment // Define a local copy of jQuery

attaching a class to a jQuery object

I'm struggling with how best to combine javascript Classes and jQuery plugins. This question isn't very specific, what I'm hoping for is pointers to more resources.
Basically, I want to store state data and private methods in a class, and then extend each jQuery object which I call my plugin on to have those private methods and properties. Such that inside the plugin I can call methods directly off the jQuery object.
I read jQuery plugin design pattern (common practice?) for dealing with private functions, specifically David's answer, however this initializes a new Class each time, and thus can't be used to save the state of the object.
I also found http://fuelyourcoding.com/jquery-plugin-design-patterns-part-i/, which recommends creating a class and then storing it in .data().
I think ideally what I want to end up with is code that looks like
(function( $ ){
var methods = {
init : function( options ) { // Initialize each object with a state and private methods },
show : function( ) {
// testFoo() is a private method that checks the element's state
if(this.testFoo()){
// Relying on jQuery's html() method
this.html() = this.fooTemplate();
}
}
};
// Boiler plate plugin from http://docs.jquery.com/Plugins/Authoring
$.fn.myPlugin = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.myPlugin' );
}
};
})( jQuery );
Finally, it doesn't seem like I can bake the private methods into the plugin directly because methods like "testFoo()" will return a boolean, and therefore aren't chainable.
Thoughts? Am I approaching this the right way? Is there another design pattern I should be using? Perhaps not using jQuery plugin architecture at all?
Here's a proposed solution. It combines few different approaches (John Resig's inheritance model and Alex Saxton's plugin inheritance model).
Define your inheritable plugin:
(function ($) {
My.Plugin = Class.extend({
/*
* Initialization (constructor)
*/
init: function (element, meta) {
var $meta = $.extend({ name: "pluginName" }, meta);
// Call the base constructor
this._super(element, $meta);
// TODO: Add custom initialization code like the following:
// this._testButton = $('.testButton', element).get(0);
},
/*
* Public methods
*/
show: function() {
alert('This is a public method');
},
/*
* Private methods
*/
// DEMO: Overriding the base _paint method:
_paint: function () {
// "this._super()" is available in all overridden methods
// and refers to the base method.
this._super();
alert('TODO: implement myPlugin._paint!');
}
});
// Declare this class as a jQuery plugin
$.plugin('my_plugin', My.Plugin);
})(jQuery);
Define Base class
(function () {
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function () { };
// Create a new Class that inherits from this class
Class.extend = function (prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] =
typeof prop[name] == "function"
&& typeof _super[name] == "function"
&& fnTest.test(prop[name])
? (function (name, fn) {
return function () {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name])
: prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
Plugin Creation
(function ($) {
// The "inheritance plugin" model
// [http://alexsexton.com/?p=51][1]
$.plugin = function (name, object) {
$.fn[name] = function (options) {
var instance = $.data(this, name, new object(this, options));
return instance;
};
};
})(jQuery);
Calling your plugin from javascript:
$('#someElem').my_plugin({options: {}, data: {} /* you can modify your plugin code to accept anything */}).show();
Note:
Private methods here are marked as _methodName. It's pure convention. If you really want to hide them, you can use module pattern (google for it or here's one for starters: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth)
Is this what you're looking for?

How to return an Array or an Element all from the same Object? (jQuery)

I am trying to cut down jQuery to a very specific set of functions to use in a product (as well as my general learning). Looking through the source, this seems to be the main structure behind the jQuery library. And it works fantastically.
The big part i can't get my head around, is how jQuery can return an element array, as well as retaining the jQuery object.
Ie, $("body") will return the body in an array, but i can still say $("body").hide() (so i'm essentially calling 'hide' on an array?)
The question: How can I return both an Array AND the jQuery object created in the first function?
var MyNewLibrary = function (selector, context) {
return new MyNewLibrary.fn.init(selector, context);
};
var $$ = MyNewLibrary;
MyNewLibrary.fn = MyNewLibrary.prototype =
{
el: null,
length: 0,
selector: "",
init: function (selector, context)
{
var elem;
elem = document.getElementById(
selector[0].substr(1, selector[0].length));
if (elem)
{
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
},
//Example of chained function
Html: function (str) {
if (typeof str == "string") {
this[0].innerHTML = str;
}
if (typeof str == "undefined") {
return this[0].innerHTML;
}
return this;
}
};
MyNewLibrary.fn.init.prototype = MyNewLibrary.fn;
MyNewLibrary.BasicFunction = MyNewLibrary.fn.BasicFunction = function ()
{
return "A string returned by $$.BasicFunction()";
};
An array is an object, and an object can have functions, so through transitivity, arrays can have functions. If there are multiple results, keep adding them to the newly created object of your library.
this[0] = element1;
this[1] = element2;
// and so on
It's not exactly an array but an array-like object, with a length property and corresponding numeric indexes. jQuery doesn't return an array either, which can be tested with
$("<some selector>") instanceof Array; // false
However, jQuery always returns this array-like object, even when selecting an element by id. In that case an array-like object (the jQuery object) is returned with a single element in it.

Categories