I'm new-ish to Javascript and need to make a change to this one script. I noticed this line of code that thoroughly confused me, however.
function FIValidateForm(aForWhom, aDoNotShowWarning, aFromWhere)
{
try
{
PAIWarnings = '';
var pBlFalg = true;
if (pBlFalg)
pBlFalg = mfpCheckForFISave(aForWhom, aDoNotShowWarning, aFromWhere);
if (pBlFalg == true)
pBlFalg = PAIErrors();
if ((pBlFalg) && (FIValidateForm.arguments.length == 1))
mfpIFWarnings();
return pBlFalg;
}
catch (err)
{
logError(err, arguments.callee.trace());
}
}
During run time, if I put a breakpoint on that third if statement and inspect FIValidateForm.arguments I can see an array with 3 items. The first contains a string, the second is null, and the third is undefined. The array still has a length of 3.
Am I correct in assuming that no matter what one were to pass to this method, FIValidateForm.arguments.length == 1 will always be false? Or is there some other value I'm unaware of / alternative way to call this method so that arguments.length would equal 1?
Edit: I see JS has an arguments.length and a Function.length. The latter returns the expected number of parameters... So then how does one call a method so that the value would be 1 when the function is defined with 3?
Am I correct in assuming that no matter what one were to pass to this method, FIValidateForm.arguments.length == 1 will always be false?
No, you can make its FIValidateForm.arguments.length 1 by doing this:
FIValidateForm(justOneArgHere);
arguments represents the actual arguments to the function. (See 1 below about arguments vs. FIValidateForm.arguments.)
If you want the function's arity (the number of formal declared arguments), that's the FIValidateForm.length and it doesn't vary.
So then how does one call a method so that the value would be 1 when the function is defined with 3?
JavaScript doesn't enforce the number of formal arguments at all; you can call your function with no arguments, one argument, or 15. If a formal argument (a declared one, like your aDoNotShowWarning) is not supplied, the value it will have within the function call will be undefined. If you supply more arguments than are declared, they are only accessible (in ES5 and below) via the arguments pseudo-array. (In ES2015 and above, you can make your last argument a "rest argument", which means it will be an array of all arguments from that point on.)
Example:
function foo(a, b, c) {
console.log(arguments.length);
console.log(foo.length);
}
foo(1);
foo(1, 2);
1 The arguments property of the function (e.g., FIValidateForm.arguments) is a non-standard extension to JavaScript, which is expressly deprecated by strict mode (the example above would throw an error in strict mode). Just use the freestanding symbol arguments, which is defined for normal functions (as though it were a local variable).
(And in ES2015 or higher, you can avoid using arguments at all by using rest notation on your argument declarations instead.)
No, it won't always be false.
If you call it as FIValidateForm(), then FIValidateForm.arguments.length === 0
If you call it as FIValidateForm(arg1), then FIValidateForm.arguments.length === 1
If you call it as FIValidateForm(arg1, arg2), then FIValidateForm.arguments.length === 2
If you call it as FIValidateForm(arg1, arg2, arg3), then FIValidateForm.arguments.length === 3
And so on
Howver, do not use FIValidateForm.arguments. It's bad practice and forbidden in strict mode. Use arguments instead.
TL;DR: If you ever call the function with only one argument, functionName.arguments.length === 1 will be true.
Yes, FIValidateForm.arguments.length gives you exactly the number of arguments you call FIValidateForm with.
Consider this function f:
function f(first, second) {
console.log('first argument:', first);
console.log('second argument:', second);
console.log('number of arguments:', f.arguments.length);
}
and these two cases:
// case 1:
f('something');
// prints:
// first: something
// second: undefined
// number of arguments: 1
// case 2:
f('something', undefined);
// prints:
// first: something
// second: undefined
// number of arguments: 2
Notice how undefined is the "default" value for missing arguments, but unlike an explicit undefined argument, a missing argument is not counted in f.arguments.length.
Related
First of all, the way the function is being called throws me off. How can a function be called with two separate parens like the one in this example?
addTogether(4)(3)
Lastly, could someone please explain how the closure works in this if statement(please see full code below)?
if(a) {
return function(y) {
if(checkIfNum(y)) {
return a + y;
} else {
return undefined;
}
};
If we call addTogether() like this:
addTogether(4)(3), I understand that argument a is set to 4 but not sure how y is set to 3.
function addTogether() {
function checkIfNum(num) {
return typeof num === 'number' ? num : undefined;
}
var a = checkIfNum(arguments[0]);
var b = checkIfNum(arguments[1]);//if there is no second argument, var b = undefined;
if(arguments.length > 1) {
return a && b ? a + b : undefined;
} else {
if(a) {
return function(y) {
if(checkIfNum(y)) {
return a + y;
} else {
return undefined;
}
};
} else {
return undefined;
}
}
}
console.log(addTogether(4)(3));//7
On the first invocation, addTogether(4), 'var a = checkIfNum(arguments[0]);' resolves to 'var a = 4'. The first invocation of addTogether() causes our anonymous function (y) to be invoked and returned to the main function addTogether(). When anonymous function (y) is invoked, JavaScript searches for the value of 'y'. How does JavaScript decide that '3' will be the value of 'y'? My guess is that on the first invocation of addTogether() or addTogether(4), the argument object associated with addTogether() is set to the value of 4. Once 4 gets passed to the function addTogether(), we now have 'a' = 4 trapped in the closure created by addTogether() and at the same time, essentially we have three things happening : #1 : 'a' = 4, # 2 : the argument of addTogether() is now set to the value of 3 or addTogether(3), #3 : anonymous function (y) is returned and invoked causing JavaScript to search for the value of 'y'. JavaScript sees that at this exact point in time, the argument of addTogether() is set to the value of 3 so JavaScript sets the value of 'y' to 3. How does JavaScript know to set 'y' to the value of 3? When a function is invoked inside another function, does JavaScript automatically set the value of the argument of the, for lack of better words, "child" function to the value of the argument of the "parent" function? Is there something about the arguments object that I'm missing here?
Great question. I remember scratching my head over this same example about two years ago.
For your first question, "How can a function be called with two separate parens like the one in this example?":
This is only possible because addTogether returns another function. You then pass that function another integer 3. It then adds them up to give you 7.
Then you ask about closures: a closure is formed when you add additional scope inside a scope giving the inner scope access to values in the outer scope. In this case a closure is formed when you declare a function within a function.
For your second question: "I understand that argument a is set to 4 but how y is set to 3":
Here's the logic of the function addTogether:
1) Check if one or two arguments are supplied.
2) If two arguments are supplied return the addition of the numbers iff they are both numbers.
3) If there is just one, return an anonymous function which takes an integer and adds its input to a variable a available to the anonymous function because of it being enclosed inside an outer function which declares a.
This is where the closure is formed. This anonymous function, since it is inside the scope of the outer function addTogether has access to all of the outer function's properties including a and the method checkIfNum.
In your actual case:
1) You supply one argument 3 so addTogether returns an anonymous function.
2) You call this anonymous function with a value of 3. This ends up running the logic in the anonymous function, first checking if 3 is a number, then adding 3 to 4, return 7.
I love JavaScript.
Following code fails:
array.map(String.prototype.toLowerCase)
Throws Uncaught TypeError: String.prototype.toLowerCase called on null or undefined. Indeed, this is not set, I got it.
But what's weird is that following code returns an array of empty strings without failing:
array.map((s) => String.prototype.toLowerCase(s))
Any idea why? Note that I know this is not the way of having an array of lowercased strings. I'm just wondering why these two approaches behave differently.
In other words, what is the difference between .map(String.prototype.toLowerCase) and .map((s) => String.prototype.toLowerCase(s))? I thought is was identical, but obviously, it behaves differently. Note that here, String.prototype.toLowerCase could be replaced by anything.
What is the difference between .map(String.prototype.toLowerCase) and
.map((s) => String.prototype.toLowerCase(s))
For the first case when you get the function outside of an object (String.prototype.toLowerCase) you lose your context, so you context is null and map tries to call on null or undefined. With the first solution you can't get the desired result, because you need to pass a context to the toLowerCase function, which must be each item of the array, but you don't get that each item of array.
For the second case you need to pass context which is the item to the String.prototype.toLowerCase via call function.
var array = ['ASD', 'BSD'];
var lowered = array.map(item => String.prototype.toLowerCase.call(item));
console.log(lowered);
The second approach array.map((s) => String.prototype.toLowerCase(s)) doesn't throw an error, because toLowerCase isn't taken out of context, i.e. the method still has String.prototype as its receiver.
String.prototype.toLowerCase(s)) returns an empty string, because the argument s is discarded. toLowerCase takes it value from its receiving object instead. The receiving object is String.protoype. To get the actual string the prototype must be converted to string. This happens with the String.prototype.toString method, which evaluates to "". Hence String.prototype.toLowerCase(s)) evaluates to "".
You can verify this behavior by changing the toString method:
String.prototype.toString = () => "FOO";
console.log(String.prototype.toLowerCase()); // "foo"
The diffrence between .map(String.prototype.toLowerCase) and .map((s) => String.prototype.toLowerCase(s)) is that .map((s) => String.prototype.toLowerCase(s)) takes an arrow function which is an anonymous function. According to definition of arrow function
"An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target."
An arrow function does not create its own this, the this value of the enclosing execution context is used.
using .map(String.prototype.toLowerCase) will not work as you are not passing any execution context to it but it's looking for one to execute.
For example in the below code
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();
Please check this link
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I think I got it finally:
first one has this set to undefined, equivalent to:
.map((s) => String.prototype.toLowerCase.call(undefined, s))
while second one has this set to String.prototype, equivalent to:
.map((s) => String.prototype.toLowerCase.call(String.prototype, s))
Now, the question is, why String.prototype.toLowerCase() is returning an empty string... But this deserves another question :)
I'm a total newbie in js, so please be gentle with me :)
I want to understand where can we use the dot operator on some variable (in this case - an array), and when we cannot.
consider the following code:
//example 1
function f1(x) {
return x*x;
}
console.log(map(f1, [1,2,3,4,5]));
console.log([1,2,3,4,5].map(f1));
//example 2
function f2(arr) {
return arr;
}
console.log(f2([1,2,3,4,5]));
console.log([1,2,3,4,5].f2());
I know the examples are rather different, but still - in example 1 both prints work (and print the same) - even when using the array.function(..) syntax, while in example 2 the second print raises an error.
basically, what is the difference between the two, and why does it work only in example 1?
and generally - can I apply this method over different variable types (numbers, booleans, etc.)?
In the first example your are using the Array.prototype.map() function.
This function can be called in 2 different ways (See the docs)
[1,2,3].map(function(x){ ... }): //In your case the callback function is *f1()*
OR
arr.map(callback, [1,2,3]);
The second example is not working because the class Array has not function called f2()
[1,2,3,4,5] is an instance of Array "class", and that "class" has map method, that's why the following is a valid code:
[1,2,3,4,5].map(f1)
map method can accept any function as a parameter, and you're passing in your f1 function. It will be executed inside map function and it's executed as a standalone function.
Array "class" doesn't have f2 method, that's why this is invalid code:
[1,2,3,4,5].f2()
Here, f2 is executed immediately and is a part of [1,2,3,4,5] object
In the first case, map is defined as both a global function and public function of the Array. You can therefore call it via map(arr) or arr.map(..)
In the second case, as you only defined f2 as a global function which means the array can't access it.
var a = 1;
var b = {
a : 2,
c : function () {
console.log(this.a);
}
};
b.c(); // logs 2
(b.c)(); // logs 2
(0, b.c)(); // logs 1
The first is understandable, for "this" is pointed to Object "b". But why does the second one log the same result? I thought "this" should be pointed to the global execution context. And the third one, it seems that the comma operator influences the execution context.
You really have a nice corner case there!
My take on it:
the first is straightforward. Just a standard call. The '.' operator lets you call the function setting b as the execution context.
the second is exactly the same thing: the parens are entirely optional and the interpreter is treating the expression inside it as a bound function call. Actually I didn't expect this: I thought the interpreter would be going to reset this to the global object, but actually it's keeping it linked. Probably just so "casual" language users do not freak out.
the third one is more standard (at least for those who live in JavaScript land): as soon as your function is passed on in an expression (in this case by the , operator) the this value is lost. This is because b.c is a Property Reference (deep rabbit hole details in the specification, here, courtesy of T.J.Crowder). So, you are actually passing around the function itself, no more bound to the declaring object. So when you call it this is going to be passed in as the global object.
See it this way: (object.function)() gets simplyfied into object.function(), because the enclosing parens are completely optional; (0, object.function)() is parsed as (expression yielding a function)() which is going to lose the object binding to this, because function is already unbound.
Really nice example!
Refer to Indirect eval call, which gives more details about it.
( 0 , b.c ) ( )
|____| |_____| |_____|
Literal Operator Identifier
|_________________________|
Expression
|______________________________|
PrimaryExpression
|______________________________| |________|
MemberExpression Arguments
|________________________________________________|
CallExpression
We can use the comma operator to fashion an indirect call to b.c which will force it to execute in the global context, the value of a is 1 in the global context.
Also the result of (b.c = b.c)() is 1
> (b.c = b.c)()
1
Speaking in terms of ECMAScript, this is because both — comma operator (in (0, b.c) example) and = operator (in (b.c = b.c) example) perform GetValue on its operands.
Other indirect call formats as below
> (b.c, b.c)()
1
> (1? b.c: 0)()
1
> (__ = b.c)()
1
I'm writing a function that checks if arguments are zero, and it doesn't seem to work correctly. Note: Using Chrome as my browser, but this code should be cross-browser supported.
// check all arguments, and make sure they aren't zero
function zeroCheck(arg1, arg2) {
var i, argsLen = arguments.length;
for (i = 0; i <= argsLen; i += 1) {
if (arguments[i] === 0) {
// This is where it doesn't behave as I expected
arguments[i] = 1; // make arg1 = 1
}
}
console.log(arg1); // arg1 = 0
}
zeroCheck(0, 2);
I was expecting arg1 to be equal to 1, but it is still equal to 0.
From the ECMA-262 spec:
"For non-strict mode functions the array index (defined in 15.4) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object‘s properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values."
But if you read the technical details of how the arguments object is set I think you'll find it is based on how many arguments are actually passed to the function when it is called, not how many named arguments are declared, so using arguments and a loop to check the value of each named parameter might not work if they're not all passed in. Though in your case if you're testing specifically for 0 it should work since parameters that are not passed will be undefined rather than 0.
Having said that, exactly how the arguments object actually behaves depends on the browser. Chrome doesn't follow the spec.
Though some browsers appear to work the way you want (Chrome and Firefox), it isn't obvious to me from the ECMAScript spec that it will always be this way. It makes it sounds like the arguments array is probably just a reference to the named arguments in non-strict mode and it specifically says that the two should have no connection to one another in strict mode (in other words what you want to do is specifically NOT supposed to work in strict mode).
You can see in this jsFiddle http://jsfiddle.net/jfriend00/bG5xp/ that Chrome appears to implement it as the spec describes. There is linkage between arguments[0] and arg1 in non strict mode and there is no linkage between them in strict mode. A careful reading of the spec doesn't say that javascript is required to have linkage between the two in non-strict mode, but it does make it sound like it is likely. If you wanted to rely on that and you were sure you never needed your code to work in strict mode, then you would have to test a bunch of browsers to see if the behavior you desire is widely supported.
It is also not clear from the spec if the arguments array is always meant to be modifiable though that seems more likely given that it's implemented with a javascript object (not an actual array).
The safe way to modify the arguments array is to make a copy first and modify the copy. That will, of course, not modify any named arguments. You could modify those manually if you wanted to.
A common way to make a copy of the arguments array is:
var args = Array.prototype.slice.call(arguments, 0);
One generally uses either the arguments array or the named arguments and not both since any named argument is also in a known position in the arguments array so you don't really need to worry about a named argument changing value when you modify the arguments array.
Try it like this. arg1 with value 0 evaluates to false/falsy, so you can use this shortcut boolean evaluation:
function zeroCheck(arg1,arg2) {
arg1 = arg1 || 1;
console.log(arg1); //=> 1
}
zeroCheck(0,2);
A generic function to check for all arguments (returns an Array)
function zeroCheckArgs(args){
return [].slice.call(args).map(function(a){return a || 1;});
}
//more conservative
function zeroCheckArgsAlt(args){
var retArgs = [];
for (var i=0;i<args.length;i+=1){
retArgs.push(args[i] || 1);
}
return retArgs;
}
function some(){
var args = zeroCheckArgs(arguments);
console.log(args);
}
function someAlt(){
var args = zeroCheckArgsAlt(arguments);
console.log(args);
}
some(1,0,0,12,12,14,0,1); //=> [1, 1, 1, 12, 12, 14, 1, 1]
someAlt(1,0,0,12,12,14,0,1); //=> [1, 1, 1, 12, 12, 14, 1, 1]
It works for me, check this example fiddle
#nnnnnn -
"For non-strict mode functions the array index (defined in 15.4) named
data properties of an arguments object whose numeric name values are
less than the number of formal parameters of the corresponding
function object initially share their values with the corresponding
argument bindings in the function’s execution context. This means that
changing the property changes the corresponding value of the argument
binding and vice-versa. This correspondence is broken if such a
property is deleted and then redefined or if the property is changed
into an accessor property. For strict mode functions, the values of
the arguments object‘s properties are simply a copy of the arguments
passed to the function and there is no dynamic linkage between the
property values and the formal parameter values."
Your citation actually answers my original question. The reason that my code as posted below does not work as expected is because I was actually using "strict" mode.
// check all arguments, and make sure they aren't zero
function zeroCheck(arg1,arg2) {
var i, argsLen = arguments.length;
for (i = 0; i <= argsLen; i += 1) {
if (arguments[i] === 0) {
// This is where it doesn't behave as I expected
arguments[i] = 1; // make arg1 = 1
}
}
console.log(arg1); // arg1 = 0
}
zeroCheck(0,2);
It worked in for xdazz, Jeroen Moons, and jFriend00 - because they did not include the strict:
http://jsfiddle.net/nSJGV/ (non-strict)
http://jsfiddle.net/HDjWx/ (strict)