When is a JavaScript String not a string? [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why does (“foo” === new String(“foo”)) evaluate to false in JavaScript?
Over here I caught the advice that it's best to use non type-coercive string comparison, but in Chrome, I discovered something kind of odd:
var t1 = String("Hello world!");
var t2 = new String("Hello world!");
var b1 = (t1==t2); // true
var b2 = (t1===t2); // false
Is this standard behavior? If so, what are the respective types of t1 and t2? Thanks.

If you don't use the "new" keyword with String, you get a primitive string.
If you use "new" keyword, you get a string object instead of primitive.
When you use == it will try to convert to a comparable type, so it can be equal.
If you use ===, it won't convert, so an object can not equal a primitive.

Here is the explanation
typeof String("Hello world!");
"string"
And:
typeof new String("Hello world!");
"object"
when you use === it is paying attention to the type so it returns false

String, called as a function, converts its argument to a string. String, called as a constructor, creates an object whose prototype is the String function. (Check James's Answer for the relevant ECMAScript specification section.)
This is indeed confusing.
The two equality operators actually do very different things. From the ECMA-262, v 5.1 document, === does:
If Type(x) is different from Type(y), return false.
If Type(x) is Undefined, return true.
If Type(x) is Null, return true.
If Type(x) is Number, then
a. If x is NaN, return false.
b. If y is NaN, return false.
c. If x is the same Number value as y, return true.
d. If x is +0 and y is -0, return true.
e. If x is -0 and y is +0, return true.
f. Return false.
If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false.
If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.
Return true if x and y refer to the same object. Otherwise, return false.
Whereas == does:
If Type(x) is the same as Type(y), then
a. If Type(x) is Undefined, return true.
b. If Type(x) is Null, return true.
c. If Type(x) is Number, then
i. If x is NaN, return false.
ii. If y is NaN, return false.
iii. If x is the same Number value as y, return true.
iv. If x is +0 and y is -0, return true.
v. If x is -0 and y is +0, return true.
vi. Return false.
d. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
e. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
f. Return true if x and y refer to the same object. Otherwise, return false.
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
If Type(x) is Number and Type(y) is String, return the result of the comparison
x == ToNumber(y).
If Type(x) is String and Type(y) is Number, return the result of the comparison
ToNumber(x) == y.
If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
Return false.
Note that in the spec, the Type of a primitive string object is String, whereas the type of any object (including the String object) is Object.
With === the relevant line is #1: the Type of the objects are different, so false is returned.
With == the relevant line is #8: x is a String ("Hello world!") and y is an Object (The String object containing the string "Hello world!"). Thus the comparison x == ToPrimitive(y) is made. ToPrimitive ends up calling the valueOf method of the object, or if that method doesn't exist, the toString method. In this case, a String object's valueOf method returns the primitive string the object contains. Thus the equality operation is done again, this time between two primitive strings which contain the same text, which returns true thanks to #1.d.
JavaScript is a bit messy under the hood...
EDIT: Notice that if two objects are compared, no conversions apply, but rather, rule #1.f applies. Thus, thanks to the spec, I was able to correctly predict the output of the following code:
> new String("hi") == new String("hi")
false
EDIT: Just thought I'd add that these distinctions are even further blurred by more implicit type conversion. For example, the following works:
> ("hi").toString()
"hi"
but that's not because "hi" is an object (like in Python):
> typeof "hi"
"string"
But rather, because the . operator does a conversion from the primitive string type to the string Object type (creating a new string object) whose toString method is then called.

This behaviour is detailed in the ECMAScript 5 specification, 15.5.1 and 15.5.2:
When String is called as a function rather than as a constructor, it performs a type conversion.
...
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty String "" is returned.
So String("some string") creates a string value.
When String is called as part of a new expression, it is a constructor: it initialises the newly created object.
So new String("some string") creates an instance of the String object.
And to actually answer your questions:
Is this standard behavior?
Yes, for the reasons detailed above.
If so, what are the respective types of t1 and t2
You can check this with the typeof operator:
console.log(typeof t1); //string
console.log(typeof t2); //object

This is happening because the == operator only checks if the values are the same, whereas === checks both the value and type. new String("Hello world!") is not actually being given the type string, it is an object, while String("Hello world!") is actually a string.

The first example String("Hello world!)" creates a primitive string while the second example new String("Hello world!") creates an String object.

Related

Does the `===` operator in Javascript have separate definitions for primitives vs non-primitives?

With the === operator in Javascript, if it operators on primitives, it returns false if either the values are different or the types are different. If it's operating on non-primitives, it returns false if the two operands don't point to the same object.
This seems like the === has separate definitions when applied to primitives and non-primitives. Like "if operands are primitives, do this, else do this". Is there a broader definition of === that encompasses its treatment of both primitives and non-primitives? Like "whether primitives or non-primitive, do this"?
Yes, to some extent - there's a bit of a process here.
7.2.15 IsStrictlyEqual ( x, y )
If Type(x) is different from Type(y), return false.
If x is a Number, then Return Number::equal(x, y).
Return SameValueNonNumber(x, y).
and
7.2.12 SameValueNonNumber ( x, y )
Assert: Type(x) is the same as Type(y).
If x is a BigInt, then
a. Return BigInt::equal(x, y).
If x is undefined, return true.
If x is null, return true.
If x is a String, then
a. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.
If x is a Boolean, then
a. If x and y are both true or both false, return true; otherwise, return false.
If x is a Symbol, then
a. If x and y are both the same Symbol value, return true; otherwise, return false.
If x and y are the same Object value, return true. Otherwise, return false.
The only real answer here is the spec, where we see === defined:
EqualityExpression : EqualityExpression === RelationalExpression
1. Let lref be ? Evaluation of EqualityExpression.
2. Let lval be ? GetValue(lref).
3. Let rref be ? Evaluation of RelationalExpression.
4. Let rval be ? GetValue(rref).
5. Return IsStrictlyEqual(rval, lval).
So we're comparing "whatever the GetValue spec function says we should be comparing", paired with "and we have to use the strict equality test for that", so we're not so much comparing "two primitive values, or two references". There's a few more steps involved, which have zero relevance to "actually using JS" in most circumstances, so for practical purposes they simply don't matter...but when you have fundamental questions, the spec is the fundaments =)

Comparison Behaviour of JavaScript Objects

We suppose that we have 3 variables : a,b and c
var a = new Boolean(true);
var b = true;
var c = new Boolean(true);
console.log("First comparison : ", a == b);
// true
console.log("Second comparison : ", b == c);
// true
console.log("Contradiction : ", a == c);
// false
I already know that the keyword 'new' creates a new object.
The type of this object, is simply object.
Mathematically, how can we explain this contradiction ?
In the first two examples, since the comparison involves a primitive value of b, a and c end up being coerced to primitives. In the last case, on the other hand, you are comparing two distinct objects, and therefore no coercion takes place.
To be precise, the double-equal comparison with a boolean uses this rule from the spec (taking the case of b == c):
If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
So this means that ToNumber(b) is compared with c. ToNumber(b) is 1. So we are comparing 1 with c. Next, the following rule is applied:
If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
So this means we compare 1 with ToPrimitive(c) which is ToPrimitive(Boolean(true)). ToPrimitive invokes valueOf, which yields 1. So we compare 1 to 1. QED.
In the case of your third example, the following portion of the spec applies:
If Type(x) is the same as Type(y), then...Return true if x and y refer to the same object. Otherwise, return false.
To answer your question:
Mathematically, how can we explain this contradiction ?
It's not a matter of mathematics. It's a matter of the definition of comparisons in the JS spec.
Mathematically, how can we explain this contradiction?
If you think this is a contradiction is probably because you assumed that == defines an equivalence relation.
But it doesn't:
It doesn't satisfy reflexivity, e.g. NaN != NaN.
It doesn't satisfy transitivity, as you noticed.
It satisfies symmetry, though, but that alone is not enough.
I don't think this kind of behaviour can be explained from a mathematical standpoint.
What you are doing on the variables a and c is commonly referred as "boxing": taking a Javascript primitive value (undefined, null, String, Number, Boolean, Symbol in ES6) and calling it with the newoperator.
The result of:
var a = new Boolean(true);
is a Javascript Objectwrapping a Boolean primitive value.
The same invocation pattern implicitly happens when you use a primitive value as the context (this) in some of the language built-in facilities such as Function.prototype.call and Function.prototype.apply
Even replacing ==with === would yield the same results, and that's because of how the Objects comparison work in JS.

Why is "" == [null] true in JavaScript?

I know JavaScript has lots of insane results with comparisons between types, though I don't fully understand why. Came across this one today.
Why does
"" == [null]
evaluate to true in JavaScript?
Some more Javascript equality amusement, thanks to #Qantas:
Why does 2 == [2] in JavaScript?
Why is 0 == "" true in JavaScript
Why if([]) is validated while [] == false in javascript?
Why does !{}[true] evaluate to true in JavaScript?
The "Abstract Equality Comparison Algorithm" has many parts, but the important one here is this:
If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
(There's a mirror-image of that too.) So, because "" is a string and [null] is an object, we've got to first convert [null] to a string by calling ToPrimitive([null]). That's an internal operation described as follows, when it's asked to convert an Object instance to a primitive value:
Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.
Now, the [[DefaultValue]] internal operation will call .toString() on the object and return that value. Try [null].toString() in your browser console:
> [null].toString()
""
And there you have it.
Edit: And why is [null].toString() an empty string? Because the .toString() operation on Array instances always just calls .join(), and that always yields an empty string for null and undefined values. Thus an array of one null ends up as just a single empty string.
It's according to the arcane type-conversion rules of Javascript. Rule #8:
If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
So the comparison is between x = "" and y = [null] converted to a string using ToPrimitive. Converting an array with one null element results in an empty string (because Array.toString() returns a comma-separated list of values), hence they evaluate to equal.
Why does "" == [null] evaluate to true?
Because you're comparing an array with a string, using the non-strict equality operator == - so it will try to cast values to the same type before comparing them.
What happens in detail is:
you compare a string to an object, so the object is cast to a string:
When an array is cast to a primitive value, it's .toString() method is invoked (as explained in detail by the other answers), which is equivalent to calling .join():
which in case of a one-element array that only contains an undefined or null value returns the empty string
which finally is equivalent to the empty string
This third step is the unexpected one ([null]+"" != null+""), if it actually did cast that to a string the result would have been "null" and your equality be false.
Let's look at the spec and follow through each step
Going via the Abstract Equality Comparison Algorithm (§11.9.3):
typeof ""; // string and typeof [null]; // object so not applicable
neither is null or undefined so not applicable
same as 2
neither is a number, not applicable
same as 4
neither is a bool, not applicable
again not applicable
finally, something applicable, now we need to know ToPrimitive([null])
§9.1 ToPrimitive for Objects says we need to work out [[DefaultValue]] (§8.12.8), points 1 and 2 of which say if you can do .toString and it gives a string, return that, so
[null].toString(); // ""
So we are now performing the comparison "" == "" which is true by the Abstract Equality Comparison Algorithm's point 1. d.
If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
JavaScript is weakly typed; you can use the following to get a false result:
"" === [null]
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.
The value null is a literal (not a property of the global object like undefined can be). In APIs, null is often retrieved in place where an object can be expected but no object is relevant. When checking for null or undefined beware of the differences between equality (==) and identity (===) operators (type-conversion is performed with the former).
typeof null // object (bug in ECMAScript, should be null)
typeof undefined // undefined
null === undefined // false
null == undefined // true

Python 'is' vs JavaScript ===

The Python use of 'is' seems to be similar to JavaScript '===' but not quite.
Here they talk about exact instances:
http://www.learnpython.org/en/Conditions
here (for JS) they talk about "equal AND the same type."
http://www.w3schools.com/js/js_comparisons.asp
SO can you have two different instances of (say) a string of "Bob" and have them not return true when compared using 'is'? Or is it infact the same as ===?
I am guessing this is related to strict vs non-strict typed languages. . . .
Python Part
SO can you have two different instances of (say) a string of "Bob" and
have them not return true when compared using 'is'? Or is it infact
the same as ===?
a = "Bob"
b = "{}".format("Bob")
print a, b
print a is b, a == b
Output
Bob Bob
False True
Note: In most of the Python implementations, compile time Strings are interned.
Another example,
print 3 is 2+1
print 300 is 200+100
Output
True
False
This is because, small ints (-5 to 256) in Python are cached internally. So, whenever they are used in the programs, the cached integers are used. So, is will return True for them. But if we choose bigger numbers, like in the second example, (300 is 200+100) it is not True, because they are NOT cached.
Conclusion:
is will return True only when the objects being compared are the same object, which means they point to the same location in memory. (It solely depends on the python implementation to cache/intern objects. In that case, is will return True)
Rule of thumb:
NEVER use is operator to check if two objects have the same value.
JavaScript Part
Other part of your question is about === operator. Lets see how that operator works.
Quoting from ECMA 5.1 Specs, The Strict Equality Comparison Algorithm is defined like this
If Type(x) is different from Type(y), return false.
If Type(x) is Undefined, return true.
If Type(x) is Null, return true.
If Type(x) is Number, then
If x is NaN, return false.
If y is NaN, return false.
If x is the same Number value as y, return true.
If x is +0 and y is −0, return true.
If x is −0 and y is +0, return true.
Return false.
If Type(x) is String, then return true if x and y are exactly the
same sequence of characters (same length and same characters in
corresponding positions); otherwise, return false.
If Type(x) is Boolean, return true if x and y are both true or both
false; otherwise, return false.
Return true if x and y refer to the same object. Otherwise, return
false.
Final Conclusion
We can NOT compare Python's is operator and JavaScript's === operator, because Python's is operator does only the last item in the Strict Equality Comparison Algorithm.
7. Return true if x and y refer to the same object. Otherwise, return false.
Completely different.
>>> a = 'foo'
>>> b = 'bar'
>>> a + b is 'foobar'
False
>>> 1000 + 1 is 1001
False
>>> a = "Hello, World!!!"
>>> b = "Hello, World!!!"
>>> a is b
False
However note that:
>>> a = "Bob"
>>> b = "Bob"
>>> a is b
True
In this case it condition was True because the compiler is free to intern string literals, and thus reuse the same object, and it does do that with small strings. However there is no guarantee as to when this happens of if this happens at all and the behaviour changes between versions and implementations.
A realiable False output should be:
>>> a = 'Hello, World!!!!'[:-1]
>>> b = 'Hello, World!!!!'[:-1]
>>> a is b
False
Or anything that actually computes the strings.
Python's is keyword compares references (and so is about identity) while === does a minimal amount of coercion (and is therefore concerned with equality, at least in the case of primitives) so they are different.
As I understand it, things that are concerned with identity are concerned with uniqueness from the runtime's point of view (do these two variables point to the same address in memory) while equality is concerned with the uniqueness of the contents of the variables (are these two variables equivalent, regardless of where they are placed in memory relative to each other).

[] is not identical to [] [duplicate]

This question already has answers here:
How to compare arrays in JavaScript?
(55 answers)
Closed 9 years ago.
I was asked to write a function sortByFoo in Javascript that would react correctly to this test :
// Does not crash on an empty array
console.log(sortByFoo([]) === []);
But I've tried something :
[] === [];
>> false
Just so I can be sure, such a test would always fail, no matter the sortByFoo function, wouldn't it ?
But I'd like to have an explanation on why this happens. Why [] isn't identical/equal to [] ?
Please forgive my approximate english, it is not my native language :p
If you look at the specification for javascript/ecmascript, particularly section 11.9.6, you will see how comparisons with === are performed.
The Strict Equality Comparison Algorithm
The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:
If Type(x) is different from Type(y), return false.
If Type(x) is Undefined, return true.
If Type(x) is Null, return true.
If Type(x) is Number, then
If x is NaN, return false.
If y is NaN, return false.
If x is the same Number value as y, return true.
If x is +0 and y is −0, return true.
If x is −0 and y is +0, return true.
Return false.
If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false.
If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.
Return true if x and y refer to the same object. Otherwise, return false.
Since your arrays go all the way down to the seventh step they will have to be the same object, not just two identical objects. The same goes for the regular equality operator (==).
Because every time you write [] you are calling array's constructor.
[] is the same as new Array(), and in Javascript new objects compared with equals method are different. See the reference, it is the same as new Object() when using {}.
When you do [] === [] you're comparing references and not values (or deep comparison by values).
Take a look at this solution for javascript array comparison: Comparing two arrays in Javascript
Yes, you are correct that two array literals are never equal. That's because they are references to two separate instances of arrays.
The code to describe the test should be written:
var arr = [];
var result = sortByFoo(arr);
console.log(result === arr && result.length == 0);
Checking that the reference returned by the function is the same that was sent in, and that the array is still empty, ensures that the function returned the same array unchanged.

Categories

Resources