Reflect.set not working as intended - javascript

It seems unlikely that I found some kind of glaring cross-browser bug. But according to the documentations Reflect.set is supposed to use the 4th parameter as the thisArg (for example if the set variable is a setter). The first argument is the object the value is to be set on, yet whenever I supply any object as the 4th argument, the value gets set on that instead of the target object.
var target = new Object;
var thisArg = new Object;
Reflect.set(target, 'variable', 52, thisArg);
target.variable == undefined
thisArg.variable == 52
Any explanation?

The first argument is the object the value is to be set on
Not exactly. The first argument is the object whose setters are invoked (including those on the object's prototype chain).
whenever I supply any object as the 4th argument, the value gets set on that instead of the target object.
Yes. Because the property always gets set on the receiver. It's just that the argument is optional because it's usually the same as the target, and therefore defaults to the first argument when not supplied.

Related

Changing array member within local scope changes member in global scope

Why does JavaScript treat the scope of an array differently than it does the scope of other variables? Typically, if one passes a variable in a global scope into a function as a parameter, it is then local and changing it within the function does not change the value of the global variable. For example:
var globalInt = 1;
function test1(x){
x = x + 1
}
test1(globalInt);
console.log(globalInt); //globalInt is still 1
However, it appears the same does not apply when passing an array of values.
var globalArray = ["TEST"];
function test(x){
x[0] = x[0].toLowerCase()
}
test(globalArray);
//globalArray is now ["test"] instead of ["TEST"]
console.log(globalArray[0]);
This happens for me when I test in Chrome, but I have not tested it in other browsers so far. Why does this happen and does it happen in other browsers?
This is simply because an array (which is an Object) are passed by reference while primitives are not. Note that technically it is passed by value but in this case that value is a reference, thanks Esteban
A object is automatically passed by reference, without the need to specifically state it
If you pass an object (i.e. a non-primitive value, such as Array or a user-defined object) as a parameter and the function changes the object's properties, that change is visible outside the function, as shown in the following example:
MDN Link
Yes... JavaScript objects and arrays (which are objects) are passed by reference. But you should remember to read the method signature explicitly because some array methods don't alter the original array but return a new one. Read the MDN documentation carefully about the method in question. Array.prototype.map() would actually leave the original array intact and return a brand new array which you'd need to set to assign to a variable.

Why would you pass 'null' to 'apply' or 'call'?

According to this JavaScript reference:
The value null is a JavaScript literal representing null or an "empty"
value, i.e. no object value is present. It is one of JavaScript's
primitive values.
function getMax(arr){
return Math.max.apply(null, arr);
}
Wouldn't explicitly passing the keyword this be clearer, or at least more readable? Then again, at this point I may not understand why you would use null.
Why would you pass 'null' to 'apply' or 'call'?
When there is no value you wish to specify for the this pointer inside the function and the function you're calling is not expecting a particular this value in order to function properly.
Wouldn't explicitly passing the keyword this be clearer? Or at least
more human readable. Then again at this point I may not understand why
you would use null.
In your specific case, probably the best thing to pass is the Math object:
function getMax(arr){
return Math.max.apply(Math, arr);
}
While it turns out that it doesn't matter what you pass as the first argument for Math.max.apply(...) (only because of the implementation specifics of Math.max()), passing Math sets the this pointer to the exact same thing that it would be set to when calling it normally like Math.max(1,2,3) so that is the safest option since you are best simulating a normal call to Math.max().
Why would you pass 'null' to 'apply' or 'call'?
Here are some more details... When using .call() or .apply(), null can be passed when you have no specific value that you want to set the this pointer to and you know that the function you are calling is not expecting this to have any specific value (e.g. it does not use this in its implementation).
Note: Using null with .apply() or .call() is only usually done with functions that are methods for namespace reasons only, not for object-oriented reasons. In other words, the function max() is a method on the Math object only because of namespacing reasons, not because the Math object has instance data that the method .max() needs to access.
If you were doing it this way:
function foo() {
this.multiplier = 1;
}
foo.prototype.setMultiplier = function(val) {
this.multiplier = val;
}
foo.prototype.weightNumbers = function() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += (arguments[i] * this.multiplier);
}
return sum / arguments.length;
}
var x = new foo();
x.setMultiplier(3);
var numbers = [1, 2, 3]
console.log(x.weightNumbers.apply(x, numbers));
When the method you are calling .apply() on needs to access instance data, then you MUST pass the appropriate object as the first argument so that the method has the right this pointer to do its job as expected.
Calling apply with null as the first argument is like calling the function without providing any object for the this.
What does the apply method do?
The apply() method calls a function with a given this value and
arguments provided as an array (or an array-like object).
fun.apply(thisArg, [argsArray])
thisArg
The value of this provided for the call to fun. Note that this may not
be the actual value seen by the method: if the method is a function in
non-strict mode code, null and undefined will be replaced with the
global object, and primitive values will be boxed.
Further documentation can be found here.
One case where I have found this useful is when the function I'm calling is already bound to a particular context.
Because bound functions cannot be rebound, and they will always be called with the thisArg that was passed into bind, there is no use in passing a thisArg into call or apply. From source:
The bind() function creates a new bound function (BF).... When bound function is called, it calls internal method [[Call]] on [[BoundTargetFunction]], with following arguments Call(boundThis, args).
Here's an example:
class C {
constructor() {
this.a = 1;
}
}
function f(n, m) {
console.log(this.a + n + m);
}
let c = new C();
var boundF = f.bind(c, 2); // the context `c` is now bound to f
boundF.apply(null, [3]); // no reason to supply any context, since we know it's going to be `c`
I am bit late to answer this. I will try to give a long descriptive explanation here.
What is null in JavaScript?
The value null is a literal (not a property of the global object like undefined can be). It is one of JavaScript's primitive values.
In APIs, null is often retrieved in place where an object can be expected but no object is relevant.
fun.apply(thisArg, [argsArray])
thisArg: The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray: An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
If you are using 'strict mode', then it is advisable to pass the this or
Math as the parameter.
Apply is useful when you want to pass along the responsibility for doing something to a function that is determined at run time, and pass a variable number of arguments to that function. You may or may not have any appropriate "this" context when you're doing that.
For example I use a library I wrote to facilitate listening for and raising application events that uses apply.
I wanted to be able to be able to raise an event like this:
EventManager.raise('some:event-name', arg1, arg2, arg3, ..);
..and have all of the registered handlers for that event get called with that list of arguments (arg1, arg2, etc). So in the raise function, it goes through the handlers that are registered for that event name and calls them, passing all the passed in arguments except for the event name, like this:
var args = [];
Array.prototype.push.apply(args, arguments);
args.shift();
for (var l in listeners) {
var listener = listeners[l];
listener.callback.apply(listener.context, args);
}
When a registered handler (listener.callback) is called, apply is used to pass along a variable number of arguments. Here I have allowed the listener to supply a this context for its event handler when the listener is defined, but that context might not be defined or it might be null, and that's perfectly fine.
For a long time the raise function didn't even facilitate using any callback context. I eventually came across a need for it, so I put in support for it, but most of the time I don't really need or use it.

