JavaScript - Why does the increment operator modify immutable strings? - javascript

In JavaScript, strings are immutable. That means any operation on them returns a new object. Methods like trim, replace and slice don't modify the existing string.
However, I was playing around in jsconsole and found an exception
string = "string";
string + 37;
=> "string37"
string;
=> "string"
The original string hadn't changed here. Here's what happens when I apply the increment operator on the string. Here I am expecting to see string returned.
string++;
=> NaN
string
=> NaN
I was trying to see whether this would return strinh, like it would in some other languages.
Regardless of whether string++ would work, it shouldn't modify existing string objects. Yet it did just that. Does anyone know why?

That ++ operator works similarly to:
string = string + 1;
So, you are re-assigning to that variable. (while the original string remains immutable)
If you run string = string + 1 yourself, you'll notice it results in string1. This is because the 1 is being converted into a string and then treated as an "append". (again, never modifying the original string value)
However, the ++ does not attempt to coerce anything, it simply attempts to add a non-number (ie: string) to a number (1) and the result is NaN.

Here is a simple example:
var baz = "string";
baz = 5;
Certainly we have not modified the value of the string "string" by assigning baz to 5. Instead, we have simply done away with the string altogether and put the value 5 in its place. Similarly, your example does not alter any strings by assigning your variable to NaN.
You expect the variable string to be a constant, but it's not. Furthermore, constant variables and immutable values are different things. You haven't changed the immutable string "string" into NaN; you have substituted the immutable value "string" for a completely different value, NaN.
Your operations create new values by using the immutable string as an operand.
Consider a real constant variable in an environment that supports the const keyword:
> const foo = 5;
> foo++
5
Now consider a constant variable that is an object:
> const bar = {};
> bar.baz = 5
> bar
{ baz: 5 }
> bar = 10;
> bar
{ baz: 5 }
In this case, we mutated the value (because the value is not immutable) but could not alter which value the variable contains. Your example does not use constant variables, so the variable string can freely change its value.

Related

If a String is immutable in JavaScript why does the following code mutate a string?

