Understanding callback.call function in jquery - javascript

Please go through the below code.
I am not able to exactly what's calllback.call is doing.
Also, I am not able to know what is the difference between this and this[i] and how if(callback.call(this, this[i])) is evaluated to true or false.
Array.prototype.each = function(callback) {
var i = 0;
while (i < this.length) {
callback.call(this, this[i]);
i++;
}
return this;
};
Array.prototype.map = function(callback) {
var i = this.length;
var found = []
while (i--) {
if(callback.call(this, this[i])) {
found.push(this[i]);
}
}
return found;
};
The functions are called below:
Array.each(function(value){
...
})
Array.map(function(value){
...
})

I am not able to exactly what's calllback.call is doing.
Function.prototype.call lets you invoke a function with an explicit value for this
callback.call(this, this[i]);
is the same as
callback(this[i])
except that inside the callback the value of this is the set to be the same as it was in the current context.
Also am not able to know what is the difference between this and this[i].
In this context, this is the current array. So this means the whole array, this[i] gets the i'th element of the array.
The .each function you have will loop through an array and call a function for each of them. This is similar to javascript's built in Array.prototype.forEach.
The .map is named as though it were a polyfill for Array.protoype.map but is actually doing a .filter operation. Strange.

Related

How to get reference to function in array if you know only the name of function?

lets say I have array of functions such as [ function f1{}, function f2{}] and I know that the function I want to get a reference too is called "f2", so that I can remove it from the array, how would I go about it? I tried using the object that contains an array like
ctrl.$parsers["f2"] but it does not return anything. Is this possible?
You could iterate over the array and inspect the function's name attribute:
function getNamedFunction(arr, name) {
for (var i = 0, l = arr.length; i < l; i++) {
if (arr[i].name === name) {
return arr[i];
}
}
}
But Function#name is not a standard property:
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.
A better (more compatible) approach would be to store your functions in an object:
var functions = {
'f1': f1,
'f2': f2,
};
and access it with
functions['f1']
You could use the filter method of the Array to select functions with a specific name
var myFunctions = [
function a(){ alert(1); },
function b(){ alert(2); },
function c(){ alert(3); }
];
var someName = 'c'; // name of function to search for
// filter array checking the name property of each function in the array
var someFunction = myFunctions.filter(function(f){
return f.name == someName;
})[0]; // get the first function from the resulting filtered array
someFunction(); // alerts '3'
Use the name prop and try this
for (var i=0;i<arr.length;i++)
{
if (arr[i].name=='f2')....
}
Later you can use splice to remove the element.
array.splice(index, 1); ( returns the same array instance)
p.s.
This feature is non-standard
So what can you do ?
you can do this :
var g=[function f2(){},function f4(){}];
for (var i=0;i<g.length;i++)
{
var m=g[i].toString().match(/function\s+(\w+)/i);
if (m && m[1]=='f4') alert(i);
}

How do I call a function stored in a variable that is of indeterminate depth

I looked at this:
Calling a JavaScript function named in a variable
But it doesn't answer my question.
This normally works:
window['class']['sub_class']['function_name'](data);
But now I'm trying to make a general function that can handle any depth:
function callbackFunction(callback, data){
//callback = a.b.c, or a.b, or a
callback = explode(callback);
//I need to be able to call callbackFunction and somehow callback and form their proper form below
window[callback.a](data);
//or
window[callback.a][callback.b](data);
//or
window[callback.a][callback.b][callback.c](data);
}
I believe the duplicate suggested by Bergi will only solve half of your problem. Since your final value will be a function, and since that function is a member of an object, you'll end up executing it in the wrong context (i.e., with the wrong this value).
I suggest you use something like this:
function getCallback(path) {
var arr = path.split('.');
var k;
var fn = window;
while(k = arr.shift()) {
if(typeof fn[k] === "function") {
fn = fn[k].bind(fn);
} else {
fn = fn[k];
}
}
if(typeof fn === "function") return fn;
return function(){};
}
http://jsfiddle.net/7CEd5/
Compare the value of this in the callback with what you get by using the answers to Convert string in dot notation to get the object reference.
You can chain references to objects/sub-objects/etc for however long you want. If you have a point-delimited string (e.g. "document.blah.blah2.method"), then you need to split it to individual tokens (e.g. ["document", "blah", "blah2", "method"]).
Then it's simply a matter of looping through the chain:
var c = window;
for (var i = 0; i < chain.length - 1; i++) {
c = c[chain[i]];
}
c[chain[chain.length-1]](some_arguments);

Working equivalent for method .some() in javascript or jquery?

