Writing JavaScript Functions - Should I Pass jQuery Objects as arguments? - javascript

I'm curious from both a performance and a "best practices" standpoint. I'm using a lot of jQuery in my JavaScript, and often find myself passing jQuery objects as arguments into functions I'm writing. Is it more efficient/effective to pass the selector string rather than the actual jQuery object as the argument? Is this just a stylistic difference, or are there any good reasons to use one method over the other?
Using jQuery objects in arguments:
function updateSelectOptions(optionsData, $selectElement) {
// function code
}
Or using a selector string as the argument:
function updateSelectOptions(optionsData, selectorString) {
var $selectElement = $(selectorString);
// function code
}

You should accept anything the jQuery constructor can for maximum flexibility.
Re-wrapping a jQuery collection doesn't blow up, so I often use something like...
var fn = function(elems) {
var $elems = $(elems);
};
That way, you can accept a jQuery collection, selector string or reference to native DOM element(s).

If you find yourself wanting to write a function that takes a jQuery object as a parameter, why not just make your function a jQuery plugin? It's pretty easy, and it makes using the function fit in with the rest of your jQuery code.
Instead of
function something(jq) {
jq.css("color", "red");
});
you'd write
$.fn.something = function something() {
this.each(function() {
$(this).css("color", "red");
});
return this;
};
Now when you want to turn something red, you can just say
$(".whatever").something();
The value of this in a jQuery plugin is the jQuery object that's being passed along the chain. You don't have to wrap it with $(this). Unless your function is something that returns some value, it's good to return whatever's passed in so that you can use your own plugin in the middle of a dotted chain.

In my opinion, passing the object is fine and would be better for performance.
Why?
Most of the time, the reason for using functions is to reuse code. Hence, if you pass the string (the selector) such as updateSelectOptions(optionsData, selectorString)
every time you call the function and then use that string to select the element:
var $selectElement = $(selectorString);
This will consume more memory, because the element will have to be searched for every function call.
Where if you pass the cached object this element will only be selected and searched for only once.

The second approach remove any reference to the object after the function finished to execute. The first one allow you to keep the reference to the object to manipulate it outside the scope of the function.

Related

What JavaScript concept allows for the same name to be both a function and an object?