Can someone explain this function in JavaScript [duplicate]

This question already has answers here:
Applying a Function to Null in Javascript
(5 answers)
Closed 7 years ago.
I am learning about call and apply in javaScript from a online TUT. This function allows more arguments to be passed, rather than having a fixed amount.
var calculate = function(){
var fn = Array.prototype.pop.apply(arguments);
return fn.apply(null, arguments);
};
What I am having difficulty wrapping my head around is this statement.
var fn = Array.prototype.pop.apply(arguments);
The presenter of the of the TUT, explained it as the following:
We are binding the apply method onto the arguments object. This is going to give us the Function Object and assign it to the fn variable. It will also remove the Function Object from the argumentsObject. Because the Array's pop method takes the final element in the array, it removes it from the Array and then assigns to what ever called the method. In this case the fn variable.
What confused me was the following:
We are binding the apply method onto the arguments object. This is
going to give us the Function Object
It will also remove the Function Object from the arguments
Object.
And when we write in the return statement:
return fn.apply(null, arguments);
Why are we including null?
Array.prototype.pop.apply(arguments);
When you have a function, there's automatically an arguments objects, which is an Array-like object of arguments. If you call this fake function:
someFunction('hello', 'world');
and someFunction looks like this:
function someFunction() {
console.log(arguments);
}
The console.log will output ['hello', 'world']. However, don't be confused... That is not an Array object! It is an "array-like" object. Therefore, you can't say arguments.pop()... because arguments doesn't have that method (it belongs to Array.prototype). However, normal Array objects do have access to Array.prototype (e.g. [1,2,3].pop() // => [1,2]).
When you say .apply(), the first argument is the context... It sets the this. So really, Array.prototype.pop.apply(arguments) is a clever way of mimicking arguments.pop(). But you can't do arguments.pop(), because it doesn't have a pop method.
In return fn.apply(null, arguments);, null is the first arguments because we don't need to set a new context for this example. arguments is the second arguments because it's being passed in to use with fn.
.apply() returns a function object, so it returns something like this:
function() { ... }
We can then later invoke that function.
By the way, .pop() mutates the original object (in this case, the array-like object arguments). So you're passing in arguments to fn, but it's missing the last item that was in it previously.
According to MDN:
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg:
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray:
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
The last argument passed to calculate is assumed to be a function. It is popped from the arguments list. (Using apply because arguments is not a real array.)
This popped function (fn) is called with the rest of the arguments list. (All other arguments passed to calculate). The arguments list no longer contains fn because pop() modifies the original object.
NULL is used because fn is called without a value for this. (See MDN)
If you call calculate for instance like
calculate(2, 3, function(a, b){ return a + b });
it will return 5.

