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.
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
In articles about closures, you will often see closures being created inside of loops using self-invoking functions to pass the iterator variable to a returned function expression in order to create a closure around the value of the iterator variable at the time the self-invoking function was invoked, rather than its value after the loop finishes. Here is an example:
var func = [];
for (var i = 0; i < 3; i++)
{
func.push((function(j){ return function(){ console.log(j); }; })(i));
}
// Logs
// 0
// 1
// 2
// to the console
for (var i = 0; i < func.length; i++)
{
func[i]();
}
This technique works with both numbers and strings, based on my simple experiments. However, the same technique does not work with plain JavaScript objects. If an object is passed to a self-invoking function and referenced in a function expression, changes to the enclosed object are visible in the function expression because the value passed to the self-invoking function was a reference to an object and not a copy of the object, as is the case with numbers and strings.
I can understand why this technique would not work with a variable that stores an object, but I don't understand why this technique should work with numbers and strings, whose prototype chains terminate with Object.
Does this mean that strings and numbers are just special cases of objects that are handled differently by the interpreter, or am I suffering from a fundamental misconception?
First, it is not true that "all JavaScript types are objects". Primitive strings, numbers, and boolean values are not objects.
Second, everything in JavaScript is pass-by-value. It's important to understand what "pass-by-value" means. It means that when a function is called, like this:
var someVariable = something;
someFunction(someVariable); // <--- this is the function call
then what the language does is copy the value of someVariable and pass that copy to the function. What a "pass-by-reference" language would do is pass a reference to that variable to the function. Because a copy of the value of the variable is passed to the function in a pass-by-value world, the function has absolutely no way to modify the value of someVariable. In a "pass-by-reference" language, it does.
To some extent, C++ lets you employ either parameter passing scheme. JavaScript does not.
The fact that in JavaScript variables have object references as values sometimes does not mean the language is pass-by-reference. I know that that seems like a silly pedantic distinction, but it's important to understand that "pass-by-value" and "pass-by-reference" are precise terms used to describe language semantics. If they don't have precise meanings, they're useless for that purpose.
One more thing: JavaScript implicitly wraps string, number, and boolean primitive values in wrappers of the corresponding String, Number, and Boolean types when the primitive values are used as if they're objects. That's what happens when you do something as common as:
var five = "hello".length
The left-side operand of the . operator has to be an object, so there's no special-case here: there's an implicit promotion of the primitive string value to a String instance. (What the runtime really does under the covers, well, we can't tell and we shouldn't care. Conceptually, a temporary wrapper object is created, used, and thrown away.)
In javascript there are 6 primitive types: string,number,boolean,null,undefined,symbol - new in ECMAScript 2015. However there are Object wrapper classes for these primitives. All 6 except null and undefined have wrapper classes. Reference These primitive types are passed by value not by reference per the javascript design.
From what I understand, the (basic) string type in JavaScript is a primitive type, meaning its variables are allocated on the stack.
I would have thought that for a type to be allocatable on the stack, it needed to have a fixed size -- something which presumably holds true for the other primitive types like boolean, number, etc.
Am I somehow wrong to assume that, or is some other internal magic used to make strings in JavaScript primitive types?
EDIT:
This gets more complicated when one considers that JavaScript is loosely typed. Which makes me wonder how any local variable can be allocated on the stack.... given that the size of what might be assigned to it during the course of a function is not fixed.
But I guess (a perhaps simplified) answer to this might be that all local variables could be assigned a fixed maximum size on the stack. Say this is 8 bytes which I think is the size of the number type, and should be large enough to accommodate all the other primitive types (except the string) as well as memory addresses (for when a local variable is assigned a reference type). But, surely strings cannot be limited to 8 bytes (or any size for that matter). Which makes me conclude that strings (even the primitive type ones) are not (cannot be) assigned on the stack. And hence the term "Primitive type" in JavaScript is used to mean a "basic/building block" type, rather than one which is necessarily allocated on the stack (contradicting what I have read in numerous sources including the book "Professional JavaScript..." by Nicholas Zakas).
Anyone have any other take or a pointer to a good source talking about this?
A string is a both an object and a primitive.
When doing:
var s = "this is a string";
you actually do:
var s = new string("this is a string");
behind the curtains.
The first being a primitive array with characters, on which the second one refers.
Strings are immutable, meaning they can't be changed. If you try to change it (i.e. reverse it), you will create a new string primitive, on which the object reference will point to.
The storage used to represent variables in the Javascript interpreter need not look anything like a stack - it's implementation dependent.
Strings aren't allocated on the stack
where a variable is allocated doesn't distinguish primitives from objects
Strings aren't primitives, they are of the class "string"
The difference between a primitive and a type is that types have methods and you can assign new properties to them:
var a = 1, b = {}, s = '';
a.foo = 1; // doesn't work, but no error either
b.foo = 1; // works
s.foo = 1; // doesn't work, but no error either
console.log(a.foo);
console.log(b.foo);
console.log(s.foo);
gives
undefined
1
undefined
So all in all, I'm not sure that using "primitive" makes sense in JavaScript since the line is blurred.
A string is a "value object" which means you can't change any of the properties. For example, when you replace characters in a string, you get a new string; the old string doesn't change.
I've got a JavaScript that uses this notation to make sure an object is initialized:
MyObject = MyObject || {};
I understand what it does, namely checking wether MyObject is anything, if not assigning an empty object to MyObject. I don't really know all the internals of JS that well, so I don't see how a logic comparison could be used in an assignment.
How does it work? Is there any other languages that allows this?
Depends on the language.
For example, in Ruby, you could use this style.
But in PHP, you could not use this style, because in PHP, the Logical Operators always returns a boolean value (true/false).
Conclusion:
If the Logical Operators always return a boolean value, then you could not use this style.
If the Logical Operators returns the first value when first value evaluates true otherwise return the second value, then you could use this style.
This syntax means "assign MyObject to itself, if it's defined. Otherwise, give it the empty object."
It works because Javascript treats undefined values as false, and defined ones as true. Combine that with lazy evaluation of logical expressions, and you get the behavior described above.
The logical-'or' operator has short-circuit semantics, which means that the right-hand operand is only evaluated if the left-hand one evaluates as false. Thus if MyObject is initialized, the statement reads MyObject = MyObject; (which does nothing), and otherwise it is MyObject = {};.
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.