Was looking for "equivalent for some method in javascript" and "return just one value if is in array", but saw only the answers to the way in which to determine the type of variables or too many unnecessary.
I bypass all inputs in html and i want something like this:
$('#goodsFilter')
.find('input[type="number"]')
.some(function(i,el){
return (isNumber($(el).val())) ? 1 : 0;
});
But it throws an error:
"TypeError: 'undefined' is not a function" (eg. Safari 6.0.4).
UPD: Error comes from the last line, yeah, where });.
isNumber:
function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
This should check for the presence of each input information, and, if at least one of them is not empty, return 1, otherwise 0.
How can I replace it to work in most modern browsers?
UPD:
Problem was solved. I'm a little confused in choosing the answer. The code of #RobG implementation of .some() is more understandable for beginners (and I am) so I switched my vote.
For anyone else who comes to this thread, you can use some() on a jQuery object this way:
$.makeArray($(...)).some(function(x) { ... })
jQuery.makeArray() converts the jQuery object into an Array, so you can use some() on it.
As suggested by #alf-eaton, you could use:
$(…).toArray().some(function(node) { … })
Array.prototype.some returns true or false, so you can do:
.some(function(el){
return !isNaN(el.value);
}
You don't say where the error comes from, is it from the call to isNumber?
Edit
Ah, so your issue is with some.
If you want a jQuery some method, then it should at least mimic the built–in ECMAScript some, which takes two arguments: a callback function and an optional this argument.
The callback function should take three arguments: the value, the index (optional) and an optional value to use as the this argument. It should access the numeric members in ascending order and only visit members that actually exist.
So it should be something like (noting that jQuery.fn === jQuery.prototype):
jQuery.fn.some = function(fn, thisArg) {
var result;
for (var i=0, iLen = this.length; i<iLen; i++) {
if (this.hasOwnProperty(i)) {
if (typeof thisArg == 'undefined') {
result = fn(this[i], i, this);
} else {
result = fn.call(thisArg, this[i], i, this);
}
if (result) return true;
}
}
return false;
}
So if you want now you can do:
var result = $('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
})? 1 : 0;
or you can do either of the following to coerce true to 1 and false to 0:
var result = Number($('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
}));
or
var result = +($('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
}));
The above is only lightly tested, the optional thisArg parameter might be redundant.
You could use the .filter method, and then check the length.
$('#goodsFilter')
.find('input[type="number"]')
.filter(function(i,el){ return isNumber($(el).val())); })
.length > 0
$(...).is(function) should work too. The jQuery API documentation states (emphasis mine):
Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
So using the example in the question, we would have something like:
var result = $('#goodsFilter')
.find('input[type="number"]')
.is(function(idx, el) {
return isNumber(el.value);
})? 1 : 0;
. . In the most basic version, you can just create a "some" function:
function eSome(arr, f) { var i = 0, n = arr.length;
for (;i<n;i++) { if (!i in arr) { continue }
if (f(i, arr[i])) { return true; }
} return false;
}
var list = [0, 1, 2, 3, 4, 5];
var testFunction = function (i, e) { return e === 2; };
console.log(eSome(list, testFunction));
//returns true and the loop ran only for the necessary three times.
. . If you want to chain the .some call in a jQuery object, you can add it as a jQuery function as well, using something like this (now tested and fixed) example:
jQuery.fn.some = function (f) { var i = 0, n = this.length;
for (;i<n;i++) { if (!i in this) { continue }
if (f(i, this[i])) { return true; }
}
return false;
}
$('.a').some(function (i, el) { return ($(el).text() == 'weeee!'); });
. . As #RobG pointed out in the comments, the native Array.prototype.some implementation calls your callback with a different set of parameters. I'm following the OP's sample code, but you can mimic the ECMA implementation's parameter with if (f(this[i], i, this)) { return true; } inside the loop.
. . You can also shim it on Array.prototype.some, but I strongly advise against any direct modifications to the built-in prototypes.
Implementation in Vanilla Javascript( with arrow syntax)
function some(arr,callback){
for(let i=0;i<arr.length;i++){
if(callback(arr[i],i,arr)){
return true;
}
}
return false;
}
some use:
check if an array has an even number:
function hasEvenNum(arr){
return arr.some(value=>{
return value % 2 === 0;
});
}
Try to use [].prototype.call method, the first argument will be an Array-like value, the second one is a function that will be called on each element.
[].some.call($('#goodsFilter').find('input[type="number"]'), function (el) {
return isNumber($(el).val());
});

How to catch last iteration inside $.each in jQuery?

