Let's say I've created this plugin:
$.fn.my_namespace = function() {}
with level 1 sub functions:
$.fn.my_namespace.category_func = function() {}
and level 2 sub functions (actual application):
$.fn.my_namespace.category_func.app_func() {
alert(this);
alert(this.selector);
}
Execution:
$('div').my_namespace.category_func.app_func();
how can I now in my app_func retrieve the actual selector? In this case, 'this' seems to be the parent function (category_func) and not the jQuery object (selector).
How come? And how do I access the selector from app_func() ?
jQuerys .fn namespace is intended to hold functions which return a jQuery object / array of objects.
You can't just throw a new object in there and expect everything to work just like that.
I swear I've answered this before, but I can't seem to find it. this always refers to the object you are calling the method on. In this case you are using category_func as that object, and calling app_func().
The pattern that jQuery UI uses is one possible way to work around this issue. They allow you to call methods on a UI object by doing something like $elem.draggable('destroy');
Imagine for a moment:
$.fn.my_namespace = function(submethod, method) {
var args = [].slice.call(arguments, 1);
var func = $.fn.my_namespace[submethod];
if (func && method) {
if ($.isFunction(func[method])) {
args.shift(); // remove the method
func = func[method];
}
}
if ($.isFunction(func)) {
// using .apply() allows us to pass `this` along to our "method functions"
return func.apply(this, args);
} else {
// didn't find the method, return... or do something else...
console.log('my_namespace', this, arguments);
return this; // jQuery chaining default
}
}
$.fn.my_namespace.category_func = function() {
console.log('category_func', this, arguments);
return this;
}
$.fn.my_namespace.category_func.method_func = function() {
console.log('method_func', this, arguments);
return this;
}
$("body").my_namespace('category_func', 'method_func', 10);
//method_func jQuery(body) [10]
$("body").my_namespace('category_func', 10);
//category_func jQuery(body) [10]
$("body").my_namespace(10, 'slow');
//my_namespace jQuery(body) [10, "slow"]
Related
I was wondering how does JQuery use "$" both as a function to return a new instance and an instance itself.
I guess that it's not exactly the case but I mean, we can use $(element).method and for exemple $.ajax without brackets (.ajax will still be a method).
EDIT :
I think that I misspoke. I know how objects work in JavaScript and my question was not about that.
JQuery allows us to use $ without the key word new. It has a function that returns a new instance automatically. So my question was about how it can use $ both as a function to instanciate a new object and an object itself.
Let's say we have
(function() {
var jQ = function (arg){
this.own = arg;
};
jQ.prototype = {
foo : function (){
alert("Foo");
},
bar : function (){
alert("Bar");
}
};
window.jQ = window.$ = jQ;
return jQ;
}());
In this exemple, i have to go througth the key word new if I want to use my object.
So how does JQuery do to avoid us this step ?
Function is an object in javascript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function You can check this code:
var f = function () { alert(1); };
f.func1 = function () { alert(2); };
f.func2 = function () { alert(3); };
and you can call f(), f.func1() and so on...
It's not jQuery. In JavaScript functions are objects.
In the case of $(element).method you are passing a parameter element into the jQuery function, where with the $.ajaxcall you are calling the ajax function inside of the $ jQuery object. In both cases we are talking about the same piece of code, but we are using it two different ways.
Have a look at the raw source code for jQuery and that should help to visualize this a little: https://code.jquery.com/jquery-2.1.1.js
Note: the jQuery function that is used repeatedly is aliased at the bottom of the page.
Remember that in JavaScript, functions are objects. So, using the specific functions you called out in your question, you could create them like this:
var $ = function(selector) {
...
};
$.ajax = function(url) {
...
};
EDIT: To respond to your edited/clarified question, you don't have to use prototyping to make constructor functions in javascript. Remember, all a constructor is doing is returning an object - here's the equivalent of your prototyping code, but without having to use the new operator to instantiate the object:
(function() {
var jQ = function (arg){
return {
own: arg,
foo: function (){
alert("Foo");
},
bar: function (){
alert("Bar");
}
}
};
window.jQ = window.$ = jQ;
return jQ;
}());
I believe this style is actually preferred by Douglas Crockford because forgetting to use the new keyword won't throw an error but you'll get some very unexpected behavior.
JQuery allows us to use $ without the key word new. It has a function that returns a new instance automatically.
Nothing magical here. The jQuery function simply returns an instance of another constructor (source):
// Define a local copy of jQuery
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 );
},
The only magic going on in the code (not shown in the example) is that jQuery.fn.init.prototype = jQuery.prototype. But jQuery.fn.init is a different function than jQuery.
Applied to your example:
var jQ = function (arg){
return new jQ.prototype.init(arg);
};
jQ.prototype = {
init: function(arg) {
this.own = arg;
},
// ...
};
jQ.prototype.init.prototype = jQ.prototype;
How to write chainable functions but do not pollute $.fn ? Write functions only for using inside my plugin. Is it possible?
$('.myclass').makeSomething().andOneMoreFunction().andLast();
It is correct approach?
UPD.
The best solution in my case is extension method:
String.prototype.getMyLength = function(){return this.length;}
And now I can apply this function to any string like this:
var mystring = "test";
mystring.getMyLength();
Or
"teststring".getMyLength()
And make it chainable:
String.prototype.getMe = function(){return this;}
"string".getMe().getMe().getMe().getMe().getMe();
Thanks for answers!
You can chain all you want. If you define a $.fn yourself it is important that you return this at the end of you function.
If you want to write some javascript yourself you can also chain! It just depends on what you return. So if you return some other object, you can chain on from that object. The return value is used for this.
Example
var obj = {
test : function(){
alert("Y");
return this;
},
test2 : function(){
alert("2");
return this;
}
}
obj.test().test2(); // And so on since it returns this
jQuery Plugin API
$.fn.test = function(){
var methods = {
method0 : function(){
alert("method0");
return this;
}
};
return methods;
}
var api = $("obj").test(); // Returns methods
api.method0(); // Calling a function from the returned methods.
// OR
$("obj").test().method0();
Above function is not jQuery chainable anymore. So you can't use the $("obj").test().addClass("test") because you return your own API!
You can avoid pollution by using the first parameter of your plugin's function to specify the method of choice; for instance
(function () {
var o = { // object holding your methods
'bar': function () {console.log('bar', this); return this;},
'foobar': function () {console.log('foobar', this); return this;}
};
$.fn.foo = function (method /*, args*/) {
return o[method].apply(
this,
Array.prototype.slice.call(arguments, 1) // pass your args
);
};
}());
and then
$('something').foo('bar').foo('foobar');
/*
bar, thisobj
foobar, thisobj
*/
This way you keep access to the jQuery object as normal, too.
When you call a.foo(), the function foo is invoked with this set to a. You can use this to your advantage.
Also recall that the expression a.foo() evaluates to whatever you returnd from within the function.
So, just return this.
Then a.foo() evaluates back to a, and (a.foo()).bar() becomes equivalent to calling a.foo() then calling a.bar()... i.e. chained operations on a!
$.fn is not particularly magical — it simply uses the above logic in the same way that you are about to.
OK! First of all this question comes from a man who digs too deep (and posibly get lost) in the jQuery universe.
In my reserch I discovered the jquery's main pattern is something like this (If needed correction is wellcomed):
(function (window, undefined) {
jQuery = function (arg) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init(arg);
},
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function (selector, context, rootjQuery) {
// get the selected DOM el.
// and returns an array
},
method: function () {
doSomeThing();
return this;
},
method2: function () {
doSomeThing();
return this;,
method3: function () {
doSomeThing();
return this;
};
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function () {
//defines the extend method
};
// extends the jQuery function and adds some static methods
jQuery.extend({
method: function () {}
})
})
When $ initiates the jQuery.prototype.init initiates and returns an array of elements. But i could not understand how it adds the jQuery method like .css or .hide ,etc. to this array.
I get the static methods. But could not get how it returns and array of elements with all those methods.
I don't like that pattern either. They have an init function, which is the constructor of all jQuery instances - the jQuery function itself is just a wrapper around that object creation with new:
function jQuery(…) { return new init(…); }
Then, they add the methods of those instances to the init.prototype object. This object is exposed as an interface at jQuery.fn. Also, they set the prototype property of the jQuery function to that object - for those who don't use the fn property. Now you have
jQuery.prototype = jQuery.fn = […]init.prototype
But they also do two [weird] things:
overwriting the constructor property of the prototype object, setting it to the jQuery function
exposing the init function on jQuery.fn - its own prototype. This might allow Extending $.fn.init function, but is very confusing
I think they need/want to do all this to be fool-proof, but their code is a mess - starting with that object literal and assigning the init prototype things afterwards.
It’s easier to digest if you think of the API as an external collection of methods, and the jQuery function as the wrapper.
It’s basically constructed like this:
function a() { return new b();}
a.prototype.method = function() { return this; }
function b() {}
b.prototype = a.prototype;
Except that a is jQuery and b is jQuery.prototype.init.
I’m sure Resig had his reasons for placing the api constructor in the init prototype, but I can’t see them. A couple of more strangeness besides the ones Bergi mentioned:
1) The patterns requires a reference copy from jQuery.fn.init.prototype to jQuery.prototype, wich allows a weird endless loop:
var $body = new $.fn.init.prototype.init.prototype.init.prototype.init('body');
2) Every jQuery collection is actually an instance of jQuery.fn.init, but since they reference the same prototype object, it tricks us to "think" that the collection is an instance of jQuery. You can do the same sorcery like this:
function a(){}
function b(){}
a.prototype = b.prototype;
console.log( new b instanceof a); // true
console.log( new a instanceof b); // true
Sidenote: I have personally used the following constructor pattern with similar results without the weirdness:
var a = function(arg) {
if (!(this instanceof a)) {
return new a(arg);
}
};
a.prototype.method = function(){ return this; };
At first - sorry for my terrible english. I must practice it and will give my very best.
I'll try something new for me in javascript. i get the idea by jQuery libary. There are two
different ways to work with 'jQuery' or '$'.
jQuery(arg).foo(); // first way
jQuery.foo(); // second way
Now i wanted to do the same with an object.
obj(arg).foo();
obj.foo();
My first question was: How can jQuery be an function that returns an object and be an object in
the same way ?
obj(arg).foo();
seems like a function that returns an object.
But
obj.foo();
seems like an object.
I tried something to work with obj.foo() and obj().foo() but nothing worked - in any way i tried out something an error returned: foo() is undefined.
Do you know how jQuery solved it, to register the variable jQuery in this unnormaly way ?
The following is what i want to realize (the code doenst work!):
myClass = function () {
this.foo() {
window.alert('foo()!');
return this;
}
}
var myObj = new myClass();
function obj() {
return myObj.foo(arguments);
}
var obj = {
secondFoo : function () {
myObj.foo();
}
};
obj('arg').foo(); // alert(foo()!) && alert(foo()!)
obj.secondFoo(); // alert(foo()!)
obj('arg'); // alert(foo()!)
here is a little fiddle that display how jQuery does it (well not exactly but the core logic is there) http://jsfiddle.net/UD2Mv/1/
Basically jQuery always returned itself (actually not the jQuery object but an instance of jQuery.fn) unless the function call is expected to return something.
Because it return itself all the properties and methods are returned and are available as a chain. The trick that jQuery does is that it stores element inside this but by using integer keys like in an array.
Which is what I do in the example with this line
this[0] = document.getElementById(id);
This mean the element is now store and available to all methods that are part of this, so when I call colorMe this[0] gives me the targetted element.
Here is the code
function wrap(id) {
return new wrap.init(id);
};
wrap.init = function(id) {
this[0] = document.getElementById(id);
return this;
};
wrap.init.prototype = {
colorMe: function(color) {
this[0].style.color = color;
return this;
}
};
$(function() {
wrap('boo').colorMe('red');
});
In Javascript, all functions are also objects.
You can write
someFunction.someProperty = function() { ... };
someFunction.someProperty();
I mean object as in {} [object Object]. How does it do $(selector) and $.fn.init at the same time?
Can you give me a simple example please?
This isn't unique to jQuery, but an aspect of javascript. All functions are objects. E.g.:
var f = function() { alert('yo'); }
f.foo = "bar";
alert(f.foo); // alerts "bar"
f(); // alerts "yo"
Javascript is an object oriented language, so functions ARE objects, just fancy ones that you can call.
foo = function() { console.log("foo") }
foo.bar = function() { console.log("bar") }
foo() //=> prints "foo"
foo.bar() //=> prints "bar"
$ is a function.
a method of $ can return any thing.
For example:
$ = function() {
return {
foo : function() { return 'baa'; },
r1: 1,
r2 : 'string'
}
};
typeof $ <- function
typeof $() <- object
typeof $().foo <- function
typeof $().foo() <- string
typeof $().r1; <- number
typeof $().r2 <- string
jQuery or $ is an function(you know $ is an alias of jQuery).
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
In Javascript, everything is an object even function too. Hence You can directly add properties to function.
jQuery.find = function () {
}
It is an object.
$ holds different functions.
You can test this yourself by making your own object:
var $ = {
select: function(id){return document.getElementById(id);}
}
function $(id){
return $.select(id);
}
people play around with javascript functions and it leads to interesting design patterns.. Jquery uses of many of these patterns and creates a nice wrapper around many functions.. so ultimately jquery is like a static class using which one can do really neat stuff..
like all classes it has a name and the default Name is jQuery. The $ is nothing buy an identifier that is tied into the jQuery Library and stops you from having to type “jQuery” as the identifier.
The fact that it is a $ symbol is arbitrary. At some point a decision was made to use the $ symbol but the fact of the matter is that it could have been almost any type of ECMAScript acceptable identifier.
The main reason we use $ as the identifier is that you are less likely to make simple typo mistakes when typing one character instead of a string.
Hope that makes things clear.. please correct me guys if I hv got something wrong
A simple example of say my own library would be say a calculator class
var Calculator= (function()
{
function add(a,b)
{
return a+b;
}
function subtract(a,b)
{
return a-b;
}
function multiply()
{
return a*b;
}
function log()
{
console.log("log 123")
}
return
{
add: add,
subtract: subtract,
multiply: multiply
}
}());
Now I can perform operation using the Calculator class as follows:
Calculator.multiply(Calculator.add(2,3),5);
add my local functions are private and are not exposed to be used outside. In this case my log function can't be accessed using Calculator.log it will say method not found on object.
Now coming back to your question you could do something like this:
var _=Calculator;
and now use the calc functions like
_.multiply(_.add(2,3),5);
Interestingly there is a library called underscore too..
var q=function(){};
var s = function(){
alert("base");
window.s = s;
return new q()};
q.fn = q.prototype = {};
q.fn.x = s.x = function(){alert("x");return this;};
q.fn.y = s.y = function(){alert("y");return this;};
q.fn.z = s.z = function(){alert("z");return this;};
s().y().z().x();
s.z().x().y();
var s = function(){};
s.test = function(){console.log('inside s');}
s.test();
is perfectly legal code.