How can I get the index of a function stored in an array? The following code returns -1
var myArray = [ function(){console.log('fct1')} ];
myArray.indexOf( function(){console.log('fct1')} );
jsFiddle
More details:
I'm using jQuery to delegate events. Each event has one or more callback functions to call. It's impossible for me to know what the functions are since they are not pre-coded. Each callback function will be stored in an array. When a new callback function is added, I want to verify that it isn't already in the array, to avoid duplicates which would be both called by the event.
Any object in JavaScript will not be equal to something similar, except itself.
var func = function() {
console.log('fct1')
};
console.log(Object.prototype.toString.call(func));
# [object Function]
Since functions are also objects in JavaScript, you cannot search for a function object with another function object which does the same.
To be able to get a match, you need to use the same function object, like this
var func = function() {
console.log('fct1')
};
var myArray = [func];
console.log(myArray.indexOf(func));
# 0
This happens due to multiple references.
Each function you declared has a different reference and is not equal to the other.
That's why indexOf doesn't identify it.
Try this:
var func = function(){console.log('fct1')};
var myArray = [func];
alert(myArray.indexOf(func)); // will alert 0.
Fiddle
Related
I would like to understand the meaning of that code fragment. "saveTo" is a array, the programmer assigned a function() to the splice method. I don't understand what does it mean. Is that a override? What is the meaning of the return argument?, and why the function takes no argument while splice requires 2 or more arguments?
saveTo.splice = function() {
if (saveTo.length == 1) {
$("#send").prop("disabled", true);
}
return Array.prototype.splice.apply(this, arguments);
};
Javascript lets you re-assign methods at runtime. In this case, what the programmer was doing is reassigning splice on this specific instance of an array in order to call a jQuery method. Beyond that, it works in exactly the same way as the existing splice as they are calling return Array.prototype.splice.apply(this, arguments); - meaning that this method just passes on whatever arguments are passed to it.
Here's a demo:
var myArray = [1,2,3,4];
console.log("Splice before re-assing: ", myArray.splice(1,1));
// reset it.
myArray = [1,2,3,4];
myArray.splice = function(){
console.log("From inside new splice function");
return Array.prototype.splice.apply(this, arguments);
}
console.log("Splice after re-assiging: ", myArray.splice(1,1));
Whether this is a good thing to do is debatable. It breaks a few principles of programming.
The programmer that wrote this code knew that some other part of the program is calling splice on this array, and he wanted to attach an event to that, in order to update the user interface (hence the call to jQuery).
This is commonly called "Monkey Patching". You can read about it at https://www.audero.it/blog/2016/12/05/monkey-patching-javascript/
This is not a good pratice as it obfuscate what is happening: no programmer would expect that calling a data manipulation function has side-effects somewhere else.
You can run this sample to understand how it works:
const myArray = [];
// Patch push method only for this instance of array.
myArray.push = function() {
// log event
console.log('myArray.push was called with the following arguments', arguments);
// Call the original push function with the provided arguments.
return Array.prototype.push.apply(this, arguments);
}
myArray.push(1);
You can also patch methods for all instances of a given class:
// Patch push method on all arrays
const originalPush = Array.prototype.push;
Array.prototype.push = function() {
// log event
console.log('.push was called with the following arguments', arguments);
// Call the original push function with the provided arguments.
return originalPush.apply(this, arguments);
}
const myArray = [];
myArray.push(1);
As for your question about the arguments, in javascript all functions can access the arguments array-like object that contains the arguments the function was called with, which does not depend on which arguments are specified in the original declaration.
function doSomething(arg1) {
console.log(arguments[2]);
}
doSomething(1, 2, 3); // outputs "3"
Here is the MDN documentation about it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Note that there is a better way to extend arrays in ES6:
class CustomArray extends Array {
splice(...args) {
if(this.length === 1) {
$("#send").prop("disabled", true);
}
super.splice(...args);
}
}
Now that there are other ways to change the arrays length, .length, .pop, .shift, etc. so those should be overriden as well. However then it is still questionable wether the code calling those methods should not just cause the side effect.
What this does is it adds some checks for specifically saveTo.splice. If you call anyOtherArray.splice, then it'll just be evaluated as per normal. The reason it takes no arguments is because Array.prototype.splice takes arguments, and also the calling context of saveTo, as well as the array-like objects arguments, representing all the arguments passed to saveTo.splice. So it's just adding a little bit of extra code based on a specific condition - other than that, there's no difference to the native splice.
1) Yes, the programmer has overridden splice method, its not recommended
2) return statement is nothing but calls Array.prototype.splice(the original method).
3) Yes, splice requires arguments, but in JS, you may not define them as function params. You get the passed parameters as an array like object arguments inside your functions,
if you look closely, they call Array.prototype.splice with this and arguments object.
Okay, let's dissect this piece by piece.
saveTo.splice = function() {
if (saveTo.length == 1) {
$("#send").prop("disabled", true);
}
return Array.prototype.splice.apply(this, arguments);
};
As we all know that in JavaScript functions are first class objects, so if we have an object let's say saveTo something like this:
const saveTo = {};
Then we can assign a function to one of its properties like :
saveTo.splice = function() {
};
or something like this to:
const saveTo = {
splice: function() {
}
};
With that out of the way, you are just calling the Array#prototype#splice method to create a shallow copy out of the array and passing it an iterable to it.
So in total you have overridden the native Array#prototype#splice to fit your requirement.
Let's say I have a function
var addOneToArray = function(arr) {
return arr.push(1);
};
If arr is always going to be the same in the program (let's say it's always myArray), does it make sense to do this instead:
var addOneToArray = function() {
return myArray.push(1);
};
What I'm wondering is, is there any added value to doing the latter in terms of speed or something else? Or is it better to have a more generic function, that maybe gets reused?
If you have the only method that needs to executed then it would be fine. But when you have to implement multiple methods related to same object (in our case Array object) it is always fine to have own constructor for the same.
You can use something like,
var customArray = function() {}
customArray.prototype = Array.prototype; // Make it to behave like Array
Now, customArray is same as the Array class. You can add your method as
customArray.prototype.addOneToArray = function() {
this.push(1);
};
How you can use it,
var arr = new customArray();
arr.addOneToArray();
I am writing a Javascript function to count the number of instances of an element in an unsorted array. It has a method signature like this
Array.prototype.numberOfOccurrences = function() {
}
Here is an example of expected behavior
var arr = [4, 0, 4];
Test.assertEquals(arr.numberOfOccurrences(4), 2);
My problem is that I don't know how to access the elements in the array. The function doesn't take any parameters so how do I reference the array being passed in?
Note: The instructions aren't very descriptive for this kata on code wars and adding a parameter to the function returns some error unexpected token.
Inside the function you are creating into the Array.prototype you can access all the prototype functions through the "this" keyword.
Meaning you can access the array items using numeric properties like this[0] or this[1] to a access the first and second item respectively.
You can also call functions which allows you to iterate over each item on the array, such as: forEach, filter, etc.
Please refer this page to see everything you can do with the array prototype:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype
Lastly don't forget that the JavaScript implementation varies on each browser, so a function that works on Chrome, might not work on InternetExplorer, always confirm on caniuse.com If the function you are used has the same implementation on your targets browsers.
Cheers.
Whether you should extend javascript base objects aside, this is your friend:
Array.prototype.numberOfOccurrences = function(valueToFind) {
return this.filter(function(item) {
return item === valueToFind;
}).length;
}
var a = [1,2,3,3,3,3];
console.log(a.numberOfOccurrences(3)); //4
As noted above, if you're not able to change the function signature for whatever reason you can specify it as follows:
Array.prototype.numberOfOccurrences = function() {
var valueToFind = arguments[0];
...
}
I would recommend adding the parameter to the function for clarities sake. Seems counter intuitive for a function like numberOfOccurences to not take in a parameter - numberOfOccurences of what?
Fiddle: http://jsfiddle.net/KyleMuir/g82b3f98/
You might try using the locally available variable 'arguments' inside of the function. So for example, your code might look like thsi:
Array.prototype.numberOfOccurrences = function() {
var args = arguments || {};
var testArray, testCheck;
if (args[0] && Array.isArray(args[0]) {
// do something with the array that was the first argument, like:
testArray = args[0];
testCheck = testArray.indexOf(args[1]);
return testCheck;
} else {
// do what you want to do if the function doesn't receive any arguments or the first argument
// received isn't an array.
}
}
'arguments' is always available to you inside a declared function.
I build a objects source in JavaScript.
Is there any way to call some methods in one line like this:
var x = new object("aaa").method_a().method_b().method_c();
If you want to chain function call's you need to return this from your functions
function method_a(){
// do something
return this;
}
Same for other functions -
then you can do var x = new object("aaa").method_a().method_b().method_c();
The way to do that is making each method to return the object itself. For example:
function Person() {};
Person.prototype.setName=function(n){
this.name=n;
return this;
}
Person.prototype.setAge=function(a) {
this.age=a;
return this;
}
var p= new Person().setName("John").setAge(20);
The obvious gotcha is you cannot do that if the method has to return any other value (you can do it with setters but not with getters)
If your object doesn't support a fluid interface you can always wrap that functionality on top of it:
function FluidWrapper(obj)
{
var o = {};
for (var p in obj) {
if (typeof obj[p] == 'function') {
o[p] = function(method) {
return function() {
obj[method].apply(obj, [].slice.call(arguments, 0));
return o;
};
}(p);
}
}
return o;
}
var x = new object("aaa");
FluidWrapper(x).method_a().method_b().method_c();
Demo
While Mohammad Adil's answer is the most common scenario, i feel that the possibilities haven't been explored properly.
A function returns a value. In JavaScript you can call methods on any value except null and undefined. This means that this is perfectly acceptable:
var x = 987654321;
var y = x.toString().split('').sort().join('0');
In this scenario,
the toString() method was called on a Number who's internal value is 987654321 and returns a string.
the split('') method was called on a String who's internal value is '987654321' and returns an Array.
the sort() method was called on an Array holding the following values:['9','8','7','6','5','4','3','2','1'] and returns the same Array (but sorted).
The join('0') method was called on the same Array, but holding the values ['1','2','3','4','5','6','7','8','9'] and returns a string.
finally, after all these operations, y contains the value '10203040506070809';
So it is not necessary for the object the chained methods act on to be the same, as long as you are aware at every step of what that object is.
When you have a method called on an object, inside that method this will refer to the object. So if you return this;, then another method of that object can be called afterwards.
It is important to note that sometimes you want to return a new object of the same type rather than change the object and return it. Both work equally well when chaining, but the results are different when not. Consider the following jQuery example:
var divs = $('div'); // all divs on the page
var marked = divs.filter('.marked'); // all marked divs on the page
marked.css('color', 'red'); // make marked divs red
Because the filter method returns a new jQuery object, the initial divs variable still contains all the divs on the page. If the filter method were to eliminate things from the jQuery object it was called on and return it, then divs would point to the same object as marked and therefore would no longer have all divs on the page.
From a chaining perspective, nothing changes between the two potential implementations (except for some throw-away objects):
$('div').filter('.marked').css('color', 'red');
I need to change array to a new array created inside a function.
function changeArr(a)
{
a=["hello"]; // don't work
//a.push("hello"); //works, but the real array pretty big (dont want copy)
}
var arr=[];
changeArr(arr);
console.log(arr); // should echo ["hello"]
It seems like all you really want to do is clear the array that is passed into the function, before appending to it:
function changeArr(a)
{
a.length = 0; // clear the array here if you don't care about the prev. contents
a.push("hello"); // this adds to the array that was passed in
}
Inside the function changeArr(), a is only a local variable referencing the array you passed as an argument when calling this function. a=["hello"] makes this local variable reference a newly created and different array. This changes does not affect the original array passed in. What you want to do is likely what Miky Dinescu suggested: use the local variable to modify the original array but don't assign/attach anything new to it.
If you are trying to completely replace the array, you can use a return value.
function changeArr()
{
return ["hello"];
}
arr = changeArr();
If there is a reason you aren't doing this, explain and I'll adjust my answer.
You can use the splice method as such:
a.splice(0,a.length,"hello")