I've been using JavaScript for a few years now in web design/development, and everything I know has been self-taught (I was a design major and hobbyist front-ender turned full web developer in pursuit of my career). With that background, I discovered something that I want to learn more about and have no idea what it is called, how it works, or that it may even be something extremely simple.
I've tried to search for more information about this (to prevent myself from needing to ask this), but it's difficult when I'm not sure what it's called that I'm even looking for...
I noticed in a few JavaScript libraries that I use that a variable name can be both a function and an object. For example, the jQuery library uses the name "jQuery". When logged using typeof jQuery it is a function, and typeof jQuery() is an object. What is interesting to me is that initial thought would suggest that jQuery() would be a function but it's actually an object.
//jQuery:
ƒ (a,b){return new r.fn.init(a,b)}
//jQuery():
r.fn.init {} //Expanding this reveals functions inside of __proto__ (which is obviously a clue, but I need more info)
When I try to do something like this I end up overwriting the name (as I would expect):
//Declare an object:
var planetEarth = {
'sky': 'blue',
'grass': 'green'
}
//Now overwrite the object as a function:
function planetEarth(){
console.log('inside of a function now');
}
This is understandable within the realm of JavaScript, so my question is how does a library like jQuery pull off having both at the same time?
Ultimately (using the above example) I would like to be able to do something like this:
planetEarth().sky; //"blue"
So my roundabout question is simply what is this called?
And a follow-up of where can I learn the basics of accomplishing this?
I've found resources on object-oriented JavaScript and classes, as well as prototypes, but I'm not sure if either (or both) of those concepts are what this is. All of the articles I've found aren't starting at the beginning and seem to always jump into unnecessarily complex examples. I'm not looking to make a giant library- I just want to get some more experience at the very basic level. Again, this could be embarrassingly simple and I've just never come across the concept before, so I appreciate being pointed in the right direction, thanks.
Every JavaScript function is also an object, just as every array is also an object. I don't think there is a special name for this, it's just how the language works.
You may be confusing two different things: what a function returns when you call it, vs. the function itself. Take your example:
planetEarth().sky; // "blue"
This code does not rely on the function planetEarth being an object and having any properties. You're calling the function, so to make this code work the function would need to return an object.
Because a function is an object, you can also set properties directly on the function itself. The code below uses both of these features. This version of planetEarth() returns an object with sky and grass properties, so it works as in your example: you call the function to get an object with those properties.
The code also sets an exists property directly on the function, and you can access that property without calling the function:
function planetEarth() {
// Return an object when this function is called
return {
sky: 'blue',
grass: 'green'
}
}
// Set a property directly on the function itself
planetEarth.exists = true;
// Test accessing the function's property
console.log( planetEarth.exists );
// Test accessing a property in the object that the function returns when called
console.log( planetEarth().sky );
jQuery makes use of both of these facilities. jQuery is a function that you can call. It returns a value commonly called a "jQuery object". That object has properties and methods, such as jQuery('#foo').show(). It also has "array-like" properties that you can access as you would any array, e.g. jQuery('#foo').length and jQuery('#foo')[0]. The jQuery function adds those properties to the value it returns.
At the same time, the jQuery library adds other properties and methods to the jQuery function itself. You access without calling the function, e.g. jQuery.ajax({...}).
A couple of other notes to help you understand the jQuery code. First, download the uncompressed version of jQuery. It looks like you are studying the compressed version, which has shortened names that won't make any sense.
Also, if you are wondering what jQuery.fn is, it is simply an alias for jQuery.prototype. Whenever you see jQuery.fn you can mentally substitute jQuery.prototype; learn how JavaScript prototypes work and you will understand jQuery.fn.
And now you may wonder why jQuery.fn exists at all. Why not use jQuery.prototype directly like other JavaScript code that uses prototypical inheritance? In fact, you could, and it would work the same as jQuery.fn. But the very first version of jQuery, back in 2006, didn't work this way. jQuery.fn was its own separate object, and every time you called the jQuery function, it copied all of the methods and properties from this object into the value it returned.
As you can guess, this was rather inefficient, so jQuery was changed to use .prototype so it could return an object that inherits all the methods such as .show() and .hide() instead of copying them all one by one. At the same time, jQuery.fn was kept around as an alias for jQuery.prototype to avoid breaking existing code.
This is a silent answer...
function f() {
return {};
}
console.log(typeof f, typeof f());
This is how jQuery does it. f is a function but when it gets called it returns an object.
Interesting part: function is also an object. f instanceof Function and f instanceof Object are both valid. So, you can call a variable as a function and / or assign some properties because it is also an object.
f.test = 123;
First-Class Objects
In Javascript, functions are first-class objects. This means that functions are just another kind of object. You can put a function in a variable, you can return a function, you can make an array of functions, and all that. Functions are objects.
Consider a slight change in your attempt:
//Declare a function:
function planetEarth(){
console.log('inside of a function now');
}
// Now add fields to it, since it is also an object
planetEarth.sky = 'blue';
planetEarth.grass = 'green';
// Test stuff
planetEarth(); // works
console.log(planetEarth.sky, planetEarth.grass); // works
You mention that you would like to use planetEarth().sky, but observe that while planetEarth is a function (and an object, as I said), planetEarth() is the result of calling planetEarth with no parameters. Therefore, whether you can or can't do planetEarth().sky does not depend on planetEarth as an object having the sky field, but rather depends on whatever you return from planetEarth having that field.
Bonus: did you know that functions can be declared very much like "normal" variables? See below:
// Both lines of code below are identical:
function myFunc() { ... }
var myFunc = function() { ... };
Perhaps looking at the code above will help you clear the confusion. The function is myFunc. myFunc() is simply the act of calling that function. If typeof myFunc() gives function, it is just a coincidence that the object that myFunc returned happened to also be a function.
jQuery is a function. Properties can be assigned to a defined function.
function planetEarth(options){
console.log('inside of a function now', options);
return window["planetEarth"];
}
var planetEarthOptions = {
'sky': 'blue',
'grass': 'green'
}
for (let prop in planetEarthOptions) {
planetEarth[prop] = planetEarthOptions[prop];
}
window["planetEarth"] = planetEarth;
planetEarth("selector");
console.log(planetEarth.sky);
console.log(planetEarth().grass);