var arr = {'a':fn1,'b':fn2,'c':fn3}
$.each(arr,function(name,func){
(do something particular for the last iteration)
...
})
It'll be best if no additional variables are used.
EDIT:
I mean LITERALLY last one,which is the last pair I type them.
Your example variable is called 'arr', but it's not an array at all (it's an object). This makes it a little confusing.
When iterating over an object, there's no such thing as a "last" property, because the order of properties is undefined by design.
When iterating over an array, you can simply compare the first parameter of the callback with the (array.length-1) to detect the last iteration.
In code (for arrays):
var arr = [ "a","b","c" ];
$.each(arr, function(i,val) { if (i == arr.length-1) ... });
Philippe Leybaert's answer outlines the problems with your question very well, and there is probably a clearer way of doing what you want. But that said, I cannot see a way to do what you ask without using an extra variable.
var obj = { 'a': fn1, 'b': fn2, 'c': fn3 };
var lastKey;
$.each(obj, function(key, fn) {
// do stuff...
lastKey = key;
});
obj[lastKey].doStuffForLastIteration();
If you need something to happen, say you are iterating over a single list and you wanted another object to be inserted conditionally but if the condition is not met you need it to be inserted last, you can do something like:
$list = $({{some_selector}});
$list_elt = $({{some_html}})
$list.each(function (i) {
if ({{condition is met}}) {
$(this).before($list_elt);
return false;
}
else if (i == $list.length - 1) {
$(this).after($list_elt);
return false;
}
});
which is the same thing as Philippe's solution, really. If there is some reason this should not work, please comment and let me know, because I use it.
Here I propose a brand new, improved answer.
An elegant way could be using a after() function wrapper. Here's the code:
function after(fn, times){
return function(){
if(--times === 0){
var args = Array.prototype.slice.call(arguments);
fn.apply(this, args);
}
};
}
fn is the function you want to be executed at last, times is the number of different response you are waiting for.
after() wraps your function and creates a new function that runs its code only after times calls. Here's an example in brief:
function onLastResponse(foo){
console.log('this is last iteration');
}
var myCallback = after(onLastResponse, 3);
myCallback(); //not executed
myCallback(); //not executed
myCallback(); //executed
Check this jsbin for a live example: https://jsbin.com/sufaqowumo/edit?js,console
Now that I have seen your duplicate question - where you state, "For the following,it's 'c':fn3" - it seems you might be after the value of the maximum property of an object.
var obj = { 'a': fn1, 'b': fn2, 'c': fn3 };
var maxKey;
for (var key in arr) {
if (!(maxKey > key)) {
maxKey = key;
}
}
// fn will be fn3
var fn = obj[maxKey];
Being jQuery.each function syncronous, do you really need to track last iteration? Just put your code after the $.each() call.

How can I extend Array.prototype.push()?

I'm trying to extend the Array.push method so that using push will trigger a callback method and then perform the normal array function.
I'm not quite sure how to do this, but here's some code I've been playing with unsuccessfully.
arr = [];
arr.push = function(data){
//callback method goes here
this = Array.push(data);
return this.length;
}
arr.push('test');
Since push allows more than one element to be pushed, I use the arguments variable below to let the real push method have all arguments.
This solution only affects the arr variable:
arr.push = function () {
//Do what you want here...
return Array.prototype.push.apply(this, arguments);
}
This solution affects all arrays. I do not recommend that you do that.
Array.prototype.push = (function() {
var original = Array.prototype.push;
return function() {
//Do what you want here.
return original.apply(this, arguments);
};
})();
First you need subclass Array:
ES6 (https://kangax.github.io/compat-table/es6/):
class SortedArray extends Array {
constructor(...args) {
super(...args);
}
push() {
return super.push(arguments);
}
}
ES5 (proto is almost deprecated, but it is the only solution for now):
function SortedArray() {
var arr = [];
arr.push.apply(arr, arguments);
arr.__proto__ = SortedArray.prototype;
return arr;
}
SortedArray.prototype = Object.create(Array.prototype);
SortedArray.prototype.push = function() {
this.arr.push(arguments);
};
Array.prototype.push was introduced in JavaScript 1.2. It is really as simple as this:
Array.prototype.push = function() {
for( var i = 0, l = arguments.length; i < l; i++ ) this[this.length] = arguments[i];
return this.length;
};
You could always add something in the front of that.
You could do it this way:
arr = []
arr.push = function(data) {
alert(data); //callback
return Array.prototype.push.call(this, data);
}
If you're in a situation without call, you could also go for this solution:
arr.push = function(data) {
alert(data); //callback
//While unlikely, someone may be using "psh" to store something important
//So we save it.
var saved = this.psh;
this.psh = Array.prototype.push;
var ret = this.psh(data);
this.psh = saved;
return ret;
}
While I'm telling you how to do it, you might be better served with using a different method that performs the callback and then just calls push on the array rather than overriding push. You may end up with some unexpected side effects. For instance, push appears to be varadic (takes a variable number of arguments, like printf), and using the above would break that.
You'd need to do mess with _Arguments() and _ArgumentsLength() to properly override this function. I highly suggest against this route.
Or you could use "arguments", and that'd work too. I still advise against taking this route though.
There's another, more native method to achieve this: Proxy
const target = [];
const handler = {
set: function(array, index, value) {
// Call callback function here
// The default behavior to store the value
array[index] = value;
// Indicate success
return true;
}
};
const proxyArray = new Proxy(target, handler);
I wanted to call a function after the object has been pushed to the array, so I did the following:
myArray.push = function() {
Array.prototype.push.apply(this, arguments);
myFunction();
return myArray.length;
};
function myFunction() {
for (var i = 0; i < myArray.length; i++) {
//doSomething;
}
}

Categories

Resources