Passing `null` as context object vs directly calling the function - javascript

From this question, given that I don't want to specify any context, hence, passing null to thisArg in call().
What would be the difference between line 2 and line 3 in the following code? Is there any benefit from doing one over the other?
function sum(a,b) { return a + b; }
var result1 = sum.call(null,3,4); // 7
var result2 = sum(3,4); // 7
Similarly for apply():
var arr = [1,2,4];
var result3 = Math.max.apply(null, arr); // 4
var result4 = Math.max(...arr); // 4

It depends on whether the function you're calling was defined in loose mode or strict mode.
When calling a loose-mode function, the following three things all call the function such that this within the call is the global object for the environment (globalThis, aka window on browsers):
Calling the function without setting this: fn()
Calling the function providing a this value of undefined: fn.call(undefined) and similar
Calling the function providing a this value of null: fn.call(null) and similar
With a strict mode function, the first two both cause this during the call to be undefined, and the third (explicitly setting it to null) sets it to (you guessed it) null.
Examples:
function loose() {
console.log(`loose: ${this === null ? "null" : typeof this}`);
}
loose();
loose.call(undefined);
loose.call(null);
function strict() {
"use strict";
console.log(`strict: ${this === null ? "null" : typeof this}`);
}
strict();
strict.call(undefined);
strict.call(null);
In the normal case, if you don't need to set this to anything in particular, just call the function so the default behavior takes place.
One wrinkle: If you have an array of arguments you need to spread out as discrete arguments to the function, in any even vaguely up-to-date environment, you can use spread notation to do that: fn(...theArray). If you're stuck in an obsolete environment, the rough equivalent is fn.apply(undefined, theArray).
If you have a specific need to set a specific this value, you can do that via call or apply (as you've found), including (for strict mode functions) undefined or null.

TJ Crowder has the detailed response here, but as a general rule:
fn.call(null): In almost all cases, just call the function.
fn.apply(null, args): This is useful in some cases where you have an array of arguments and your environment doesn't support ...args, but otherwise spreading the arguments is probably more conventional: fn(...args)

Related

Extending Date prototype - can this ever be null?

I'm trying to extend the Date prototype in JavaScript like this:
Date.prototype.toLastDayOfPreviousMonth = function() {
return new Date(this.getFullYear(), this.getMonth(), 0);
}
However I'm not sure if I can ever expect this to be null here. My thinking is that if someone ever wants to do something like this:
var dateA = null;
dateA.toLastDayOfPreviousMonth();
The above function won't ever get called - because we're calling this on null and not Date. I'm not interested in quirks like calling the function through apply etc. Just 'genuine' usage date.toLastDayOfPreviousmonth();
One of the similar extensions I found had this check:
var obj = this instanceof Date ? this : null; Does it have any sense at all?
Not in sloppy mode. In that mode, the this value must always be an object. If you pass a null or undefined primitive, the this value end up being the global object.
However, strict mode allows arbitrary values. Then yes, this can be null.
"use strict";
Date.prototype.toLastDayOfPreviousMonth = function() {
return this === null;
};
Date.prototype.toLastDayOfPreviousMonth.call(null); // true
If you don't want to consider ways of manually passing a custom this value (e.g. call, apply, bind or Reflect), then
Either you are not calling the function as a method, in this case this will be the global object in sloppy mode and undefined in strict mode.
Or you are calling the function as a method of some object. In this case, this will be that object. null is not an option because it's not an object.
Or you are calling the function as a method of some primitive which can be wrapped in an object. In this case, this will be that object wrapper. null is not an option because it's not an object-wrappable.

Is it possible to pass undeclared variables as parameters in Javascript?

Let's say I have a variable myvar, and I don't have a variable myvar2. I can run the following without a problem:
typeof myvar
// ⇒ 'string'
typeof myvar2
// ⇒ 'undefined'
typeof and delete are the only functions I know of which don't throw errors when given an undefined parameter like this. I looked at the language spec for typeof and to my uninitiated eyes it seems to use internal functions like IsUnresolvableReference.
Edit: I'd been working in a language that checks type with a synonymous function, and hadn't noticed typeof is actually an operator in JavaScript. I've removed parentheses from the code here but left the above as written.
When I create a function:
function myFunc(input_variable) {
return("hello");
}
... as expected this throws a ReferenceError when passed myvar2 as a parameter, unless I run var myvar2;.
If I wrap the return in a try/catch statement to handle the myvar2 not defined case, I still get the same error, as the variable seems to be checked for a resolvable reference upon entry into the function (upon runtime?) :
function myFunc(input_var) {
try {
return "hello";
} catch(error) {
if (error.name === 'ReferenceError'){
return "world";
}
}
}
I was wondering how I can make a function that accepts unresolved references. My general guess is that, if it's a standard behaviour of functions, then perhaps I could modify some prototype for this construction specifically...? I'm aware prototypes are for objects, I'm wondering if this level of control over function is possible somehow?
By way of context, I always find myself writing function(input_var) :
if (typeof input_var == 'undefined' || my_settings.input_var_is_optional === true)
var input_var = 'Sometimes variables are optional. This is my default value.';
return dealWith(input_var);
} else if (typeof input_var == 'string') {
return dealWith(input_var);
} else {
// Already checked that input_var isn't optional, so we have a problem
return false; // or throw a TypeError or something like that
}
but the verbosity of all that plain puts me off writing type checking into my code, making it less robust to use functions more freely, or to pass onto other developers.
I'd like to write a type handling function, e.g.
For a function myFunc(input_var), if the variable passed in as parameter input_var has been defined, check if it's a string, else set it as "default_value". If it wasn't defined, also set it as "default_value", else it's a valid string, so just use input_var as is.
...but it's sabotaged by the fact that I can't actually pass anything in that's undefined, effectively stopping me from isolating this complexity in a separate function to which I could just pass 2 parameters: input_var (the real deal, not just its name), and expected_type.
function typeTest(input_var, expected_type) {
var is_optional_value = (typeof expected_type != 'undefined'
&& expected_type === true);
var optional_str = is_optional_value ? "|(undefined)" : ''
var type_test_regex = RegExp('^(?!' + expected_type + optional_str + '$)');
var is_expected_type = type_test_regex.test(typeof(input_var));
}
For example, to check that an optional variable passed into a function was both defined, and was defined as a string,
var myvar = 'abc'
// myvar2 is never defined
// Mandatory type (expecting a string):
typeTest(myvar, 'string'); // true
// if (/^(?!string)$)/.test(typeof(myvar))
typeTest(myvar2, 'string'); // throws error
// Mandatory type (expecting a number):
typeTest(myvar, 'number'); // false
typeTest(myvar2, 'number'); // throws error
// Optional type ("expected is true"):
typeTest(myvar, true); // true
// if (/^(?!string|(undefined)$)/.test(typeof(myvar))
typeTest(myvar2, true); // throws error
I was wondering how I can make a function that accepts unresolved references.
You can't. When you access an undeclared variable, the ReferenceError occurs before the function even gets called. There's nothing you can do inside the function to recover from this, because it hasn't even been called.
typeof and delete are the only functions I know of which don't throw errors when given an undefined parameter like this.
typeof and delete are not functions. That's why.
For example, to check that an optional variable passed into a function was both defined, and was defined as a string.
There's nothing stopping you from doing this. There is a difference between:
variables with the value undefined
parameters that have not been passed a value
undeclared variables.
There is no problem in dealing with the first two:
function hasType(val, type) {
return typeof val === type;
}
function myFunc(param1, param2) {
console.log('param1: ', hasType(param1, 'string'));
console.log('param2: ', hasType(param2, 'string'));
}
myFunc('hello');
There is no need to check whether someone is trying to call your functions with undeclared variables. If they are, then the problem is with their code and they need to fix it. If they are taking advantage of optional parameters, that is a different matter, and you can handle for that scenario just fine.
as the variable seems to be checked for a resolvable reference upon entry into the function
It is checked before entry.
Given foo(bar), the logic for resolution is "Get foo, then get bar, then call foo with the value of bar as an argument.
If bar isn't declared then you'll get a ReferenceError before the function is called in the first place.
typeof and delete are the only functions I know of which don't throw errors when given an undefined parameter like this.
From the documentation you link to:
The typeof Operator
The delete Operator
They aren't functions.
I was wondering how I can make a function that accepts unresolved references.
You can't.
For example, to check that an optional variable
If you want an argument to be optional then either:
Explicitly pass undefined:
typeTest(undefined, 'string');
Put the optional argument last in the arguments list:
typeTest('string');
Pass an object:
typeTest({ argument_name: 'string' });
You can using a function slide.
function attempt(f){
console.log(f());
}
attempt( function (){ return nomansland} );
//later an ajax call declares it:
var nomansland = "ok";
attempt( function (){ return nomansland} );

