Return function with &&? - javascript

I am confused about this snippet from command pattern tutorial,
What does it mean by
return carManager[name] && carManager[name].apply( carManager, [].slice.call(arguments, 1) );
Also I can understand if it is carManager.execute( "buyVehicle"), then carManager["buyVehicle"] can invoke the function, but what about carManager[buyVehicle", "Ford Escort", "453543"]?
(function(){
var carManager = {
// request information
requestInfo: function( model, id ){
return "The information for " + model + " with ID " + id + " is foobar";
},
// purchase the car
buyVehicle: function( model, id ){
return "You have successfully purchased Item " + id + ", a " + model;
},
// arrange a viewing
arrangeViewing: function( model, id ){
return "You have successfully booked a viewing of " + model + " ( " + id + " ) ";
}
};
})();
carManager.execute = function ( name ) {
return carManager[name] && carManager[name].apply( carManager, [].slice.call(arguments, 1) );
};
carManager.execute( "buyVehicle", "Ford Escort", "453543" );

return carManager[name] && carManager[name].apply( carManager, [].slice.call(arguments, 1) );
if the function carManager[name] exists in the carManager then execute the function.
carManager[name].apply( carManager, [].slice.call(arguments, 1) );
.apply() is used to invoke a certain function on someother or the same object. it takes the object as the first param and the arguments as the second param. you can provide any object as the first param as long as the function doent cause any change to the object's existing properties. for eg this works the same.
carManager.execute = function ( name ) {
return carManager[name] && carManager[name].apply( {}, [].slice.call(arguments, 1) );
};
code:
[].slice.call(arguments, 1);
.call() does pretty mush the same function as apply(). its taking the slice function of the Array object and invoking it on to some other object, this time on the arguments (object) array. slice returns a part of an array. in this case its just avoiding the first element in the arguments array. ie its making the array without the first element
carManager.execute( "buyVehicle", "Ford Escort", "453543" );
it calls the invoke function. the first argument provides the key of the function. the second and the third arguments provide actual data that has to be passed into the method present in the carManager object. and thats the very reason why the arguments array was sliced.

Simplifying the example:
return f && f();
The way the && operator works in JavaScript is by producing its left operand if its left operand is falsy and its right operand otherwise. It also short-circuits, meaning that if the left operand is falsy, the right operand isn’t evaluated at all. Here, it has the effect of calling f if f is truthy (e.g. a function) and returning its return value, or returning f if f is falsy (e.g. undefined).
In other words, it’s a short way of writing:
return f ? f() : f;
with the actual meaning of:
if (f) {
return f();
}
and when carManager is an object with function properties, that in turn means calling the method named name only if it exists and passing on its return value.
Also I can understand if it is carManager.execute( "buyVehicle"), then carManager["buyVehicle"] can invoke the function, but what about carManager[buyVehicle", "Ford Escort", "453543"]?
This question is a bit hard to interpret but
carManager.execute("buyVehicle", "Ford Escort", "453543")
probably answers it.

It's a guard against the possibility that carManager[name] is a falsy* value (most likely not filled in at all). The code is assuming the property is either missing entirely or has a falsy value, or has a function in it.
If carManager[name] is falsy, it returns that falsy value.
If carManager[name] is not falsy (truthy), the code assumes it's a function and calls it (via apply) and returns whatever it returns.
* "falsy" = false if coerced to boolean. The falsy values are 0, NaN, "", null, undefined, and of course, false. All other values are truthy.

Question 1
When you do:
return carManager[name] && carManager[name].func();
You are saying: check if carManager[name] is untruthy. If it is, return false. Otherwise, execute the func() and return its result.
Question 2
The arguments object can be used to get all the arguments passed to a function, even if they aren't present in the function declaration.
function foo(baz) {
console.log(arguments[0]); // outputs baz: "me"
// same of: console.log(baz);
console.log(arguments[1]); // outputs "you"
console.log(arguments[2]); // outputs "them"
}
foo('me', 'you', 'them'); // call the function, pass 3 arguments

Related

How to get the underscore function _.first to work on the arguments objects?

As part of the precourse for a coding bootcamp, we have to create a simpler version of the underscore JS library. I am struggling with creating the _.first function, which:
Returns an array with the first n elements of an array.
If n is not provided it returns an array with just the first element.
This is what I've got so far:
_.first = function(array, n) {
if (!Array.isArray(array)) return [];
if (typeof n != "number" || n <= 0) return [].slice.call(array, 0, 1);
return n >= array.length ? array : [].slice.call(array, 0, n);
};
It passes all test except one: "It must work on an arguments object"
I know the arguments object passes an array with all the arguments passed and it has a length property but Im struggling to work with it.
Any help would be much appreciated.
The arguments object is just that, a variable defined implicitly on each function scope that acts like an array. Has a length property and you can access the elements by using number properties like a normal array:
var _ = {};
_.first = function() {
if (arguments.length == 0) { // If there's no arguments
return [];
} else { // When there's 1 or more arguments
var array = arguments[0];
var n = arguments.length > 1 ? arguments[1] : 1; // If there's only the "array" argument ("n" is not provided), set "n" to 1
// And now your code, which has nice checks just in case the values are invalid
if (!Array.isArray(array)) {
return [];
}
if (typeof n != "number" || n <= 0) {
n = 1;
}
return [].slice.call(array, 0, n); // Don't worry if slice is bigger than the array length. It will just work, and also always return a copy of the array instead of the array itself.
}
};
console.log( _.first() );
console.log( _.first([0,1,2]) );
console.log( _.first([0,1,2], 2) );
console.log( _.first([0,1,2], 10) );
I would add something to the first answer. The arguments object is something that is normally created implicitly by JavaScript and made available inside the function body. In order to write a unit test for "It must work on an arguments object", they must explicitly define an arguments object and pass it in. This is a bad unit test because it is testing the internal working of your function. You should be free to write the function any way you like, and a unit test should test the external behaviour of the function (return value and/or side effects, based on the arguments passed).
So imo your original solution is good and the test is designed to force you to use a certain syntax for the sake of learning, but this is misleading.

What does "return a" in this recursive combinations function does?

I went over this function over and over in Chrome debugger and still can't understand what return a does. Thanks for helping me out!
Here's some clarification:
I understand the first round. The anonymous function gets called with the following parameters:
active = "", rest = "abc", a = []
It then calls itself til rest is empty, when it populates the a array:
active = "abc", rest = "", a = ["abc"]
At this point we arrive to return a, and the debugger jumps to the second fn call in the else statement instead of the first if statement. The parameters already look like this:
active = "ab", rest = "c", a = ["abc"]
This is the part I don't understand at all. I know the recursion is only over when active and rest is empty, but after return a the if statements don't even get checked in the debugger, it just highlights the mentioned second function call, and at that point "c" is already assigned to rest. I guess the reason why this function doesn't produce duplicates also lies here, but that might be another question if not. Anyway, thanks again for the help!
combinations("abc");
function combinations(str) {
var fn = function(active, rest, a) {
if (!active && !rest)
return;
if (!rest) {
a.push(active);
} else {
fn(active + rest[0], rest.slice(1), a);
fn(active, rest.slice(1), a);
}
return a;
}
return fn("", str, []);
}
In this case the recursion construct is written oddly and the "return a" is merely to smuggle out the mutated array, the same object initially supplied, back to the helper function so that a direct return can be used on the inner recursive function call. (In most contexts, and especially those discussed in recursion primers, recursive functions usually utilize the returned value.)
Here is a cleaner re-write using the helper function more. I also removed all explicit return statements because they were not adding value here.
function combinations(str) {
var fn = function(active, rest, a) {
if (!active && !rest) {
// base case #1 - implicit return, no recursive call
} else if (!rest) {
// base case #2 - implicit return, no recursive call
a.push(active);
} else {
// recursive case, where function is called again
// (the object "a" is modified in recursion;
// result of recursion not directly used)
fn(active + rest[0], rest.slice(1), a);
fn(active, rest.slice(1), a);
}
}
var o = []; // only one output object created..
fn("", str, o); // ..mutated..
return o; // ..and returned to caller.
}
It should then be easy to observe that passing "a" to the recursive function is not needed (ie. it could access "o" in the enclosing scope) because there is no new object assigned to "a" after the initial result array is created.
There is a subtle difference in the re-write will (more correctly) return an empty array for an empty input string.
While I understand that this question is primarily about how that particular recursive implementation works (and there's already an excellent answer to that!), I think it's worth pointing out that it's an odd implementation, and something like this is probably cleaner:
const combinations = (str, active = '') =>
str .length == 0
? active .length == 0
? []
: [active]
: [
... combinations (str .slice (1), active + str [0]),
... combinations (str .slice (1), active)
]
console .log (combinations ('abc'))

How can this function work with a missing parameter

How can callback function work with one parameter when it requires 2 (selector and data) to go? Why doesn't it throw an error?
let links = document.querySelectorAll("a");
links.forEach(function(link){
link.addEventListener("click",function(e){
e.preventDefault();
ajax("get",e.target.href,render)
})
})
function ajax(url,metodo,callback){
let xhr = new XMLHttpRequest
xhr.open(metodo,url)
xhr.addEventListener("load",function(){
if(xhr.status==200){
callback(xhr.response)
}
})
xhr.send()
}
function render(selector,data){
document.querySelector(selector).innerHTML = data
}
In javascript, it is not necessary to call with same number of parameters as defined in function definition. If we do not define a default parameter value in function definition, then parameter becomes type of undefined.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
Default function parameters allow formal parameters to be initialized
with default values if no value or undefined is passed.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
Starting with ECMAScript 2015, there are two new kinds of parameters:
default parameters and rest parameters.
Default parameters: In JavaScript, parameters of functions default to
undefined. However, in some situations it might be useful to set a
different default value. This is where default parameters can help.
In the past, the general strategy for setting defaults was to test
parameter values in the body of the function and assign a value if
they are undefined. If in the following example, no value is provided
for b in the call, its value would be undefined when evaluating a*b
and the call to multiply would have returned NaN. However, this is
caught with the second line in this example:
function multiply(a, b) {
b = typeof b !== 'undefined' ? b : 1;
return a * b;
}
multiply(5); // 5
With default parameters, the check in the function body is no longer necessary. Now, you can simply put 1 as the default
value for b in the function head:
function multiply(a, b = 1) {
return a * b;
}
multiply(5); // 5
For more details, see default parameters in the reference.
Rest parameters: The rest parameter syntax allows us to represent an
indefinite number of arguments as an array. In the example, we use the
rest parameters to collect arguments from the second one to the end.
We then multiply them by the first one.
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
Argument Object:
Using the arguments object, you can call a function with more
arguments than it is formally declared to accept. This is often useful
if you don't know in advance how many arguments will be passed to the
function. You can use arguments.length to determine the number of
arguments actually passed to the function, and then access each
argument using the arguments object.
For example, consider a function that concatenates several strings.
The only formal argument for the function is a string that specifies
the characters that separate the items to concatenate. The function is
defined as follows:
function myConcat(separator) {
var result = ''; // initialize list
var i;
// iterate through arguments
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
You can pass any number of arguments to this function, and it
concatenates each argument into a string "list":
// returns "red, orange, blue, "
myConcat(', ', 'red', 'orange', 'blue');
// returns "elephant; giraffe; lion; cheetah; "
myConcat('; ', 'elephant', 'giraffe', 'lion', 'cheetah');
// returns "sage. basil. oregano. pepper. parsley. "
myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');
To make it throw error, if same number of argument is not passed in function, typescript can be used.

How does the "arguments" object work internally?

As shown in the JS snippet below, arguments[0] and a always keep the same value. This is well-known in JS, but I'm still curious how this is implemented.
For example, if both a and arguments[0] are referencing to the same JS object, it's understandable that they always get the updated value. But this cannot explain the situation for primitive values like 1.
If I understand correctly, JS always copies the value for primitives which implies that both a and object[0] hold a copy of this value. If this is the case, how are a and arguments[0] always synced?
function func(a, b) {
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
arguments[0] = 123;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
a = 123456;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
func(1, 2);
Here is the output:
>node test.js
a = 1
arguments[0] = 1
a = 123
arguments[0] = 123
a = 123456
arguments[0] = 123456
The arguments object is special, it hooks into the way the Javascript implementation stores the function arguments. So when you assign to arguments[n], it actually finds the memory block used to hold the parameter variables, and puts the value you're assigning there. And that same memory location is also used when referencing the parameter variables by name.
Another way to think about it is that every function has a local arguments array-like object. When the JS implementation compiles the function definition, it replaces all the parameter variables with references to that array. So expressions that use a are compiled as if they'd used arguments[0], b is translated to arguments[1], and so on.
"The arguments object is an Array-like object corresponding to the arguments passed to a function." from MDN: Arguments object
To keep it simple, just think.
function func(a, b) {
// imagine this line of code gets automatically added
// var arguments = [a, b];
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
arguments[0] = 123;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
a = 123456;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
In JavaScript, function parameters are treated as local variables. In the example you provided, the a argument is assigned the value of 1 when the function is called.
The arguments object is an array-like object that contains all of the arguments passed to the function.
arguments[0] is a reference to the first argument passed to the function, which in this case is 1.
In JavaScript, when you assign a primitive value like 1 to a variable, the value is copied and stored in the variable. This means that a and arguments[0] each hold a separate copy of the value 1.
However, when you assign a new value to arguments[0], this updates the value stored in the arguments object, which in turn updates the value of a since a and arguments[0] refer to the same value. This is because in JavaScript, variables are passed by reference.
When you assign a reference to an object or an array to a variable, both the variable and the arguments object reference the same object in memory.
When you modify the object through one of the references, it is reflected in both references. So in summary, the behavior you observed is due to the fact that in JavaScript, variables are passed by reference and the arguments object is a reference to the arguments passed to the function.

What is the difference between arguments and parameters in javascript?

I know that a parameter is a variable passed to a function and gives value to the argument in the function, but I'm having trouble understanding:
What is the main difference between "arguments" and "parameters" in javascript?
The parameters are the aliases for the values that will be passed to the function. The arguments are the actual values.
var foo = function( a, b, c ) {}; // a, b, and c are the parameters
foo( 1, 2, 3 ); // 1, 2, and 3 are the arguments
When you define a function, the variables that represent the values that will be passed to it for processing are called parameters. For example, the following function definition has one parameter called $number:
function doubleIt($number) {
return $number *= 2;
}
However, when you use a function, the value you pass to it is called an argument. So, in the following case, $price is passed as the argument to doubleIt():
$price = 50;
$inflated_price = doubleIt($price); // 100
parameters (if any) define the method signature.
Arguments are values passed into a function.
But same difference I guess.
void function(int param1, string param2) //defines the types the function must receive.
function(1, "Hello World") 1 and "Hello World" are passed as arguments. The parameter receives (if you like) the argument.
It is explained well here
13 Function Definition
Syntax
FunctionDeclaration :
function Identifier ( FormalParameterList (opt) ) { FunctionBody }
FunctionExpression :
function Identifieropt ( FormalParameterList (opt) ) { FunctionBody }
FormalParameterList :
Identifier
FormalParameterList , Identifier
FunctionBody :
SourceElements (opt)
Officially they are called parameters, but the actual arguments are given in the same called object. However, both words are interchangeable.
Parameters are properties of a function.
Arguments are properties of a particular call to a function.
In javascript, if you don't give a number of arguments equal to the number of parameters, the extra come across as undefined.
function f(a,b,c) // 3 parameters
f(1) // 1 argument given; inside the function f, a will be 1, and b and c will be undefined
so parameters works just like a place holder for your arguments
when you going to call the function to give it a value you will give the function a value and that value stored in the place of parameter1 and parameter2
function add (parameter1 , parameter2 ) {
return parameter1 + parameter2;
}
add(10 (argument 1), 20 (argument2 ))

Categories

Resources