If I call a function like this:
var n = 0; f(n, n += 100)
the values of it's arguments are 0 and 100, not 100 and 100 as I would have expected. This behavior does what I currently need in my program but I don't understand why it works this way and it troubles me.
Can someone explain why it happens so and whether it's safe to pass arguments like this?
The first parameter value is evaluated before the second one.
Unlike C (and like Java), Javascript does in fact have a defined order of argument evaluation: left-to-right. So, somewhat surprisingly, it is safe to write the code as you have. However, I would consider it very poor style and in fact almost unreadable since not many people know off the top of their heads whether the evaluation order is in fact defined (case in point: I had to look it up to answer this question).
Edit: See section 11.2.4 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
The variable n is a primitive type in JavaScript (the primitive types include numbers, string, and boolean values). Primitive types are passed by value, explaining the behavior you observed. If n were a non-primitive type, it would be passed by reference and any modifications made to it would be reflected in all references to n.
See JavaScript: Passing by Value or by Reference for additional information.
Related
Trying to understand the difference between null and undefined in JavaScript I came across this answer where it mentions
null is an assignment value. It can be assigned to a variable as a representation of no value
The question is, once you define a variable like:
var someVariable = null;
Isn't someVariable defined some place in the memory? then shouldn't it have random 0s and 1s already in it unless assigned specific value otherwise?
On a bit level what does null represent? Does it mean bits empty of 0 or 1?
You seem to be confused by that answer stating that "null is not a value", or that it means "a variable having no value". That's wrong, and that's not what it says.
The null value is very much a value in the domain of the JS language. It's a primitive value just like undefined, true, NaN, 0 or 42.
The answer you quoted says that this value can represent a "no value" in your application domain. Putting the value null there means that it has no other value, e.g. being "neither true nor false".
(And whichever JS value we are looking at, we are not concerned with its bit-level representation in memory, like which bit exactly would distinguish a string from an integer. They're just values that we can work with in our program, and there are no others.
In fact a let variable in JS can indeed have a state of being uninitialised, but that doesn't mean there is a random bit pattern we can read back, but rather it's an explicit state that causes the program to throw an exception when the variable is evaluated. And var variables are always initialised, with the undefined value when a scope is created.)
Firstly, when programming in JS, you should never worry about what exactly is held in memory when you declare a variable. It is a high-level language with automatic memory management (including garbage collection) - not like C where I believe you can deal with the actual memory if you want to. (NB I've never programmed in C or a low-level language like it, so anything I say about that language should be taken with more than a grain of salt.) JS variables are just an abstraction where you use a name as a "container" for a particular value (which can change over time - hence the name "variable").
As for why JS has two "nothing" values in null and undefined - this is a very good question, and is likely just a quirk of the language. undefined is the best representation of "no value", since it's the value that declared variables will hold before they've been given another value, the default return value from a function where you just put return; or leave off the return statement, and the value automatically assigned to any function parameters which are in the function's signature but not passed in when called.
As for null, I'm not sure why it needs to exist - but it has since the beginning. Largely by convention, it tends to be used by developers of certain libraries, in places where an object is expected but none actually exists. (Despite the fact that, as I commented on another answer, null is not itself an object vale.) MDN, for example, highlights this usage: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null#Description
Eclipse has an option to warn on assignment to a method's parameter (inside the method), as in:
public void doFoo(int a){
if (a<0){
a=0; // this will generate a warning
}
// do stuff
}
Normally I try to activate (and heed) almost all available compiler warnings, but in this case I'm not really sure whether it's worth it.
I see legitimate cases for changing a parameter in a method (e.g.: Allowing a parameter to be "unset" (e.g. null) and automatically substituting a default value), but few situations where it would cause problems, except that it might be a bit confusing to reassign a parameter in the middle of the method.
Do you use such warnings? Why / why not?
Note:
Avoiding this warning is of course equivalent to making the method parameter final (only then it's a compiler error :-)). So this question Why should I use the keyword "final" on a method parameter in Java? might be related.
The confusing-part is the reason for the warning. If you reassign a parameter a new value in the method (probably conditional), then it is not clear, what a is. That's why it is seen as good style, to leave method-params unchanged.
For me, as long as you do it early and clearly, it's fine. As you say, doing it buried deep in four conditionals half-way into a 30-line function is less than ideal.
You also obviously have to be careful when doing this with object references, since calling methods on the object you were given may change its state and communicate information back to the caller, but of course if you've subbed in your own placeholder, that information is not communicated.
The flip side is that declaring a new variable and assigning the argument (or a default if argument needs defaulting) to it may well be clearer, and will almost certainly not be less efficient -- any decent compiler (whether the primary compiler or a JIT) will optimize it out when feasible.
Assigning a method parameter is not something most people expect to happen in most methods. Since we read the code with the assumption that parameter values are fixed, an assignment is usually considered poor practice, if only by convention and the principle of least astonishment.
There are always alternatives to assigning method parameters: usually a local temporary copy is just fine. But generally, if you find you need to control the logic of your function through parameter reassignment, it could benefit from refactoring into smaller methods.
Reassigning to the method parameter variable is usually a mistake if the parameter is a reference type.
Consider the following code:
MyObject myObject = new myObject();
myObject.Foo = "foo";
doFoo(myObject);
// what's the value of myObject.Foo here?
public void doFoo(MyObject myFoo){
myFoo = new MyObject("Bar");
}
Many people will expect that at after the call to doFoo, myObject.Foo will equal "Bar". Of course, it won't - because Java is not pass by reference, but pass by reference value - that is to say, a copy of the reference is passed to the method. Reassigning to that copy only has an effect in the local scope, and not at the callsite. This is one of the most commonly misunderstood concepts.
Different compiler warnings can be appropriate for different situations. Sure, some are applicable to most or all situations, but this does not seem to be one of them.
I would think of this particular warning as the compiler giving you the option to be warned about a method parameter being reassigned when you need it, rather than a rule that method parameters should not be reassigned. Your example constitutes a perfectly valid case for it.
I sometimes use it in situations like these:
void countdown(int n)
{
for (; n > 0; n--) {
// do something
}
}
to avoid introducing a variable i in the for loop. Typically I only use these kind of 'tricks' in very short functions.
Personally I very much dislike 'correcting' parameters inside a function this way. I prefer to catch these by asserts and make sure that the contract is right.
I usually don't need to assign new values to method parameters.
As to best-practices - the warning also avoids confusion when facing code like:
public void foo() {
int a = 1;
bar(a);
System.out.println(a);
}
public void bar(int a) {
a++;
}
You shoud write code with no side effect : every method shoud be a function that doesn't change . Otherwise it's a command and it can be dangerous.
See definitions for command and function on the DDD website :
Function :
An operation that computes and returns a result without observable side effects.
Command : An operation that effects some change to the system (for
example, setting a variable). An
operation that intentionally creates a
side effect.
I'm working through Learning Javascript Properly and am reading Chapter 4 of Professional Javascript for Web Developers. On p. 86 they say
Primitive values can’t have properties added to them even though
attempting to do so won’t cause an error. Here’s an example:
var name = “Nicholas”;
name.age = 27;
alert(name.age); //undefined
They also say that JavaScript does not treat strings as objects, like other languages do. I wanted to see if I could get it to print out name.age.
I tried
var name = "Nicholas";
name.age = 27, alert(name.age);
And got undefined.
Trying
var name = "Nicholas";
name.age = 27 & alert(name.age);
Also gave undefined.
But,
var name = "Nicholas";
alert(name.age = 27);
gives 27!
With regards to the text's original example, the author says
"Here a property called age is defined on the string name and assigned
a value of 27. On the very next line, however, the property is gone.
Only reference values can have properties defined dynamically for
later use."
What is going on with the comma separated assignment and function call - I knew you could use commas to separate variable assignments, but you can also do function calls? What are the limits to the comma in javascript?
How does the & operator work for chaining together code snippets? What is that operator and what should it be used for?
Why did my last example work when the other ones wouldn't? Does this have to do with scope?
Edit: Thanks JLRishe and SirReal. I didn't really understand JLRishe's answer until reading SirReal's, but I recommend reading both!
Really, the answer to all three of your questions has to do with how the ,, & and = operators are defined, and has nothing to do with properties on primitive values.
What is going on with the comma separated assignment and function call
Your second example is an example of the comma operator, and this is different from the comma used in variable declarations. It evaluates each of its operands one at a time from left to right, and the ultimate value of the expression is the value of the last operand.
How does the & operator work for chaining together code snippets?
The & operator evaluates its two operands from left to right and produces the result of applying a bitwise AND to them. Your alert() is executing as a side effect of this. The expression 27 & alert(name.age) evaluates to 0, and this is what is assigned to name.age (but this doesn't really do anything). You can see similar behavior with:
name.age = 27 * alert(name.age);
or
name.age = 27 - alert(name.age);
or several other operators.
Why did my last example work when the other ones wouldn't?
What you observed in your last example is the well-defined behavior of the = operator. It performs an assignment and produces the value of the right-hand side. The assignment doesn't actually happen in this case, for the reasons you quoted, but it evaluates to the value of the right-hand nonetheless, and that is what is passed to the alert() function.
& is bitwise AND, a comparison operator. It's used to compare values at the bit level. You should probably not be using this for chaining code together.
, (comma) is the comma operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
There don't appear to be any limits to that, but there are not a lot of good reasons to abuse this. MDN suggests that this is mostly for working with multiple values in for loops.
=, the simple assignment operator allows chaining. That means x = y = 2 is valid and will result in x == 2 and y == 2. I haven't found explicit documentation to support this, but I think it's safe to infer that the result of the assignment is returned when using =, which is the strange behavoir you're seeing. While you can't actually set a property on a string, the value you try to set is returned in case you wanted to chain it or otherwise use it.
You can't think like this in javascript. Variable assignment will work in this context if the variable can be assigned but has no bearing on the value passed to the function, that is, in this case 27, not name.age.
var name = "Nicholas";
alert(name.age = 27);
This will always alert an integer of 27. This is because you are not just assigning the name.age property a value, you are also passing the value to the alert function regardless of the property you attempted to assign the value to.
Comma operators string together operands and evaluate them in the order that you define them. As they are evaluated separately name.age has no value when it is evaluated as the second operand in your example.
The & operator is a bitwise operator that converts both sides to integers and evaluates both sides as their 32 bit binary representations. As other comments and answers have pointed out if either the left or right side is NaN it is treated as 0.
Irrelevant detail added in original answer below
&& evaluates left to right, similar to comma operators, however if the left side evaluates to false, the right side is never evaluated. This allows you to chain a check and an evaluation together without errors. This is called short circuiting.
callback instanceof Function && callback();
The above example checks if callback is a function, only if it is TRUE will the right hand side be evaluated.
The reason alert(name.age = 27) works is because of how primitive wrapper types operate. Primitive values like booleans, numbers, and strings in javascript are treated as objects in certain special cases, permitting the use of methods (like substring()).
Normally, a primitive value would not be able to call a method since it is considered a primitive, not an object (reference type). However, there are primitive wrapper types that enable this to work behind the scenes.
The three primitive wrapper types are Number, Boolean, and String and correspond to their respective primitive values. When you execute a method on a primitive value what is actually happening is the following:
The primitive value is cast as an instance of the primitive wrapper type
The method is executed on the primitive wrapper instance (an object)
The primitive wrapper instance is destroyed (your value goes back to being a regular primitive value).
The variable name in the code example is a string primitive value. When the property .age is added to it, it must first be cast as an instance of the primitive wrapper type String. However, that instance is immediately destroyed after that call executes, which is why the only call that does not return undefined is the one that alerts the property at the same time it is assigned.
The major difference between reference types and primitive wrapper types is the lifetime of the object. When you instantiate a reference
type using the new operator, it stays in memory until it goes out of
scope, whereas automatically created primitive wrapper objects exist
for only one line of code before they are destroyed. This means that
properties and methods cannot be added at runtime. Take this for
example:
var s1 = “some text”;
s1.color = “red”;
alert(s1.color);//undefined
Here, the second line attempts to add a color property to
the string s1. However, when s1 is accessed on the third line, the
color property is gone. This happens because the String object that
was created in the second line is destroyed by the time the third line
is executed. The third line creates its own String object, which
doesn’t have the color property. (Zakas, 147)
This is also why the following code would have worked:
var name = new String("Nicholas");
name.age = 27;
alert(name.age); //27
Here we have an explicit construction of an instance of the String primitive wrapper type, so it survives as an actual object.
Source: Zakas, Professional Javascript for Web Developers, p. 147 3rd ed.
I'm a Javascript novice so please excuse the fundamental question.
I'm working my way through 'Professional Javascript For Web Developers' and in Chapter 3, "Understanding Arguments" section, it discusses accessing function arguments with the arguments[] keyword.
One of the examples shows that you can modify the values in arguments[]:
function twoNums(num1, num2) {
arguments[1] = 10;
console.log(arguments[0] + num2);
}
twoNums(4,8); output = 14
But it goes on to say that "This effect goes only one way: changing the named argument does not result in a change to the corresponding value in arguments."
However, changing the code to:
function twoNums(num1, num2) {
num2 = 10;
console.log(arguments[0] + arguments[1]);
}
twoNums(4,8); output = 14
results in the same output so the value in 'arguments[1]' is definitely changing.
Is this:
an error in the book?
an error in my understanding?
something that has changed in Javascript since the writing of the book?
Thanks,
Neil
ANSWERED: A combination of answers solved my problem. Thanks everyone.
It is supposed to work that way, unless in strict mode:
function foo(a) {
"use strict";
console.log(a, arguments[0]);
a = 10;
console.log(a, arguments[0]);
arguments[0] = 20;
console.log(a, arguments[0]);
}
foo(1);
// output:
// 1 1
// 10 1
// 10 20
The ES5 specification addresses that on section 10.6:
NOTE 1 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.
Maybe it worked differently on ES3 (the previous version), but I doubt it (since they had to add a special case for strict mode).
Interesting fact: the presence of an eval call in the function can influence how the arguments object behave in some browsers, which is extremely weird. The use of the non-standard functionName.arguments reference also has an impact. See Why does an unexecuted eval have an effect on behavior in some browsers?, and my answer to it.
There is an error in the book, by the looks of things: if you reassign any of the arguments passed, the arguments object will change (both reference the same value).
Perhaps, though, what was meant in the book was this:
function f(n1, n2)
{
arguments[0] = 2;
console.log(arguments[0] + arguments[1]);
}
f(1, 2);//logs 4
f(1234,2);//logs 4
But, honestly, it shouldn't really matter. The arguments object should be treated as a read-only object. It's a good idea to uphold the mantra "Don't change objects you don't own" in JS. It's a bad idea trying to change the Object.prototype, as it is not the best of ideas to change the behaviour of any object (console, window...) by deleting and adding methods at random.
If you want to get some more details on arguments, or anything else MDN is there to help. I've not looked at all code examples there, but AFAIKT there's no code that effectively changes the arguments object.
Some time ago I think I read an article my Douglas Crockford on the matter, where he gave an example of how changing the arguments object actually lead to unexpected behaviour (arguments swapping places and all that).
Edit:
I'd thought I'd not go into strict mode, but as bfavaretto's answer pointed out: strict-mode actually does make the arguments object a read-only object. That's terrific news, and now I have all the more reason to love the way JS is going. ES6 will introduce block scoping and probably make the arguments object read-only all the time (at least, I hope it will).
it will work that way only in strict mode..
"use strict"
The Arguments object has one very unusual feature. In non-strict mode,
when a function has named parameters, the array elements of the
Arguments object are aliases for the parameters that hold the function
arguments. The numbered elements of the Arguments object and the
parameter names are like two different names for the same variable.
Changing the value of an argument with an argument name changes the
value that is retrieved through the arguments[] array. Conversely,
changing the value of an argument through the arguments[] array
changes the value that is retrieved by the argument name. Here is an
example that clarifies this:
function f(x) {
console.log(x); // Displays the initial value of the argument
arguments[0] = null; // Changing the array element also changes x!
console.log(x); // Now displays "null" } This is emphatically not the behavior you would see if the Arguments object
were an ordinary array. In that case, arguments[0] and x could refer
initially to the same value, but a change to one would have no effect
on the other.
This special behavior of the Arguments object has been removed in the
strict mode of ECMAScript 5. There are other strict-mode differences
as well. In non-strict functions, arguments is just an identifier. In
strict mode, it is effectively a reserved word. Strict-mode functions
cannot use arguments as a parameter name or as a local variable name,
and they cannot assign values to arguments.
Here is a great (and recent) article on this topic:
https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-8/function-arguments-and
I Suggest you use it :)
I quite often have optional arguments in functions, but some testing is showing a huge performance hit for them in firefox and safari (70-95%). Strangely, if I pass in the literal value undefined then there is no penalty. What could be happening here? I wouldn't have thought that it was a scope chain issue as they are inherently local to the function. Am I to start passing undefined into every optional argument?
jsPerf: http://jsperf.com/function-undefined-args/2
For a function like this:
function threeArgs(x, y, z) {
return x + y + z;
}
that's called like this:
threeArgs(1, 2, 3);
the optimizer is free to make the choice to generate no code at all. It's fairly easy for it to determine that there are no side effects, because the function simply references its parameter values and returns the result of a simple expression. Since the return value is ignored, there's no reason for the runtime to do anything at all.
Beyond that, if the code were:
something += threeArgs(1, 2, 3);
the optimizer might decide to generate code roughly equivalent to:
something += 6;
Why? Because the call was made with numeric constants, and it can safely fold those at code generation time. It might be conservative on that, because numbers are weird, but here they're all integers so it could well do this. Even if it didn't, it could safely inline the function:
something += 1 + 2 + 3;
When there's a parameter missing, however, it may be that the optimizers bail out and generate a real function call. For such a simple function, the overhead of the function call could easily account for a large difference in performance.
By using variables instead of constants in a test, and by actually using the return value of the function, you can "confuse" the optimizer and keep it from skipping the call or pre-computing the result, but you can't keep it from inlining. I still think that your result is interesting for that reason: it exposes the fact that (as of today anyway) those optimizers are sensitive to the way that functions are invoked.
I think what could explain the performance difference is the way arguments are passed to a function object: via the arguments object. When not passing any arguments, JS will start by scanning the arguments object for any of the given arguments, when those are undefined, The arguments prototype chain will be scanned, all the way up to Object.prototype. If those all lack the desired property, JS will return undefined. Whereas, passing undefined explicitly, sets it as a property directly on the arguments object:
function foo(arg)
{
console.log(arguments.hasOwnProperty('0'));
}
foo();//false'
foo('bar');//true
foo(undefined);//true
I gather that's the reason why passing undefined explicitly tends to be faster.