mocha assert.deepEqual when one of the values is a function?

Since functions are first-class objects, and can be passed inside of another js object, how can I do an assert in my tests to be sure I'm getting back the right function?
I'm using Q for promises, and mocha/chai/chai-as-promised for testing. My method returns different functions based on the if/else (I need to either redirect or use a different route).
I'll return something like:
fulfill({next: function(res) {return res.render('single-pages/site-map');}});
and my test looks like:
return assert.becomes(
page.helpers.checkIfSinglePage('site-map', null),
{next: function(res) {return res.render('single-pages/site-map');}}
);
but it's telling me that the returned values are not the same.
AssertionError: expected { next: [Function] } to deeply equal { next: [Function] }
Functions are compared by reference in JavaScript.
(function(){}) === (function(){}); // false
In fact, this is because functions are objects. At the moment (until ES7) everything in JavaScript except primitive value types (number, string, null, undefined, bool) is a reference and compares with reference equality checks.
You technically can check the two functions as two strings (comparing the code) and (assuming no old versions of firefox) it will compare equal for the same function - but that's a poor indication since two functions can mean opposite things:
var foo = (function(){
x = alert;
return function foo(){ x(); } // first function
})();
var bar = (function(){
x = console.log.bind(console,"BAR");
return function foo(){ x(); } // first function
})();
foo.toString() === bar.toString(); // true, but an incorrect check.
So to conclude, there is no way to know in JavaScript if two do the same without having a reference to them.
Instead, you can call .next and check that the rendered valuer is the same.

