简体   繁体   中英

Weird Code - Dynamic properties in primitive and reference variables in javascript (JS)

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:

  1. The primitive value is cast as an instance of the primitive wrapper type

  2. The method is executed on the primitive wrapper instance (an object)

  3. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM