So I have a Hashtable defined (with jshashtable.js):
hashy = new Hashtable();
I put some keys in it:
hashy.put("hi", "yay");
hashy.put("hello", "yes");
Now I want to iterate it with .each, as the doc says:
hashy.each(iterator());
But it says 'object is not a function'
Now, if I do this:
hashy.each(function() { });
It works, but is there a way to call an existing function this way?
I just realized the most obvious answer would be to do:
hashy.each(function() { iterator(); });
Assuming your iterator function is something like the following:
function iterator(key, value) {
// Do something with key and/or value
}
... then just pass the function in without the parentheses. With the parentheses, you're executing the function immediately (just once) and passing the returned value into each(), which is probably not what you intended.
hashy.each(iterator);
Related
I'm trying to recreate the functionality of the underscore _.invoke for learning purposes and I would like to really understand how it works as it seems to be something not too complicated.
The exercise is asking me to return an array with the result of calling "a" method to it. Ok, so here we start.
_.invoke = function (collection, methodName) {
let result = [];
// debugger;
if (Array.isArray(collection)) { // check if collection is an array.
for (let i = 0; i < collection.length; i++) { // iterate over collection
result.push(Array.prototype.methodName.call(collection[i]));
}
}
console.log('result:', result);
return result;
};
I don't know exactly what method is being past to methodName nor if it has any extra arguments to be forwarded (this I understand it would be used in case I'd use a method that requires args like .reduce for instance if I'm not wrong).
As I understand, when I use the .call method on methodName, it should return (push) the iterated element with the "function" applied onto it. Obviously there is something not right, I have used the debugger to see what it does on each step and once it runs the loop and arrives to the call, it quits the loop and runs to check whatever it is it does in the config file of the test.
I get this message in the error log of the HTML file:
_.invoke(mocks.arr, 'testCall').should.eql(mocks.arr);
_.invoke(mocks.obj, 'testCall').should.eql(mocks.objValuesArr);
argsArr = [mocks.arr, mocks.obj];
_.invoke(mocks.arr, 'testArgs', mocks.arr, mocks.obj);
called.should.be.true;
called = false;
argsArr = [mocks.obj, mocks.arr];
_.invoke(mocks.obj, 'testArgs', mocks.obj, mocks.arr);
called.should.be.true;
The this, thisArg and such are still a little hard for me to understand, can someone explain to me what am I missing here..?
So, after some digging, trial and error, I was totally wrong about my approach to the exercise, so I had to re-make the whole thing.
_.invoke = function (collection, methodName) {
// Spread all arguments into a variable.
let args = [...arguments];
// Since the arguments have been all passed to args, we don't need to call them as we normally would.
// Use an already defined function (_.map) with an iteratee to be passed as method.
return _.map(args[0], function (value) {
// Return the iterated value passed through the function of _.map
// and apply the rest of arguments to the element with the function from _.map if there are any.
return value[args[1]].apply(value, args.slice(2));
});
};
I don't know much about underscore.js, but I'm pretty sure _ isn't defined at all, so maybe do window._.invoke = ... instead to properly define it.
I have a problem passing an array.push function in javascript having a code like this:
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element);
}
function doConsoleLog(message, log) {
log(message);
}
Just curious how's that first call of addToArray works, but second causes TypeError: Cannot convert undefined or null to object. How is it possible? Type of array.push is a function. It is not possible to pass functions directly like in the case of console.log above, which works pretty well?
push is a method of array. When invoked, it uses this as a reference to the object it is being called on. If you pass it around like a normal function, you will lose that reference, the method will use the value of this defined at the moment of the call (inside addToArray, this is probably document.window), and will therefore fail.
In order to pass around a method, you need to bind it to the object you want it to operate on, in this case array. This is true for any object method in JavaScript. Function.prototype.bind() exists exactly for this purpose: it "binds" a function (in this case your method) to a given value of this, in order to work correctly.
const array = [];
addToArray("now it works", array.push.bind(array));
// or
addToArray("now it works", Array.prototype.push.bind(array));
function addToArray(element, pushFn) {
pushFn(element);
}
Once you pass a function like you did in the second case, it executes itself immediately, and array.push doesn't really mean something.
A callback function should be executed later on in the outer function.
Unlike the second example, in the first one you don't execute the function immediately, but only when you call it inside the outer function.
Your code have a lot of mistakes. What exactly should do this code? if you want to push new items in array use spread operator
const array = [...[], "works", "notWorks"]
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element); //function must return something
}
function doConsoleLog(message, log) {
log(message); //function must return something
}
I have the follwoing:
HB.formButtons.deactivatingButton = function(inputs) {
inputs.forEach(function(argument){
argument.parent().removeClass(HB.formButtons.SUBMIT_BUTTON_STYLE);
});
}
which I than call in various parts like so:
HB.formButtons.deactivatingButton($(HB.personalDetails.SUBMIT_PERSONAL_DETAILS_SELECTOR), $(HB.personalDetails.CANCEL_PERSONAL_DETAILS_SELECTOR));
The console. throws the following error:
Uncaught TypeError: formsInput.forEach is not a function
Why?
Considering your input to be HTMLCollection, the forEach doesn't work on a collection. forEach works on Array
You can, however, make it work like
HB.formButtons.deactivatingButton = function(inputs) {
[].forEach.call(inputs, function(argument){
argument.parent().removeClass(HB.formButtons.SUBMIT_BUTTON_STYLE);
});
}
If you wanted to pass your function array of two elements you have to use square brackets [].
HB.formButtons.deactivatingButton(
[
$(HB.personalDetails.SUBMIT_PERSONAL_DETAILS_SELECTOR),
$(HB.personalDetails.CANCEL_PERSONAL_DETAILS_SELECTOR)
]);
alternative you can use the special variable arguments in your function. Described at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
I have an array
var arr = [' A ', ' b ', 'c'];
and I want to trim the spaces from each of the element from array.
It can be done by using Array.map as
arr.map(function(el) {
return el.trim();
});
I'm curious about passing the trim/toLowerCase function directly to the map as callback function, like arr.map(Math.max.apply.bind(Math.max, null)); to get the maximum element from each subarray or arr.map(Number); to cast each element to Number.
I've tried
arr.map(String.prototype.trim.apply);
but it is throwing error
Uncaught TypeError: Function.prototype.apply was called on undefined, which is a undefined and not a function
I expect that String.prototype.trim.apply should be called for each element in the array with the context set to the element from array(passed to apply);
I've also tried different combinations of apply, call and bind with no success.
Why the function on prototype cannot be referenced when using map
How function can be passed as parameter to map
arr.map(String.prototype.trim.call.bind(String.prototype.trim));
call uses this internally, which must point to the trim function to work properly in this case. Simply passing String.prototype.trim.call would leave call unbound to any method, resulting in the this value pointing to window instead.
It works, but when used apply instead of call it throws error,
arr.map(String.prototype.trim.apply.bind(String.prototype.trim));
The problem is that map will pass 2 arguments, the item and the index. Therefore it ends up calling something like 'String.prototype.trim.apply('test', 0) which fails since the second argument must be an array.
one more thing [' A ', ' B ',
'c'].map(String.prototype.trim.call.bind(String.prototype.toLowerCase));,
in this, I've used trim.call and passed toLowerCase as context then
why we need trim here, why trim is not called
When using call.bind the path that you chose to access the call function reference becomes irrelevant. The function that will get called is the one that is bound.
If you want to compose functions together you will need a different approach:
var call = Function.prototype.call,
trim = call.bind(String.prototype.trim),
toLowerCase = call.bind(String.prototype.toLowerCase),
trimAndLowerCase = pipelineFrom(trim, toLowerCase);
[' TeST '].map(trimAndLowerCase);
function pipelineFrom(fn1, fn2) {
return function (val) {
return fn2(fn1(val));
};
}
However at this point you're better off with:
arr.map(function (val) {
return val.trim().toLowerCase();
});
This works, it sure is long-winded though:
var t = String.prototype.trim.call.bind(String.prototype.trim);
arr.map(t);
Because it's longwinded there are blog posts and modules devoted to uncurrying, which is what you are trying to do here.
I did ask about this here once...
I achieve a forEach function:
function forEach(arr, fn) {
for (var i = 0; i < arr.length; i++) {
fn.call({}, arr[i], i);
}
}
what I confused is about fn.call({}, arr[i], i);
the first parameter is pass empty just like above {} is better
or pass this in: fn.call(this, arr[i], i); is better?
Or it doesn't matter
It matters quite a bit. The first parameter to .call() is the value to be used for this inside the called function. Thus, it doesn't make sense to talk about what value is "better"; the right value to pass is the one you need in order for the called function to operate properly.
For example, if you want to call a function on the Array prototype, then the value of this inside that function has to be something that "feels like" an array (a "length" property and numerically-indexed properties). Thus:
var sneaky = {
"0": "hello",
"1": "world",
"length": 2
};
alert( Array.prototype.join.call(sneaky, " - ") ); // "hello - world"
That works because that function expects this to refer to the array to be joined.
There are as many other examples as there are functions that have expectations about this. In your sample code, passing {} gives the called function a this reference to that newly-created empty object. Will that work? I don't know, because that function could expect anything. There's no way to find out, either, except by looking at the code (or trusting documentation). If all you know is that it's some random arbitrary function, then {} is a reasonable guess, though undefined might be better, to force early failure.
Personally I would go with passing this. By passing {} you are limiting the flexibility of your function. You will never be able to bind another object to this function the way it is currently written. This won't work:
forEach.call(newContext, array, fn)
Neither will this:
forEach(array, fn.bind(newContext));
By binding {} inside your forEach you are adding unexpected behavior.