Is it not possible to set arguments that have not been provided, using arguments property

I was testing things. And discovered that i could not set arguments if i have not provided it.
Using arguments[1] etc.
Example:
function abc (a,b) {
arguments[1] = 'new value';
console.log(b);
}
abc('a');
It won't work.
I know i could set value like if (b=='undefined') b='default'; but why i can't like this. In other words this behavior is unexpected, isn't it?
On the other hand, if you do provide argument it will get changed!
calling function like this will output new value
abc('a','b');
is there a solution, if you wanted to set value using arguments[2] and pass argument when calling function.
after more testing it seems: b doesnt get connected with arguments[1] if not called.
My understanding is argument is dynamic whose length is set by number of arguments / parameters provided to the function.
In first case, you have provided only one parameter so size of argument is 1 and hence argument[1] should be out of bound, while in second case, you have argument of length 2 so you can change either parameter value by using its index.
After posting a terribly wrong answer, I took a look at the ECMA Spec and here is in code, what JavaScript does when the arguments object will be created:
function abc () {
var names = ["a", "b"]; //as normally provided in the function declaration: function (a, b)
var a, b;
var args = {};
var mappedNames = [];
args.length = arguments.length;
var index = args.length - 1;
//this is the crucial part. This loop adds properties to the arguments object
//and creates getter and setter functions to bind the arguments properties to the local variables
//BUT: the loop only runs for the arguments that are actually passed.
while (index >= 0) {
var val = arguments[index];
if (index < names.length) {
var name = names[index];
if (mappedNames.indexOf(name) === -1) {
mappedNames.push(name);
var g = MakeArgGetter(name);
var p = MakeArgSetter(name);
Object.defineProperty(args, index.toString(), {get : g, set : p, configurable : true});
}
}
args[index.toString()] = val;
index--;
}
function MakeArgGetter(name) {
return function zz(){
return eval(name);
};
}
function MakeArgSetter(name) {
return function tt(value) {
eval(name + " = value;");
};
}
console.log(args[0]); //ab
args[0] = "hello";
args[1] = "hello";
console.log(a); //hello
console.log(b); //undefined
}
abc('ab');
Note, that there is some more going on in the Spec and I simplyfied it here and there, but this is essentially what is happening.
What are you trying to achieve by this?
If you want to have "dynamic arguments" like abc('a) and abc('a', 'b') are both valid, but the first will set the missing argument to a default value, you either have to check for the arguments length and/or for all arguments values.
Suppose the following: abc('a', null) is given, what then? - depends on logic, can be good but can also be bad.
Given this, checking the argument vector isn't smarter but more cryptic and having something like if (b=='undefined') b='default' much straighter and better to understand.
TL;DR: Not possible per ECMA spec since arguments is binded to only formal parameters that match the provided arguments. Its recommended to only reference arguments and not alter it.
Examples: http://jsfiddle.net/MattLo/73WfA/2/
About Arguments
The non-strict arguments object is a bidirectional and mapped object to FormalParameterList, which is a list generated based on the number of provided arguments that map to defined parameters in the target function defintion/expression.
When control enters an execution context for function code, an arguments object is created unless (as specified in 10.5) the identifier arguments occurs as an Identifier in the function’s FormalParameterList or occurs as the Identifier of a VariableDeclaration or FunctionDeclaration contained in the function code. ECMA 5.1 spec (as of 03/26/2014)
arguments to variables / variables to arguments
Argument-to-variable mappings exist only internally when the method is invoked. Mappings are immutable and are created when a target function is invoked, per instance. The update bindings between the two is also disabled when the use strict flag is available within the scope of the function. arguments becomes strictly a reference and no longer a way to update parameters.
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. ECMA 5.1 spec (as of 03/26/2014)
What about length?
arguments.length can be an indicator initially to determine how many formal parameters were met but it's unsafe since arguments can be changed without length ever updating automatically. length doesn't update because it's not a real array and therefore doesn't adhere to the Array spec.

Is it risky to ask for a nonexistent javascript argument?

If I have a function
foo()
that I call with no arguments most of the time, but one argument in special cases, is
var arg1 = arguments[0];
if (arg1) {
<special case code>
}
inside the function a completely safe thing to do?
Yes it is safe. Unless you pass in false, "", 0, null or undefined as an argument. It's better to check againts the value of undefined. (If you pass in undefined then tough! that's not a valid argument).
There are 3 popular checks
foo === undefined : Standard check but someone (evil) might do window.undefined = true
typeof foo !== "undefined" : Checks for type and is safe.
foo === void 0 : void 0 returns the real undefined
But this is prefered
function myFunction(foo) {
if (foo !== undefined) {
...
} else {
...
}
}
Yes, that's fine. A reasonable alternative is to name the argument, and not use the arguments object:
function foo(specialArg)
{
if (specialArg)
{
// special case code
}
}
Note that if(bar) tests the truthiness of bar. If you call foo with any falsy value, such asfoo(0), foo(false), foo(null), etc., the special case code will not execute in the above function (or your original function, for that matter). You can change the test to
if (typeof specialArg !=== 'undefined')
{
// ...
}
to make sure that the special case code is executed when the argument is supplied but falsy.
You can do this:
function foo(arg1){
if (arg1){
// Special case
}
else{
// No argument
}
// Rest of function
}
As long as you document the behaviour sufficiently I don't see anything wrong with it.
However you'd be better off checking the argument length, as opposed to how you're doing it now. Say for example you called:
myFunction(0);
It will never process the argument.
If it's a single optional argument you may be better off having it as a named argument in the function and checking if a defined value was passed in, depends on your use case.
The basic fact you are interested in is "was foo called with 0 or 1 argument(s)?". So I would test arguments.length to avoid future problems with a special argument that evaluates to false.

Categories

Resources