checking properties of arguments passed to function we spyOn - javascript

I'm trying to figure out (not successfully yet) if it's possible to check if js function I'm trying to mock with spyOn (jasmine) was called with the exact parameters I expected, for example consider function:
foo = function(date, array) {};
So far I can only make sure that the array was indeed passed like this:
spyOn(bar, 'foo').and.returnValue(somePromise.promise);
somePromise.resolve;
//call some function that calls 'foo' eventually...
expect(bar.foo).toHaveBeenCalledWith('10/12/2100', []);
I need to check if the array passed to this function was empty, how can I achieve this? Also if it's possible, how can I check if array passed to this function contained the string I wanted?
Thanks!

Replace and.returnValue with and.callFake, then pass in a function that stores the arguments before returning the promise. Then the arguments can be used for later expect tests.

I don't know Jasmine, so this will probably not be the answer you're looking for.
On the other hand, it is a doable work around that you can implement in the mean time while you await your real answer.
If you have control over foo, then in the foo method you can tell it to set a global variable to its input parameter:
var fooParam;
var foo = function(array){
fooParam = array;
}
Then in your other code, you can do:
if (fooParam != null){
if (fooParam.contains(yourString)){
//I'm not sure if "contains" is actually a method or not,
//but that's really a separate issue.
}
}
So you set the global variable fooParam in the foo method and then you can use it later to do checking.
So there's a potential problem in that you might not have control over foo, such as if it's a system method. To work around that, replace all calls to foo with something like foo2, which you would define to have the same parameters and all it does is call foo and set the global variable. But if you have control of foo, then you don't need to do this.
The other potential problem is that foo will be called multiple times (such as if it's called within a loop). To circumvent this, you might make the global variable be an array list and have foo add its parameters to the array list instead of setting the variable. But if it's not called within a loop, or not called multiple times before it can be processed, then you don't need to do this.
I hope this helps while you're looking for the real answer

Related

Call Javascript functions from variable string name

I am looking for the best method of calling functions with variable names that all follow a specific rule. I am trying to avoid using eval() since there are so many problems about it online.
I have a system setup where different groups have a function that can be called based on their name EG Test group would have a function imported in called getValue_Test(params) and this function can be called for any group name EG getValue_Test2(params) would work if the group name was test2.
I have these functions being imported into the react component and just want to know the best way to call them without making giant switch statement since there will be hundreds of these functions.
Any help would be appreciated :) Thanks
If you've got some functions defined like this:
function foo() {}
function bar() {}
function baz() {}
You could put them in an object (or map):
const funcs = { foo, bar, baz };
Then you will be able to use a string to access and eventually call the function:
funcs["foo"](); // called foo
const key = "bar";
funcs[key](); // called bar
funcs.baz(); // called baz
However this is usually not recommended. It looks like you might want to use an array instead? Your function names differ only by a number that appears to be incrementing.

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);

Object methods assigned to variables or function arguments fail when invoked

I'm learning javascript right now, seems like beautiful functional language to me, it is wonderful move from PHP, I should have done this earlier. Although, I cannot figure this one out:
var v1 = (/[abc]/).test;
v1('a');
says test method called on incompatible undefined, I'm trying to store the test method of that regex into variable and invoke it later.
but it works with my own functions:
function foo(){
return 'I\'m foo';
}
var f = foo;
f(); // returns I'm foo
It should work on methods too, since functions are just methods of parent object anyway, right?
Ultimately, the reason I'm trying this is to be able to write something like this:
var a = ['a', 'b', 'c'];
a.every( (/[abc]/).test );
to check each array member against that regex.
Why doesn't this work? Is it limitation in passing built-in functions around? Or am I just doing something wrong?
PS: If you grind your teeth now and muffling something about bad practices, screw good practices, I'm just playing. But I'd like to hear about them too.
it works with my own functions
You are not using this inside the function. Consider this example:
var obj = {
foo: 42,
bar: function() {
alert(this.foo);
}
};
var f = obj.bar;
f(); // will alert `undefined`, not `42`
It should work on methods too, since functions are just methods of parent object anyway, right?
"Method" is just a colloquial term for a function assigned to a property on object. And functions are standalone values. There is no connection to the object a function is assigned to. How would this even be possible, since a function could be assigned to multiple objects?
Why doesn't this work?
What this refers to inside a function is determined at run time. So if you assign the function to a variable and call it later
var v1 = (/[abc]/).test;
v1('a');
this inside the function will refer to window, not to the regular expression object.
What you can do is use .bind [MDN] to explicitly bind this to a specific value:
var a = ['a', 'b', 'c'];
var pattern = /[abc]/;
a.every(pattern.test.bind(pattern));
Note though that since .bind returns a function, the only advantage over using a function expression is that it is a tad shorter to write.
Is it limitation in passing built-in functions around?
No, the problem exists for every method/function because that's how functions work. The nice thing about built-in functions though is that they often explicitly tell you when this is referring to the wrong type of object (by throwing an error).
Learn more about this.
If you store just a method, it does not carry with it a reference to your object - it just stores a reference to the .test method, but no particular object. Remember, a method is "just" a property on an object and storing a reference to a method doesn't bind it to that object, it just stores a reference to the method.
To invoke that method on a particular object, you have to call it with that object.
You can make your own function that calls the method on the desired object like this:
var v1 = function(x) {
return /[abc]/.test(x);
}
Then, when you do this:
v1('a');
It will execute the equivalent of this in your function:
/[abc]/.test('a');
But, it isn't entirely clear why you're doing that as you could also just define the regex and call .test() on it several times:
var myRegex = /[abc]/;
console.log(myRegex.test('a'));
console.log(myRegex.test('b'));
console.log(myRegex.test('z'));
The test function expects this to be a regular expression. The expression /[abc]/.test gives an unbound function (it does not remember that it belongs to /[abc]/). When you invoke it like you do, this will be undefined and the function will fail.
You can use bind to make the function remember the object it belongs to:
var v1 = /[abc]/.test.bind(/[abc]/);
or
var v1 = RegExp.prototype.test.bind(/[abc]/);
Your reference to the method has lost its knowledge of what it was a method of.
This isn't so much good practice as just the way JS works.
You can do:
var v1 = /[abc]/;
v1.test('a');
If you must encapsulate the test method, then you could do:
var v1 = function(str){
return /[abc]/.test(str);
};
v1('a');
I don't know if this is an acceptable solution, but you can do:
v1 = function(exp) { return (/[abc]/).test(exp); }
v1('a');

