How does jQuery clone its methods so quickly? - javascript

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;

Related

Conventional jquery plugin vs AMD jquery plugin

I still can't get my head around with this AMD jquery plugin.
// UMD dance - https://github.com/umdjs/umd
!function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(root.jQuery);
}
}(this, function($) {
'use strict';
// Default options
var defaults = {
};
// Constructor, initialise everything you need here
var Plugin = function(element, options) {
this.element = element;
this.options = options;
};
// Plugin methods and shared properties
Plugin.prototype = {
// Reset constructor - http://goo.gl/EcWdiy
constructor: Plugin,
someMethod: function(options) {
return options;
}
};
// Create the jQuery plugin
$.fn.plugin = function(options) {
// Do a deep copy of the options - http://goo.gl/gOSSrg
options = $.extend(true, {}, defaults, options);
return this.each(function() {
var $this = $(this);
// Create a new instance for each element in the matched jQuery set
// Also save the instance so it can be accessed later to use methods/properties etc
// e.g.
// var instance = $('.element').data('plugin');
// instance.someMethod();
$this.data('plugin', new Plugin($this, options));
});
};
// Expose defaults and Constructor (allowing overriding of prototype methods for example)
$.fn.plugin.defaults = defaults;
$.fn.plugin.Plugin = Plugin;
});
Some tests,
console.log($('.some-element').plugin({
test: 'option1',
test2: 'option2'
}));
I always get this empty object,
Object[]
So how can I use this empty object?
And I want to access the method inside the plugin,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin',plugin);
console.log(instance); // an empty object again!
console.log(instance.someMethod("hello world"));
TypeError: instance.someMethod is not a function
console.log(instance.someMethod("hello world"));
So how should I do to run the method inside the plugin then?
It is so different from the conventional jquery plugin. AMD's is so difficult to understand. Any idea how I can get this AMD works like the conventional!??
EDIT:
Finally I got something,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world
Why was it so difficult for the those who comment and answer to point this out! Sigh!
In your test with .some-element you would get an empty jQuery object if .some-element matches nothing since $.fn.plugin returns what this.each(...) returns and the jQuery.each function returns exactly the same jQuery object as what it was called on.
As to how to get the instance, let's go through each step:
var plugin = $('.element').plugin();
The line above won't create a plugin if $('.element') matches nothing. One thing for sure, the value you assign to plugin is equal to $('.element').
var instance = $('.element').data('plugin',plugin);
In context, the line above is equivalent to var instance = $('.element').data('plugin', $('.element')); And the return value of jQuery.data(key, value) is the jQuery object itself, so that here instance will be equal to $('.element').
console.log(instance); // an empty object again!
That's what you'd get if $('.element') matches nothing.
console.log(instance.someMethod("hello world"));
This is definitely not going to work.
How to access the plugin instance is detailed in a comment in your code:
// var instance = $('.element').data('plugin');
// instance.someMethod();
Of course for this to work, $('.element') has to match a non-empty set of elements.
AMD is not a factor at all in your problems. All of the issues here have to do with how to use jQuery and how to write jQuery plugins.
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world

Which form of initialization is better?

When I first started writing my own code I never understand jQuery's 'enhanced' init constructor until later on so I stuck to a different way of constructing my objects. I was wondering if I should keep my old ways or start using my own 'enhanced' init constructor.
My Constructor:
var $ = function(selector,context,createObj) {
if(createObj) {
// actually initiating object
} else {
return new $(selector,context,true);
}
};
jQuery:
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
};
Actual Init:
init: function( selector, context, rootjQuery ) {
// some code
}
Changing prototype (jQuery.prototype.init.prototype=jQuery.prototype):
jQuery.fn.init.prototype = jQuery.fn;
jQuery's constructor pattern is historically grown and bad practise - or at least unnecessarily complicated. If you want a constructor that works well without new (if applied wrong), use
function $(selector, context) {
if (this instanceof $) {
// actually initiating object
} else {
return new $(selector, context);
}
}

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

jQuery like architecture

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;
}

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?

Categories