what does wrapping a jQuery object with another `$` mean? [duplicate]

I am currently working through this tutorial: Getting Started with jQuery
For the two examples below:
$("#orderedlist").find("li").each(function (i) {
$(this).append(" BAM! " + i);
});
$("#reset").click(function () {
$("form").each(function () {
this.reset();
});
});
Notice in the first example, we use $(this) to append some text inside of each li element. In the second example we use this directly when resetting the form.
$(this) seems to be used a lot more often than this.
My guess is in the first example, $() is converting each li element into a jQuery object which understands the append() function whereas in the second example reset() can be called directly on the form.
Basically we need $() for special jQuery-only functions.
Is this correct?
Yes you only need $() when you're using jQuery. If you want jQuery's help to do DOM things just keep this in mind.
$(this)[0] === this
Basically every time you get a set of elements back jQuery turns it into a jQuery object. If you know you only have one result, it's going to be in the first element.
$("#myDiv")[0] === document.getElementById("myDiv");
And so on...
$() is the jQuery constructor function.
this is a reference to the DOM element of invocation.
So basically, in $(this), you are just passing the this in $() as a parameter so that you could call jQuery methods and functions.
Yes, you need $(this) for jQuery functions, but when you want to access basic javascript methods of the element that don't use jQuery, you can just use this.
When using jQuery, it is advised to use $(this) usually. But if you know (you should learn and know) the difference, sometimes it is more convenient and quicker to use just this. For instance:
$(".myCheckboxes").change(function(){
if(this.checked)
alert("checked");
});
is easier and purer than
$(".myCheckboxes").change(function(){
if($(this).is(":checked"))
alert("checked");
});
this is the element, $(this) is the jQuery object constructed with that element
$(".class").each(function(){
//the iterations current html element
//the classic JavaScript API is exposed here (such as .innerHTML and .appendChild)
var HTMLElement = this;
//the current HTML element is passed to the jQuery constructor
//the jQuery API is exposed here (such as .html() and .append())
var jQueryObject = $(this);
});
A deeper look
thisMDN is contained in an execution context
The scope refers to the current Execution ContextECMA. In order to understand this, it is important to understand the way execution contexts operate in JavaScript.
execution contexts bind this
When control enters an execution context (code is being executed in that scope) the environment for variables are setup (Lexical and Variable Environments - essentially this sets up an area for variables to enter which were already accessible, and an area for local variables to be stored), and the binding of this occurs.
jQuery binds this
Execution contexts form a logical stack. The result is that contexts deeper in the stack have access to previous variables, but their bindings may have been altered. Every time jQuery calls a callback function, it alters the this binding by using applyMDN.
callback.apply( obj[ i ] )//where obj[i] is the current element
The result of calling apply is that inside of jQuery callback functions, this refers to the current element being used by the callback function.
For example, in .each, the callback function commonly used allows for .each(function(index,element){/*scope*/}). In that scope, this == element is true.
jQuery callbacks use the apply function to bind the function being called with the current element. This element comes from the jQuery object's element array. Each jQuery object constructed contains an array of elements which match the selectorjQuery API that was used to instantiate the jQuery object.
$(selector) calls the jQuery function (remember that $ is a reference to jQuery, code: window.jQuery = window.$ = jQuery;). Internally, the jQuery function instantiates a function object. So while it may not be immediately obvious, using $() internally uses new jQuery(). Part of the construction of this jQuery object is to find all matches of the selector. The constructor will also accept html strings and elements. When you pass this to the jQuery constructor, you are passing the current element for a jQuery object to be constructed with. The jQuery object then contains an array-like structure of the DOM elements matching the selector (or just the single element in the case of this).
Once the jQuery object is constructed, the jQuery API is now exposed. When a jQuery api function is called, it will internally iterate over this array-like structure. For each item in the array, it calls the callback function for the api, binding the callback's this to the current element. This call can be seen in the code snippet above where obj is the array-like structure, and i is the iterator used for the position in the array of the current element.
Yeah, by using $(this), you enabled jQuery functionality for the object. By just using this, it only has generic Javascript functionality.
this reference a javascript object and $(this) used to encapsulate with jQuery.
Example =>
// Getting Name and modify css property of dom object through jQuery
var name = $(this).attr('name');
$(this).css('background-color','white')
// Getting form object and its data and work on..
this = document.getElementsByName("new_photo")[0]
formData = new FormData(this)
// Calling blur method on find input field with help of both as below
$(this).find('input[type=text]')[0].blur()
//Above is equivalent to
this = $(this).find('input[type=text]')[0]
this.blur()
//Find value of a text field with id "index-number"
this = document.getElementById("index-number");
this.value
or
this = $('#index-number');
$(this).val(); // Equivalent to $('#index-number').val()
$(this).css('color','#000000')