Javascript check arguments for zero value

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)

Where is arguments property defined?

Consider the following code
function add(x, y) {
alert(arguments.length);
var total = x + y;
return total;
}
add(); // NaN alerts 0
add(2,3); // 5 alerts 2
add(3,3,5); //6 alerts 3
Where is arguments defined? How come it is available inside my add function?
It is automatically created for all functions.
See https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments
A detailed explanation about when and how the arguments object is created:
From the ECMAScript specification:
10.1.8 Arguments Object
When control enters an execution context for function code, an arguments object is created and
initialised as follows:
The value of the internal [[Prototype]] property of the arguments object is the original Object prototype object, the one that is the initial value of Object.prototype (see 15.2.3.1).
A property is created with name callee and property attributes { DontEnum }. The initial value of this property is the Function object being executed. This allows anonymous functions to be recursive.
A property is created with name length and property attributes { DontEnum }. The initial value of this property is the number of actual parameter values supplied by the caller.
For each non-negative integer, arg, less than the value of the length property, a property is created with name ToString(arg) and property attributes { DontEnum }. The initial value of this property is the value of the corresponding actual parameter supplied by the caller. The first actual parameter value corresponds to arg = 0, the second to arg = 1, and so on. In the case when arg is less than the number of formal parameters for the Function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa.
That is just a standard feature of Javascript. Whenever any function is called, there is an arguments array automatically added to the local scope. This allows a function to receive a variable or unknown amount of parameters from the caller, and dynamically use these as necessary.
Commonly this is used when one function is placed as a wrapper around another where the exact parameters are unknown and arbitrary to the wrapper, who simply performs an action and then passes the provided arguments directly into the wrapped function.
arguments is a property of function objects. See Using the arguments object or Property: Function: arguments for more information.
It's worth noting that arguments is not a "real" array, the documentation calls it an "array-like object" - more in Turning JavaScript's arguments object into an array.

Categories