Javascript: Putting Function Pointers in an Object?

After looking around a bit, I couldn't find an answer to this question, though I suspect it's been asked before. The closest I could find is this Function pointers in objects though it doesn't get quite what I'm after.
Suppose we have a piece of code like this:
var foo = function () { /* some code */ };
now, I'm assuming that during evaluation time the name foo is bound in it's environment to an internal representation of the function, that is then looked up during application time.
Now suppose we have , a little later in the program, an object:
var obj = {};
obj.func = foo;
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func. But let's say I don't want two copies of that function, but rather have obj.func point back towards foo--is there a way to do this?
This is what I've come up with:
var obj = {};
obj.func = function () { foo(); };
Would this work? Is there a better way?
Many thanks
EDIT FOR CLARIFICATION
Apologies--I don't think I was being clear enough. I hope this helps.
I'm not sure how the Javascript evaluation process works, but let's say it keeps track of some internally represented environment, which we'll represent abstractly here as a set of key value pairs.
Now, when the evaluator sees some code such as
var foo = function () { /* some code */ };
it constructs this in it's environment: ['foo' : [function object]]. And then, later on, when it sees something like this:
var bar = foo;
it looks up foo in the environment to retrieve it's value, a function object, and then augments the environment like so. ['foo' : [function object], 'bar' : [function object]].
Now the same function really is there twice in the environment. Is this how javascript sets up its environment? If not, then how does it do so?
My motivation for asking is as follows. Let's say we define some functions, and then later an object that we export that functions as an interface to those functions, and which we know we're going to instantiate many times. It would be more efficient if the object contained pointers to functions, rather than the functions themselves.
JS variables, object properties, etc do not hold objects directly; they hold references to objects. When you say var foo = function() { };, foo is assigned a reference to that function you just defined. (Not a pointer, by the way. The difference can be subtle, but important. JS has no user-visible pointers, so it's not as much of an issue. But the languages that do, use them differently. Don't make yourself have to unlearn stuff later.)
And later in the program, when you say obj.func = foo;, you're copying the reference into obj.func. Not the function -- there's still only one function object, with two references to it.
So obj.func = foo; already does what you were saying. When you call obj.func(), it's actually calling the exact same function that's referenced by foo. There's one huge difference, though, and this may be what's causing you trouble: obj.func is a "method call", and JS sets this to obj inside the function. If foo was using this for anything, assuming it was some other object, it's going to be disappointed.
If that's a problem, you might do something like
var that = this;
var foo = function() { /* use `that` instead of `this` here */ };
Then, whether you call the function as foo or obj.func, it's not affected by the changed this.
If this doesn't help, you'll need to clarify the question.
Have you tried running your example?
Yes, that does work, however be aware that if you change the foo variable it will also change the functionality of objc.func().
I don't know any other way to do this.
This isn't true:
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func. But let's say I don't want two copies of that function, but rather have obj.func point back towards foo
If you assign things that way then obj.func and foo point to the same thing. I don't even think it's possible to copy a function.
Just to elaborate a bit:
functions are first class citizens or objects in javascript. They are used just like any other objects.
var foo = func() {
//some code
};
is the analogous to
var foo = {
//some code
};
In Javascript, all objects are passed by reference. As far as I know there is no concept of a pointer or pass by value for javascript objects.
So when you assign two references to the same function, the function object is not copied. However, the internal state of the function remains the same for both the references. In your case foo and obj.func()
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func.
You think so? Just out of curiosity, if the code said:
var obj = {};
obj.func = 5;
How many copies of 5 are floating around?
No. All code is translated to functions, which are essentially constants. You can assign several variables to the same constant without any trouble.
As for "binding", binding occurs when the function is called. That is, the this parameter is set to obj and whooosh...