How to create a shorthand for 'this.getAttribute'

Correct me if I'm wrong. I'm not sure if shorthand is the correct word. But, it's the closest I can come up with:
For example:
this.getAttribute
I am using this a lot throughout one of my scripts, over 20 items.
Is it possible to just reference it like:
getA
or something? Instead of typing this.getAttribute over and over again? I know it seems likes a first world problem, but I am very OCD about my code and like to be minimal but still sustain readability. getA to reference this.getAttribute still makes a alot of sense (to me anyway), and in essence, shorter code. Any way to accomplish this?
if you call assign a method of an object to a var and call it by that, it the value of this inside the method will be the global context, as the function context is bound to where it got called from, so you can't do that.
However, you can use the built in Function method bind to preserve the context, but it's a rather expensive operation just to alias your method.
var getA = this.getAttribute.bind(this);
getA("yourAttribute");
or use call to enforce the context when you call it
var getA = this.getAttribute;
getA.call(this,"yourAttribute");
but both are not really great and in most cases all you need to do is to keep the actual return value in a var and reuse that, without invoking the getter at all. The getter is verbose because that's a good way to make an expensive method look expensive, and discourage people from calling it 20 times in a row in the first place. They usually should only be called when you expect a different result.
var a = this.getAttribute("yourAttribute");
When you say you call it 20 times in 20 different places I'm relatively sure those are not 20 places that expect the value to be different, especially when they are inside the same function scope.
If you are dealing with custom data-* attributes you can also just directly retrieve it from the .data property on your element, it's a map of all your data-attributes which is the ideal scenario.
<element data-your-attribute="yourData"></element>
var a = this.data.yourAttribute; //automatically updated and name converted from dash to camelCase
(all examples assume this refers to you element, and your function is a method of your element)
Yes, you can create a function like this:
function getA(element, attr) {
return element.getAttribute(attr);
}
And then, instead of this.getAttribute(attr), use getA(this, attr).
However, if you want to use it to get several attributes of the same element, and you want to avoid avoid passing this each time, you can use
var getA = this.getAttribute.bind(this);
And then, instead of this.getAttribute(attr), use getA(attr).
To sum it up:
#Adeneo says:
function getA(element, attr)
{
return element.getAttribute(attr);
}
I said:
Element.prototype.getA = Element.prototype.getAttribute
Which can be used as:
var attr = getA(document.getElementById("test"), "attr"); //Adeneo
var attr = document.getElementById("test").getA("attr"); //Mouser
I strongly advise against extending the Element object.
jQuery does the following:
They have their master function object $. That function has a method called attr. So the master function object finds an element on the page to which you can call the method attr.
$("element").attr("attr");
To reproduce:
var masterFunction = function(selector)
{
this.result = document.querySelectorAll(selector);
}
var selectElement = function(selector)
{
return new masterFunction(selector);
}
masterFunction.prototype.attr = function(attr)
{
var returnArray = [];
Array.prototype.forEach.call(this.result, function(element){
if (element.hasAttribute(attr))
{
returnArray.push(element.getAttribute(attr))
}
});
return returnArray;
}
Please remember that this, like jQuery returns an array containing the results.