I have read the input as a string and, after splitting it, the string becomes an object.
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data',(c)=>{
in += c;
});
process.stdin.on('end',()=>{
spliter(in);
});
function spliter(in){
console.log(typeof(in));
in = in.split('\n');
console.log(typeof(in));
}
Your code does not mutate the string.
Strings (along with the other primitive types) are immutable in JS.
Mutating something means that changing it without creating another one.
String modifier methods return a new string, but doesn't change the original, for example:
const a = 'Hello world!'
const b = a.slice(0,5)
console.log(a) //Hello world!
console.log(b) //Hello
However, you can still reassign a string variable with a new string (but that's not mutation):
let a = 'Hello world!'
a = a.slice(0,5)
console.log(a) //Hello
Your code is a bit more complicated. String#split() returns an array of strings, but doesn't mutate the original:
const a = 'Hello world!'
const b = a.split('o')
console.log(a) //Hello world!
console.log(b) //['Hell', ' w', 'rld!']
Arrays are (in fact) objects, and they are mutable, but not the strings (and other primitives) they contain.
As others have pointed out, in the example provided, the string primitive is not being mutated.
The variable is being re-assigned to the value returned by the split() method, which is being called on a String wrapper object, which is automatically used when calling a method on a string. ​
let myString = 'Hello World!';
console.log("BEFORE reassignment - variable type is: " + typeof(myString)); // string
myString = myString.split(' '); // <----- method called on String object wrapper, returns an array, which is re-assigned to the myString variable
console.log(myString); // ['Hello', 'World!']
console.log("AFTER reassignment - variable type is: " + typeof(myString)); // object
Below is some further context:
The set of types in the JavaScript language consists of primitive values and objects.
Source: MDN Web Docs
A string is a primitive value.
In JavaScript, all primitive types are immutable and have no methods, however:
​
It is important not to confuse a primitive itself with a variable assigned a primitive value. The variable may be reassigned a new value, but the existing value can not be changed in the ways that objects, arrays, and functions can be altered.
Source: MDN Web Docs
Even though primitive types have no methods, it is possible to call a method on a primitive because all primitive values, except for null and undefined, have object equivalents that wrap around the primitive values:
String for the string primitive.
Number for the number primitive.
BigInt for the bigint primitive.
Boolean for the boolean primitive.
Symbol for the symbol primitive.
Source: MDN Web Docs
When you call a String instance method on a string primitive the following occurs:
The primitive value is temporarily converted into an object
The object's method property is used
(e.g slice() will return a new string and split() will return an array of strings)
The object is converted back to a primitive
Sources:
Head First JavaScript Programming (2014), page 306.
JavaScript Programmer's Reference (2010), page 157.
When you modify a string variable, javascript doesn't actually modify the string that's in memory, it instead creates a brand new string that looks like a modification of the initial string:
let str = " hi ";
str = str.trim(); // a new string "hi" is created
console.log(str);
That is all javascript means by saying its strings are immutable. If you want to prevent a string variable from being changed, you can use const:
const str = "Can't change me";
str = "Want to bet?" // throws error.
Quite simply, you are NOT mutating the string
If you were, the following code (based on your code) would output Object in both console.log's
let x = 'this is a string';
function spliter(x){
x = x.split('\n');
console.log(typeof(x));
}
spliter(x);
console.log(typeof(x));

Can we say that String is an object in Javascript?

I'm always confused when I hear strings are primitives in JS, because everybody knows that string has different methods like: length, indexOf, search etc.
let string = "Please locate where 'locate' occurs!";
let pos = str.lastIndexOf("locate");
let position = str.search("locate");
It's true that everything in JavaScript is just like object because we can call methods on it. When we use new keyword with string it becomes an object otherwise it's primitive type.
console.log(typeof new String('str')); //object
console.log(typeof 'str'); //string
Now whenever we try to access any property of the string it box the the primitive value with new String()
'str'.indexOf('s')
is equivalent to
(new String(str)).indexOf('s').
The above process is called as "Boxing". "Boxing" is wrapping an object around a primitive value.
Strings are not objects, they are native types, like numbers, but if you want to access the method on it they are boxed with String object. The same happen with numbers you can call (10).toString() but in fact you're calling toString on Number instance that wraps 10, when you call the method.
Not certainly in that way.
If you try to use a class method for a primitive, the primitive will be temporarily wrapped in an object of the corresponding class ("boxing") and the operation will be performed on this object.
For example,
1..a = 2;
It's equal to:
num = new Object(1.);
num.a = 2;
delete num;
So, after executing the operation, the wrapper will be destroyed.
However, the primitive itself will remain unchanged:
console.log( 1..a ) // undefined
Every data type is a object in JavaScript.
String, array, null, undefined . Everything is a object in JavaScript

How are symbols displayed in Javascript?

If I say sym1 = Symbol();, is Symbol function working like below function g,
> g = function(){
| return 2; //return some random unique value based on some internal algo
| }
g()
> var sym1 = g();
> sym1
2
Now when I do var sym1 = Symbol();, and log sym1, the console displays Symbol() instead of displaying the value returned by the Symbol function.
So here's my question: How does the Symbol function work? How is the console able to display Symbol() instead of some value when you type sym1? Can we write such function?
Yes, Symbol is just a function, similar1 to an arbitrary function g() { return something }.
However, unlike the example g in your question, it does not return numbers, but symbols, which are a new primitive value type in ES6.
When I say sym1, console displays Symbol() instead of displaying the value returned by function Symbol.
What do you think is the value returned, and what do you think how it should be displayed?
The number your g function returned is just some collection of 64 bits2. Somehow3, the console derives a base 10 string from that to display the number with the digits you expect.
Now, what is a symbol? It's just a value with an identity, and we don't even know how it is implemented. It's not immediately clear how that could be displayed, or if at all. It's not a number, it's not a string. Maybe some kind of id4?
ES6 however has taken a precaution against this, and every symbol is given a description string5 that can be passed as an optional argument to the Symbol function. Also, ES6 does specify how symbols should be displayed as string in §19.4.3.2. And that's typically what the console uses.
Pretty much this "descriptive string" resembles the way the call to Symbol looked, e.g.
var sym1 = Symbol("my description");
console.log(sym1.toString()); // "Symbol(my description)"
console.log(sym1); // Symbol(my description)6
var sym2 = Symbol();
console.log(sym2.toString()); // "Symbol()"
console.log(sym2); // Symbol()6
1: However, it's a plain function, not a constructor, and cannot be called with new.
2: plus some kind of type information that it's a number, to give the 64 bits a meaning
3: to be explicit, the console probably uses .toString for numbers when it encounters a number
4: Chrome displays such for every object when you take a heap snapshot with the dev tools, for example
5: So yeah, it's a bit more than the unique identity
6: implementation-dependent, depends on your console
Is window['Symbol'] function working like below function g,
Not certain about actual Question ? Though, appear "no"
Here is the confusion, when I say, > sym1 console displays Symbol()
instead of displaying the value returned by function Symbol
Symbol() does not return a function , but a "unique and immutable data type and may be used as an identifier"
See Symbol
A symbol is a unique and immutable data type and may be used as an
identifier for object properties. The symbol object is an implicit
object wrapper for the symbol primitive data type.
var sym = Symbol("foo");
var obj = {};
obj[sym] = 1;
console.log(obj.sym); // `undefined`
console.log(Object.keys(obj)) // `[]`
console.log(obj[sym]); // `1`
See also ES6 In Depth: Symbols

Why is Javascript not handling these reserved words like a string key in this array?

I have the following test case in a coding kata for javascript:
*word-count_test.js
var words = require('./word-count');
describe("words()", function() {
it("handles properties that exist on Object’s prototype", function() {
var expectedCounts = { reserved: 1, words : 1, like :1, prototype: 1, and : 1, toString: 1, "ok?": 1};
expect(words("reserved words like prototype and toString ok?")).toEqual(expectedCounts);
});
});
The following code will not pass this:
code v1
var words = function(phrase) {
var wordCountAry = {};
// split on whitespace, including newline
phrase.split(/\s/).forEach(function(oneWord) {
if (!wordCountAry[oneWord]) {
wordCountAry[oneWord] = 1;
} else {
wordCountAry[oneWord]++;
}
});
return wordCountAry;
};
module.exports = words;
But something like the following counter line will not trigger the error:
code v2
wordCountary[word] = (+wordCountary[word] || 0) + 1
So what is so special about that unary "+" operator?
A standard JavaScript object prototype has predefined properties, such as:
toString()
constructor()
When you test those property names using typical conditional logic, they will appear as existent, e.g.
var x = {};
if (x['toString']) {
// this gets executed
}
The second code sample works around that issue with two tricks:
+x['toString'] || 0;
First, the property is coerced into a numeric value by using the unary plus operator. For functions or undefined values, the coercion yields NaN.
Secondly, the logical or operator is used to coalesce the left hand and right hand expression; if the right hand operand evaluates to false (that includes NaN) it will yield the right hand operand, otherwise it returns the left hand operand.
In this manner, either an undefined property value or a function value will yield 0 and therefore it will work as expected.
It's not really about "reserved" names (those are another matter in JavaScript, e.g. class although it's not reserved anymore in ES6), but about inherited properties.
wordCountAry is an object, so it already has a toString method. So when you test
if (!wordCountAry[oneWord])
with the words "toString" the condition will be false (because wordCountAry["toString"] is trueish (it's a function).
So you need to use a condition that will work when the property is not a number (which is the case with the Number coercion of a function with unary +), or, to be safer, also check if it is inherited (hasOwnProperty).
When you are creating your wordCountAry object, you can can pass null as the prototype, and this should solve your problem. So, instead of initializing it to {}, see how you can set it's prototype to null.

adding properties to primitive data types other than Array

I'm not supposed to add elements to an array like this:
var b = [];
b.val_1 = "a";
b.val_2 = "b";
b.val_3 = "c";
I can't use native array methods and why not just an object. I'm just adding properties to the array, not elements. I suppose this makes them parallel to the length property. Though trying to reset length (b.length = "a string") gets Uncaught RangeError: Invalid array length.
In any case, I can still see the properties I've set like this:
console.log(b); //[val_1: "a", val_2: "b", val_3: "c"]
I can access it using the dot syntax:
console.log(b.val_1); //a
If an array is just an object in the same way a string or a number is an object, why can't (not that I'd want to) I attach properties to them with this syntax:
var num = 1;
num.prop_1 = "a string";
console.log(num); //1
I cannot access its properties using dot syntax
console.log(num.prp); //undefined
Why can this be done with array and not with other datatypes. For all cases, I should use {} and would only ever need to use {}, so why have arrays got this ability?
JSBIN
Because arrays are treated as Objects by the language, you can see this by typing the following line of code:
console.log(typeof []) // object
But other types like number literals, string literals NaN ... etc are primitive types and are only wrapped in their object reprsentation in certain contexts defined by the language.
If you want to add properties or methods to a number like that, then you can use the Number constructor like this:
var num = new Number(1);
num.prop_1 = "fdadsf";
console.log(num.prop_1);
Using the Number constructor returns a number object which you can see by typing the following line:
console.log(typeof num); // object
While in the first case:
var num = 1;
console.log(typeof num) // number
EDIT 2: When you invoke a method on a number literal or string literal for instance, then that primitive is wrapped into its object representation automatically by the language for the method call to take place, for example:
var num = 3;
console.log(num.toFixed(3)); // 3.000
Here num is a primitive variable, but when you call the toFixed() metohd on it, it gets wrapped to a Number object so the method call can take place.
EDIT: In the first case, you created a string like this first var str = new String(), but then you changed it to str = "asdf" and then assigned the property str.var_1 = "1234".
Of course, this won't work, because when you assigned str = "asdf", str became a primitive type and the Object instance that was originally created is now gone, and you can't add properties to primitives.
In the second, it didn't output undefined like you said, I tested it in Firebug and everything worked correctly.
EDIT 3:
String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings.
This is taken from MDN Documentation, when you use string like that var p = String(3) it becomes a conversion function and not a constructor and it returns a primitive string as you can see from the quote above.
Regarding your second comment, I didn't understand how my comment has been defied, because if you try to console.log(p.pr) you'll get undefined which proves p is a primitive type and not an object.
If an array is just an object in the same way a string or a number is an object,
An array is different than strings, numbers, booleans, null and undefined. An array IS an object, while the others in the list are primitive values. You can add properties to the array just like you would with any other object, anything different being just what makes arrays special (the length property you mentioned for example). You cannot add properties or call methods on primitive values.
In this previous answer i talked about the use of Object wrappers over primitive values. Feel free to read it to see more about Object wrappers. The following will be a very short example:
console.log('TEST'.toLowerCase()) // 'test'
While it may seem that the toLowerCase method is called on the string 'TEST', in fact the string is converted automatically to a String object and the toLowerCase method is called on the String object.
Each time a property of the string, number or boolean is called, a new Object wrapper of the apropriate type is created to get or set that value (setting properties might be optimized away entirely by the browser, i am not sure about that).
As per your example:
var num = 1; // primitive value
num.prop_1 = "a string"; // num is converted to a Number, the prop_1 property is set on the Object which is discarded immediately afterwards
console.log(num); //1 // primitive value
console.log(num.prp); // num is converted to a Number, which doesn't have the prp property
My example:
Number.prototype.myProp = "works!";
String.prototype.myFunc = function() { return 'Test ' + this.valueOf() };
Boolean.prototype.myTest = "Done";
console.log(true.myTest); // 'Done'
console.log('really works!'.myFunc()); // 'Test really works!'
var x = 3;
console.log(x.myProp.myFunc()); // 'Test works!'
console.log(3['myProp']); // 'works!'
On the other hand:
console.log(3.myProp); // SyntaxError: Unexpected token ILLEGAL
The number isn't necessarily treated differently, that syntax just confuses the parser. The following example should work:
console.log(3.0.myProp); // 'works!'

Categories

Resources