JQuery\Javascript - Passing a function as a variable

I was just curious if I could pass a function as a variable. For example:
I have a function
$('#validate').makeFloat({x:671,y:70,limitY:700});
I would like to do something like this:
$('#validate').makeFloat({x:function(){ return $("#tabs").offset().left+$("#tabs").width();},y:70,limitY:700});
This does not work, but ideally every time the variable was accessed it would compute the new value. So if the window was resized it would automatically adjust as opposed to a variable passed in being static. I realize I can implement this directly inside the function\widget, but I was wondering if there was some way to do something like the above.
The concept of this is independent of the plugin. I am talking about the function being "cast" as a variable.
Yes, you can pass an object which will invoke some function when its property is read (this is called a getter), but it is not cross-browser compatible. For example, this will (probably) work in IE9:
var o = {y:70, limitY:700};
Object.defineProperty(o, 'x', {get: function() {return 671;}});
$('#validate').makeFloat(o);
There are other syntaxes for other browsers such as __defineGetter__ for Firefox, and some browsers don't have this functionality at all. So it is practically useless unless you can fully control the environment where your code runs.
This won't work unless x is invoked (obj.x(), instead of just obj.x).
To make it work, the makeFloat() code must check the type of x, and if it's a function, invoke it.
I see what you're trying to do, but it won't work. Why? makeFloat expects the value to be non-function type. It probably uses that value directly. To actually execute the function, makeFloat needs to do x() or even x.call(...) or x.apply(...), which it most certainly isn't doing.
To answer your other question i.e., can you pass functions as variables, the answer is yes. In fact, this is the way callbacks and closures are handled in Javascript. For example, in jQuery when you bind an event handler you are passing in a function as a parameter:
jQuery("#myInputId").click(function() {
...
...
});
Another way that parameters are passed in are as object attributes, for example in jQuery.ajax:
jQuery.ajax({
...
success: function(data) {
},
...
});
In both cases, click and ajax both understand and expect the parameter to be a function and not just a regular variable. For example, assuming you had an object that maintained a list of integers and you had a method called addElement(int), which expected an int parameter, you wouldn't pass in a String. It works the same way in Javascript, except for the fact that the language is not strongly typed. This is why you don't really get a type-mismatch error unless the function explicitly checks the type and throws an exception. This is generally a good practice in such language; I try to do this in the Javascript code that I write.
I've done this with string variables. You'll need to exploit the toString function.
function RefString(fn) { this.toString = function() { return String(fn()); }; }
You can use it like so:
$("#someDiv").somePlugin({optionValue: new RefString(MyFunc), ... });
function MyFunc() {
return new Date().getYear().toString();
}
It works by setting optionValue to a new OBJECT, not necessarily a function. Then anything that reads this object will ask for a value, which by default is the result of the toString function. We simply override the default behavior by executing a function that is specified when the object is constructed.
I'm not sure how it will work for EVERY plugin, but it works when a string or number is expected.
How do you mean "doesn't work"?
It looks like it should compile and run. But what happens is it executes the function and sets the value no different than if you used a constant, or called a function that wasn't inline.
What you need to do is put this line of code in an event that fires when the window is re-sized.
It looks like makeFloat is from a jQuery plugin - are you sure that the plugin is aware that 'x' can be a function and will execute it properly? From the jQuery site, it looks like it only is able to comprehend a number value or 'current' as a string, not a function.
You can pass functions as variables, yes - but that's not actually what you're asking.
What it looks like your asking is "can I set a DOM property to the result of an expression?" to which the answer is "no". (Note - not outside of browser-specific behavior such as IE's CSS Expressions - which have been deprecated in IE8 anyway)
You'll need to bind an event handler to window.onresize and use a function to update the sizing yourself.
In order for a function to be executed from a variable, it has to be called, like so:
$.option.callback.call();
Where option is the containing variable, callback is the function and call executes the function.
It's not like you don't have options though. You can set it up so that the returned value of that function is executed from the line itself. Or you can set it up in the alternative manner that you described.
You need to invoke that function so that it returns the actual value you're looking for. So you're not actually passing in a function, you're invoking it and it's immediately returning a value. For example:
$('#validate').makeFloat({
x:function(){
return $("#tabs").offset().left+$("#tabs").width();
}(),
y:70,
limitY:700
});
Notice the extra () after the function call. This invokes the function immediately, thus returning the value you're looking for.
Note that x doesn't "compute new value" when is accessed (read), but when the function is called, i.e. x(). As Chad mentioned, this is how you can automatically execute a function when windows is resized:
$(window).resize(function() {
// do something
});
[Update] After re-reading your question, I think you may be thinking overcomplicated – isn't this what you are looking for?
$('#validate').makeFloat({
x: $("#tabs").offset().left + $("#tabs").width(),
y: 70,
limitY: 700
});

Categories

Resources