using this rather than $(this) in an .each() call

I've done quite a lot of reading this past day to get a deeper understanding of this vs. $(this) and how JS determines this as it interprets, but I still can't figure out one detail of a plugIn I'm analyzing to deepen my knowledge:
$.fn.plugInName = function(options) {
return this.each(function() {
new $.plugInName(this,options);
});
};
Everything I've read indicates that although this.each() is used to call JQuery.prototype.each(), each object should be referred to as $(this) within the each() function, but the above uses regular ol' this, and I can't figure why. The $.plugInName declaration looks like this:
$.plugInName = function(el,options) {
...
}
Any insights anyone may have will be a big help.
EDIT: Here's the full source on GitHub
From MDN:
When a function is called as a method of an object, its this is set to
the object the method is called on.
When you call something like
$('#myDiv').plugInName()
the function is called as a method of the jQuery object $('#myDiv'). So, in this case, this already refers to a jQuery object, thus we don't need the extra $() wrapper.
A common case when we do need $() is when binding events.
$('#myDiv').on('click', function(){
alert('You clicked my div!');
});
The difference here is that the function is called not on $('#myDiv'), but the DOM element that it belongs to. So, here this is a DOM object, not a jQuery object.
Inside the callback function passed to each, this refers to the DOM element, it is not a jQuery object.. which is just what $.plugInName wants. It expects a DOM element.

Can someone help explain this jQuery code?

$('#AddMoreErrors,#AddMoreMime,#AddMoreRemote').live('click',function(){
var newRow = $(this).closest('tr');
$(this).closest('tr').after('<tr class="'+newRow.attr('class')+'">'+newRow.html()+'</tr>').find('span').removeAttr('id').addClass('removetr').html('Del');
});
I wrote the above jquery code and this is what it does: When someone clicks on "add more" it finds the nearest tr, replicates it into another tr and appends it to the first one. Then it finds all the spans, removes its id and adds a new class and changes the text.
Now what surprised me was find('span').removeAttr('id').addClass('removetr').html('Del');
I know I could do removeAttr on span but how did addClass and html also get applied on span?
jQuery returns object of element after each operation on which it is performed, and execution order are from left to right. So its .removeAttr('id') will be on span and that span object will be returned. Similarly .addClass('removetr') and .html('Del'); are performed.
Most jQuery methods return this, so:
find('span').removeAttr('id').addClass('removetr').html('Del');
Means the same as:
var foo = find('span');
foo.removeAttr('id');
foo.addClass('removetr')
foo.html('Del');
I'm not sure what you're asking, but if you want to know why you can chain jQuery commands one after another, that's because every jQuery function return "this". You can easily do the same when working with IIFEs:
function doSth() {
// Some logic
return this;
}
What actually happens the is, that you return the object after each method call and continue working on the same object you called the method from before.
That means that
$("#sel").method().method2().method3();
is exactly the same as writing
var sel = $("sel");
sel.method();
sel.method2();
sel.method3();
Only that it's shorter, looks nicer and you don't need to save the variable in case you don't need it again afterwards.
That way, you can write one function after another and you'll always have the complete jQuery function after the function is executed.
So you also see, why all three methods are executed on the same object: It's the way chaining works. By calling "$" you create a new object. And all methods are then executed on that object.
Unless of course, the function actually returns a value. Then, this won't work.
function getSth() {
return "value";
}
This is called method chaining. Each of those jQuery methods (removeAttr(), addClass(), html(val)) returns the jQuery object to which it belongs. This means you can call further methods using the dot notation.

Categories

Resources