I am attempting to test a directive but when I mock out the directive a line in the middle of the directive runs an error. Which prevents my tests from running
$scope.getIterations = function (its) {
return new Array(its);
};
Returns
RangeError: Invalid array length
Which makes sense as the argument its returns NaN.
I figured that I could call
var its = [1,2,3];
$scope.getIterations(its)
Inside of a beforeEach but that did not make any difference as the error code returns the same thing.
Maybe I am misunderstanding this code, but I am not sure how to get around this for the unit tests.
The exception is pretty self explanatory: you're passing an Array when new Array() expects variadic arguments as items to insert or a length to pad:
$scope.getIterations = function(its) {
return new Array(its.length);
};
It's not exactly clear what you're trying to do here... Seems like you'd just want to return the argument as-is:
$scope.getIterations = function(its) {
return its;
};
Array construction takes comma separated values which will become the elements of the array, with an exception that if it has only one numeric id, it will generated array of that much length with undefined as its elements.
new Array("a") // ["a"]
new Array("a","b")//["a","b"]
new Array(3,2) //[3,2]
new Array(3) //[undefined x 3]
in your case if the value of its is an array, you can return the variable itself.
return its;
It seems it has to do with new. If your trying to instantiate a new array then try instantiating your array with Array Literal instead of the constructor:
$scope.getIterations = function (its) {
return [its];
};
but as others have pointed out, it's unclear exactly what you want to return, so if its is an array. You can just return its;.
Related
This might be very basic but I am not able to understand how chaining and return works at the same time like for example
let array = [1, 2, 3]
let newArray = array.map(val => val * 10).map(val => val * 10)
console.log(newArray) // [100, 200, 300]
Here we can keep on adding .map and it will keep returning a new array. It behaves like when chaining stops it knows now that it has to return value but when chaining continues it keeps treating it as object. How does that work and how can I achieve similar functionality in my code.
How does that work and how can I achieve similar functionality in my
code.
I'm not sure what you mean. Array objects in JS have map() method which returns always new array modified by callback function that you're passing val => val * 10.
You can think of this expression as [1,2,3].map(val => val * 10) // same as [10,20,30], and chaining map method on array will work because you'll get always an Array and you can again use an array prototype method (it works synchronously from left to right)
If you'll try chaining method that doesn't return an array e.g [1,2,3].forEach(val => val * 10).map(i => i), you'll get a TypeError when map will be executed (forEach doesn't return any value, so calling map method on undefined will throw a TypeError). That's how chaining works, you need to use always correct types and be sure that each method is called on correct type (map on Array, toUpperCase on String etc).
Array.prototype.map is a method that is called on an array, applies the given function to each element and returns the modified array. This way, you can call other methods on it. Similar methods are Array.prototype.filter and Array.prototype.reduce. They work in a similar way, as you can chain them as well.
To understand basic chaning lets make a function that removes the first letter of a string, kind of like Array.prototype.shift();
// create the strShift function
// it has to be a normal function to access this
const strShift = function(amount = 1){
// return the output of the function, so that you can
// chain another prototypical function
return this.slice(amount);
}
// add the function to the prototype of String so that its available
// with the dot syntax on all Strings for every future String you
// create after this point in the code
String.prototype.strShift = strShift;
const myStr = "Hello";
// prints "ello"
console.log(myStr.strShift())
JSFiddle Link
With that out of the way, we can look at how chaining and return works at the same time. For that lets make a function that inverts the case of each character in a string.
const strFlipCase = function(){
// create a temporary variable to then return after the loop.
const result = [];
// get an array with each letter
const strArr = this.split('');
// loop over the newly created array
for(let character of strArr){
// check whether the character is uppercase
if(character.toUpperCase() === character){
// character is uppercase so push the lowercase character
// into the temporary array
result.push(character.toLowerCase())
} else {
// character is lowercase so push the uppercase character
// into the temporary array
result.push(character.toUpperCase())
}
}
// temporary array has been filled, return the temporary variable
// as a string
return result.join('')
}
String.prototype.strFlipCase = strFlipCase;
const myStr = "Hello";
// prints "hELLO"
console.log(myStr.strFlipCase());
JSFiddle Link
I was trying the below commands on Chrome console. I am able to create the object using new(line 2 below) but using call doesn't work. Can anyone explain what could be the reason ?
function ObjConstructor(){ this.sample = 1};
let withNew = new ObjConstructor();
let usingCall = ObjConstructor.call({});
usingCall
undefined //output that came on console, this is not a command
withNew
ObjConstructor {sample: 1} //output that came on console
new does several things including:
Creating an object
Setting the this value to that object
Causes the function to return that object by default
Your code:
Creates an object manually with {}
Sets the this value to that object with call()
… but doesn't do the last thing. There is no return statement in the function, so it returns undefined.
The result of call is whatever the function returns. Your ObjConstructor doesn't return anything, so the result of calling it is undefined.
In contrast, when you use new, a new object is created and passed to the function, and unless the function returns a non-null object, the object created for new is the result of the new expression.
That's why the new version works but the call doesn't.
Also note that call doesn't create an object at all. In your ObjConstructor.call({}), what creates the object is {}, not call. It won't have ObjConstructor.prototype as its prototype. ({} is a raw object initializer, so the object will have Object.prototype as its prototype.)
try this.
Or look at this -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
var MyType = function(_param1,_param2){
this.param1 = _param1;
this.param2 = _param2;
this.ShowParam = function(){
alert(this.param1+" - "+this.param2);
}
}
var test = new MyType("PARAM TEST 1","PARAM TEST 2");
alert(test.param1+" - "+test.param2);
var test2 = new MyType("PARAM TEST 1.2","PARAM TEST 2.2");
alert(test2.param1+" - "+test2.param2);
test.ShowParam();
test2.ShowParam();
I have come across an interesting piece of code:
function repeat(str,x) {
return Array(x+1).join(str);
}
repeat("wow", 2);
The outcome of this is a string "wowwow". However, I have no idea what this Array(x+1) is actually doing. And very interesting thing is that if I just use Array(x) it prints the str only once and not twice as expected.
When I console.log Array(x+1) it gives this strange output:
Array(x+1) (3) [empty × 3]
I am aware that there exists a repeat() method on strings which can be used happily to achieve the same result as the presented function. But as I've come across it, I would like to know the mechanism behind Array(x+1). I also know what an array or new Array() is. But this I see for the first time.
Array is specified such that new is optional. From the spec:
When called as a constructor it creates and initializes a new Array exotic object. When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
(my emphasis)
To my surprise, the MDN page is not at all clear about this. If I get time, I may have to fix that...
As you can see in console Array(x+1) creates array with 3 empty elements (as far as x = 2). Then you join these elements with string wow so you have:
empty element + "wow" + empty element + "wow" + empty element = "wowwow"
Array(x+1) creates an array of 3 elements all containing empty elements.
Later you are joining these empty elements with the string wow, thus returning:
empty + "wow" + empty + "wow" + empty => wowwow
It is a function that creates an array.
Things like this you should really just try in you console before asking.
These things can be done in Javascript even though they are somewhat contra intuitive:
function A() {
return new Array();
}
let myA1 = A(); // returns an array
let myA2 = new A(); // also returns an array.
EDIT:
The reason is that Javascript is a prototypal language and a "Class" is just a function that we new. If that function is not returning anything we get a new instance of in our case A and if the function returns some other object, that object gets returned.
What is does is to create an array with size x+1(3, in this example). And filling it each cell with the str variable value.
In this code I create an array of size 3 cells and fill them with the string "yolo".
$(document).ready(function(){
var str = "yolo";
console.log(Array(4).join(str));
})
Example fiddle: https://jsfiddle.net/6o90fv9c/
No jQuery please!
The Web says that the native String.concat() and join() functions of JS are to be avoided because of their poor performance, and a simple for() loop of += assignments should work a lot faster.
So I'm trying to create a function in pure JavaScript that will concatenate strings. This is somewhat how I envision it:
I want a main function concatenate() that will concatenate all passed arguments and additionally insert a variable string after each concatenated argument, except for the last one.
If the main function is called by itself and without the chained .using() function, then that variable string should be an empty one, which means no separators in the result.
I want a chained sub-function .using() that will tell the main concatenate() function what certain string other than the default '' empty string to add after each concatenated segment.
In theory, it should work like this:
concatenate('a','b','c'); /* result: 'abc' */
concatenate('a','b','c').using('-'); /* result: 'a-b-c' */
I want to avoid having two separate functions, like concatenate() and concatenateUsing(), because the concatenateUsing() variant would then have to utilize a special constant argument (like arguments[0] or arguments[arguments.length-1]) as the injected separator and that would be terribly untidy. Plus, I would always forget which one it was.
I also want to avoid having a superceding Concatenate object with two separate sub-methods, like Concatenate.strings() and Concatenate.using() or similar.
Here are some of my failed attempts so far...
Attempt #1:
function concatenate()
{
var result="";
if(this.separator===undefined){var separator=false;}
for(var i=0; i<arguments.length; i++)
{result += arguments[i] + ((separator && (i<arguments.length-1))?separator:'');}
this.using=function(x)
{
this.separator=x;
return this;
}
return result;
}
So what I'm trying to do is:
check if the separator variable is undefined, this means it wasn't set from a sub-method yet.
If it's undefined, declare it with the value false for later evaluation.
Run the concatenation, and if separator has another value than false then use it in each concatenation step - as long as it's not the last iteration.
Then return the result.
The sub-method .using(x) should somewhere along the way set the
value of the separator variable.
Naturally, this doesn't work.
Attempt #2:
var concatenate = function()
{
var result="";
var separator="";
for(var i=0; i<arguments.length; i++)
{result += arguments[i] + ((separator && (i<arguments.length-1))?separator:'');}
return result;
}
concatenate.prototype.using=function(x)
{
this.separator=x;
return this;
}
It also doesn't work, I assume that when this is returned from the using() sub-method, the var separator="" of the main concatenate() function just overwrites the value with "" again.
I tried doing this 4 or 5 different ways now, but I don't want to bore you with all the others as well.
Does anyone know a solution for this puzzle?
Thanks a lot in advance!
What you are trying to do is impossible.
You cannot chain something to a method call that returns a primitive, because primitives do not have (custom) methods1.
And you cannot make the first function return different things depending on whether something is chained or not, because it doesn't know about its call context and has to return the result before the method call is evaluated.
Your best bet is to return an object that can be stringified using a custom toString method, and also offers that using thing. It would be something along the lines of
function concatenate() {
return {
args: Array.from(arguments), // ES6 for simplicity
using: function(separator) {
return this.args.join(separator);
},
toString: function() {
return this.args.join("");
}
};
}
console.log(String(concatenate('a','b','c')); // result: 'abc'
// alternatively, use ""+… or explicitly call the ….toString() method
console.log(concatenate('a','b','c').using('-')); // result: 'a-b-c'
1: No, you don't want to know workarounds.
Is it possible to call a method from an object using a string?
var elem = $('#test'); //<div id="test"></div>
var str = "attr('id')";
//This is what I'm trying to achieve
elem.attr('id'); //test
//What I've tried so far
elem.str; //undefined
elem.str(); //Object [object Object] has no method 'str'
var fn = eval(str); //attr is not defined
eval(elem.toString()+'.'+str); //Unexpected identifier
//Only solution I've found so far,
//but is not an option for me
//because this code is in a function
//so the element and method call
//get passed in and I wouldn't know
//what they are
eval($('#test').attr('id')); //test
UPDATE
This is my final, working answer:After running this code in the console
theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));
The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:
theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));
That's the safest, most reliable approach I can think of, really
What ever you do DON'T USE EVAL:
var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method
It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:
$('#foo').attr('id') === $('#foo')['attr']('id');
So just break the string apart, and use the method name like you would an object property and you're all set to go.
Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich
If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):
theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));
This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.
You should use one of these methods:
apply
var result = function.apply(thisArg[, argsArray]);
call
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);
Here is the sample:
var Sample = function() {
var that = this;
this.sampleMethod = function() {
return alert("Hello!");
};
this.sampleMethod2 = function(){
that["sampleMethod"].apply(that);
};
};
var objImpl = new Sample();
objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
Eval does what you want to do. Eval is evil, however, because you should not do what you want to do.
Why is using the JavaScript eval function a bad idea?