JavaScript loose comparison between an object and a string - javascript

The below code,
var someObject = {"attr":1}; // line:1
alert(someObject == "someString"); //line:2
fails with the exception: unexpected call to method or property access in line 2.
When i change the comparison to a strict equals to, it works fine.
alert(someObject === "someString");
I understand that a doing a strict comparison does not do a type conversion, but am unable to determine at what point exactly is this error thrown when the type conversion happens.
Note: The exact object has around ten attributes and each attribute has a string value of significant length.
Minimal input with which i am able to reproduce this error:
someObject = {
"a":"RESOLVED",
"b":"A-1444779652190",
"c":"{s=Hello, id=A-1444779652190}"
}
(c is a string, don't think it really matters here)

When you do someObject == "someString", the Abstract Equality Comparison Algorithm does this:
If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) ==
y.
When ToPrimitive is called with an object,
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.
Summarizing, When [[DefaultValue]] is called on a non-Date native object with no hint, it does this:
If the object has a toString method which returns a primitive, the default value is that primitive
If the object has a valueOf method which returns a primitive, the default value is that primitive
Throw TypeError
My guess is that some code modified Object.prototype.toString or Object.prototype.valueOf, and now they may throw when called by ToPrimitive.

Related

Boolean coercion fails

I am wondering why the Boolean coercion fails in this case:
!!(new Boolean(false)) === true
Although:
(new Boolean(false).valueOf()) === false
Mozilla says:
Booleans are returned as-is.
I am wondering what "as-is" means in the context of a coercion. I thought "coercion" means "convert anything to a primitive boolean". How is it possible that something which is meant to be false gets coerced to true?
BTW: Consequently this fails too:
Boolean(new Boolean(false)) === true
It seems to me, that the Boolean class itself is an error. Maybe I have to use the following code:
if (arg instanceof Boolean)
throw new Error("Stop doing nonsense")
Or maybe this:
function coerce_boolean_correctly (arg) {
if (arg instanceof Boolean)
return coerce_boolean_correctly(arg.valueOf())
return !!arg
}
There is a huge difference between a Boolean object and a Boolean primitive. The MDN page you referenced, actually warns about this at the very outset:
Do not confuse the primitive Boolean values true and false with the true and false values of the Boolean object.
Any object, including a Boolean object whose value is false, evaluates to true when passed to a conditional statement.
And this (what I marked in bold) is exactly what happens in your code: it creates a new Boolean(false) and that will coerce to true -- in other words it is a truthy value. In your code you have explicitly converted it to a boolean primitive, by applying ! to it twice. In either case (implicit coercion or explicit conversion) new Boolean(false) is truthy. Fact is that all objects are considered truthy (when coerced to boolean, they evaluate to true).
The article continues with:
Do not use the Boolean() constructor with new to convert a non-boolean value to a boolean value — use Boolean as a function or a double NOT instead.
This suggests that your code should be modified to drop the use of new, and call Boolean as a plain function, not as constructor:
!!(Boolean(false)) === false
When Boolean is called as plain function, it returns a primitive boolean (false or true). But anything that is called as constructor, even Boolean, will return an object. And objects are truthy.
When in the context of coercion MDN states "Booleans are returned as-is." they refer to boolean primitives. (Boolean) objects are covered by the last bullet point in the same list: "All objects become true". The latter in includes Boolean objects.
A Boolean object has a valueOf method which returns a boolean primitive, and so it returns what you would intuitively expect.
The MDN article rightly says:
Warning: You should rarely find yourself using Boolean as a constructor.
Don't use new Boolean, but Boolean.
If for some reason you have a Boolean object, really ask yourself why you have that object in the first place. Tackle the code that created that object and use a primitive boolean from the outset.
Objects are truthy
Some are surprised in a similar way that the following holds:
if (!!new String("")) console.log("An empty string is truthy!?");
if (!!new Number(0)) console.log("The number 0 is truthy!?");
if (!!new Number(NaN)) console.log("NaN is truthy!?");
if (!!new Object(null)) console.log("null is truthy!?");
if (!!new Object(undefined)) console.log("Undefined is truthy!?");
It is the same principle: objects are truthy. Always.* No matter what their constructor is named. No matter what value was passed to that constructor.
* document.all is an abhorrent, but accepted, violation of this rule.
See also
What's the point of the Boolean object?
What is the purpose of new Boolean() in Javascript?

Are there any built-in methods for undefined data type in JavaScript?

Asking this question I want to evaluate the following statement true or false:
"There are methods built-in to every data type in JavaScript"
As undefined in data type in JavaScript. I try to find any build-in method for an "undefined" but it appears that it's an only primitive type that does not have one. Or there are some?
let undef = void 0;
console.log(undef.anyPotentialBuildInMethod());
Does any "anyPotentialBuildInMethod()" exists?
Methods are functions that are properties of objects.
Trying to read any property of undefined throws an exception.
undefined has no built-in methods and cannot be assigned any.
const foo = undefined;
console.log(foo.sampleMethod);
While it might appear so, not everything in Javascript is an object. Primitive values (like "foo", 12.34, undefined etc) are not objects and cannot have methods or properties. However, Javascript hides that fact by dynamically boxing (or "coercing") primitive values into objects when you access their properties. So when you do
x = "foo".length
what actually happens is
1. temp = new String("foo") // dynamically coerce a primitive value into an object
2. x = temp.length // get a property of that object
3. destroy temp // delete a temporary object
(Of course, Javascript engines don't actually allocate and then discard these temporary objects, this is just an algorithm defined by the standard, see (1) below).
Two primitive values: null and undefined are exception of this rule: any attempt to coerce them results in a TypeError. So when you do
x = undefined.something
Javascript tries to create a temporary object like in the step 1 above and fails.
The bottom line: null and undefined cannot have properties, because they are primitive values and cannot be converted to objects.
References:
https://262.ecma-international.org/11.0/#sec-getvalue
https://262.ecma-international.org/11.0/#sec-evaluate-property-access-with-identifier-key
https://262.ecma-international.org/11.0/#sec-requireobjectcoercible
No, property access on undefined always throws a TypeError in a standards-compliant engine.
From ECMA-262, 11th Ed., §12.3.2.1:
MemberExpression : MemberExpression [ Expression ]
[…]
Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict).
[…]
MemberExpression : MemberExpression . IdentifierName
[…]
Return ? EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict).
In both §12.3.3 and §12.3.4 have:
Let bv be ? RequireObjectCoercible(baseValue).
And in §7.2.1 we have:
The abstract operation RequireObjectCoercible throws an error if argument is a value that cannot be converted to an Object using ToObject. It is defined by Table 15:
Table 15: RequireObjectCoercible Results
Argument Type
Result
Undefined
Throw a TypeError exception.
Null
Throw a TypeError exception.
The only thing that can possibly be considered an exception is the document.all object, which compares loosely-equal (== not ===) to both undefined and null, and returns 'undefined' when passed to the typeof operator, but actually has properties. This is specified in §B.3.7; before the 9th edition of the standard, it was actually contrary to the specification.
I'm not sure of the reason why you are asking this, but, to let you understand better, what you are trying to do is like trying (but not the same) to find an 'x' value so that (0 * x) != 0; 0 is 0 and whethever you use as 'x' you got 0.
And 'undefined' var in javascript is nothing. This is because 'var' doesn't have a type, it can be string, integer, object, function ecc
you can do
var test = "test";
console.log(test);
test = 1;
console.log(test);
and so on, and you'll get no error. var are evauated at execution time, and and undefined var is simply a pointer to nothing so you can't have a function or a property in that

Is there difference between creating a wrapped BigInt or Symbol with the "new" keyword in JavaScript?

I have examined the wrapped symbol objects using the following code.
const symObj = Object(sym);
const symObjNew = new Object(sym);
// I can see no difference between symObj and symObjNew
console.log(`typeof symObj === 'object' is ${typeof symObj === 'object'}`); // true
console.log(`typeof symObjNew === 'object' is ${typeof symObjNew === 'object'}`); // true
I have also examined the symObj and symObjNew in devtools using node --inspect-brk. I don't see any difference between them. The same is true of BigInt.
TL;DR
There is no difference between new Object(realthing) and Object(realthing). They do the same thing.
Much longer answer
When it comes to JS, reading the spec is always a good idea, although it can be incredibly hard to figure out what things mean. So, let's dive into this: in this case, we want the rules for how Object() works:
19.1.1The Object Constructor
The Object constructor:
is the intrinsic object %Object%.
is the initial value of the Object property of the global object.
creates a new ordinary object when called as a constructor.
performs a type conversion when called as a function rather than as a constructor.
is designed to be subclassable. It may be used as the value of an extends clause of a class definition.
So we have two cases to look at: Object(sym), and new Object(sym).
What happens for Object(sym)?
19.1.1.1 Object([ value ])
When the Object function is called with optional argument value, the following steps are taken:
If NewTarget is neither undefined nor the active function, then
Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
Return ! ToObject(value).
(Note that ? and ! here are not "code", they are spec syntax. ! means "this operation will always return a normal value whereas ? may return an abnormal value)
In this case, we didn't use new, so step 1 doesn't apply. The value is not nullish, so step 2 doesn't apply, and we end up executing step 3: we perform a type conversion so we end up with a new object for which typeof will now say "object", no matter what it was before, but with the original data preserved as value.
What happens for new Object(sym)?
Looking at 19.1.1.1 again, we're using new this time, so we might expect step 1 to kick in: after all, there is a NewTarget (by definition: we used new so there is a NewTarget), but it turns out the NewTarget is the Object function itself, making NewTarget the active function, and so step 1a does not kick in.
We also have a value, and it's not nullish, so step 2 doesn't kick in, and we again run step 3: type conversion.
So Object(sym) and new Object(sym) do exactly the same thing, just for subtly different reasons.
So how does ToObject work?
Looking at the spec again:
7.1.13ToObject ( argument )
The abstract operation ToObject converts argument to a value of type Object according to Table 12:
Undefined Throw a TypeError exception.
Null Throw a TypeError exception.
Boolean Return a new Boolean object whose [[BooleanData]] internal slot is set to argument. See 19.3 for a description of Boolean objects.
Number Return a new Number object whose [[NumberData]] internal slot is set to argument. See 20.1 for a description of Number objects.
String Return a new String object whose [[StringData]] internal slot is set to argument. See 21.1 for a description of String objects.
Symbol Return a new Symbol object whose [[SymbolData]] internal slot is set to argument. See 19.4 for a description of Symbol objects.
Object Return argument.
So the first observation should be that "if it's an object, ToObject does nothing". That's of course not the case for your Object(sym), so what happens for it?
A new object gets built, with its type set to object (irrespective of what the input value type is), and this new object's prototype will be set to match whatever the input value's prototype was. Then the new object's internal value literally gets copied over from the input value that's getting converted.
But we're not quite done
typeof is useless for testing "what a thing is".
Remember that almost everything in JS is an object, and typeof will only tells you the basic type of something. As such, there are only six answers it can give you:
object
function
string
symbol
number
undefined
That's it. The typeof operator tells you nothing about the what the most specific type is, instead it tells you what the most generic type is.
For the specific type, either use thing.constructor.name to find out which constructor function actually got used to build the thing you're examining, or use thing.__proto__ to get a reference to that type. Or, if you have the type already and merely need to test to see if "thing is a ...", use the instanceof operator:
> typeof 3
"number"
> typeof Object(3)
"object"
> Object(3).constructor.name
"Number"
> Object(3).__proto__
Number { 0 }
> Object(3) instanceof Number
true
As specified in MDN:
When called in a non-constructor context, Object behaves identically to new Object().
I'd venture to guess this is another one of JavaScript's infamous backwards compatibility 'features', back during such a time that the new keyword didn't exist.
You can also check the ECMAScript spec for details:
When the Object function is called with optional argument value, the
following steps are taken:
If NewTarget is neither undefined nor the active function, then
Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%").
If value is undefined or null, return OrdinaryObjectCreate(%Object.prototype%).
Return ! ToObject(value).
As thoroughly explained by Mike's answer, both cases in your sample snippet end up in Step 3, which performs type conversion on the argument. So there is no difference whatsoever.

Why does getPrototypeOf 'null' give an error?

So I know 'null' is a type of 'object'
However, when I do Object.getProtoTypeOf(null) I get an error. Same goes for instanceof, which is kinda the same thing.
If null is a type of Object...it should be in it's prototype chain, right?
// 'object'
typeof null;
// gives error - Cannot convert undefined or null to object
Object.getPrototypeOf(null);
// false ---- why?
null instanceof Object
Is there something I'm missing, or JS is just too hippie ?!
See, null is not an object. It's true that typeof null evaluates to 'object', but it actually came from a limitation and may be considered a quirk - a bug to some extent - of JavaScript. Quoting this article:
The "typeof null" bug is a remnant from the first version of
JavaScript. In this version, values were stored in 32 bit units, which
consisted of a small type tag (1–3 bits) and the actual data of the
value. The type tags were stored in the lower bits of the units. There
were five of them:
000: object. The data is a reference to an object.
1: int. The data is a 31 bit signed integer.
010: double. The data is a reference to a double floating point number.
100: string. The data is a reference to a string.
110: boolean. The data is a boolean.
That is, the lowest bit was either one, then the type tag was only one bit long. Or it was zero, then the type tag was three bits in length, providing two
additional bits, for four types. Two values were special:
undefined (JSVAL_VOID) was the integer −230 (a number outside the integer range).
null (JSVAL_NULL) was the machine code NULL pointer. Or: an object type tag plus a reference that is zero.
It should now be obvious why typeof thought that null was an object: it examined its type tag and the type tag said “object”.
Now, for the rest operations null is treated as a primitive value. That's why, for example, null instanceof Object returns false - any primitive will, as it cannot be an instance. Compare 42 instanceof Number (false) with typeof 42 === 'number'.
For Object.getPrototypeOf(null), it's a bit different. In ES5 standard this function should throw an exception if its argument is a primitive - any primitive:
When the getPrototypeOf function is called with argument O, the
following steps are taken:
If Type(O) is not Object throw a TypeError exception.
Return the value of the [[Prototype]] internal property of O.
However, in ES2015 they changed it:
When the getPrototypeOf function is called with argument O, the
following steps are taken:
Let obj be ToObject(O).
ReturnIfAbrupt(obj).
Return obj.[[GetPrototypeOf]]().
... so Object.getPrototypeOf(42) now works. Yet both Object.getPrototypeOf(undefined) and Object.getPrototypeOf(null) throw the same error:
Uncaught TypeError: Cannot convert undefined or null to object
This exception is thrown by ToObject() method (doc) - and it makes sense, as one cannot 'box' null and undefined. In some ways it's similar to the error you get when trying to access a property of null or undefined.

Safe way to get a string representation of any JavaScript value or object

I want to get a string represention of any object or value in JavaScript. I did a couple of experiments.
> var a = document.createTextNode('foo'); a
"foo"
> var a = document.createTextNode('foo'); a.toString()
"[object Text]"
> var a = 1; a.toString()
"1"
> (1).toString()
"1"
> 1.toString()
SyntaxError: Unexpected token ILLEGAL
I have the following questions:
Why does 1.toString() fail?
Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Is there any other alternative to the function str I have written in the previous point?
1). Why does 1.toString() fail?
The JavaScript parser only uses a 1 character lookahead and can't determine if that's 1.0 or 1.toString(). You can use 1..toString() to get around that.
2). Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Any literal will be converted to a temporary object in order to have its toString() called. If the object has its own toString() defined, it will be called. Otherwise, it will use Object.prototype.toString() (having gone up the prototype chain) for almost all cases (the other case is an object with a null prototype).
3). Is there any other alternative to the function str I have written in the previous point?
Yes. You can invoke the toString() implicitly by concatenating an empty string, e.g. 1 + "". You can also use the String constructor, e.g. String(value) (thanks T.J. Crowder). The advantages of these other ones is no exception will be thrown if you attempt to call toString() on null or undefined.
However, these tricks will convert null and undefined to their string equivalents (almost never what you want). One dirty trick is to put the value in a literal array, e.g. [value] and then call toString() on it. This will actually invoke join(","), but seeing as it only has one member, the comma will never become part of the string.
The real power of doing this is that null and undefined will just become an empty string. If that's OK for your program, then it can be useful. Keep in mind to comment this solution as it's not immediately obvious what this code is doing. Alternatively, check value == null which will detect null and undefined and handle it appropriately.
However, if you're wanting a string in order to classify a value, you can get the type's [[Class]] like so...
var getInternalClass = function(value) {
return Object.prototype.toString.call(value).slice(8, -1);
};
This will invoke the Object's toString() and set the ThisBinding to the value provided as the argument. This is the only way to expose an object's internal [[Class]]. The advantage of this (over typeof, for example) is that primitives and objects will always return the same value (with the primitives being converted to temporary objects, boxed by the call() context in non-strict mode).
for 1.toString(), you need to do:
1 .toString() //add space before dot (.) to avoid taking it as decimal
shortest way (alternative to function str ) to convert to string is:
var str = str + '';
Your str(a) function is correct but it will call the default implementation of toString() inherited from Object. So yes, your function will give you string representation of every JS object but not in way you want it. You need to override it.
var o = new Object();
o.toString(); // returns [object Object]
See here for reference and overriding: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

Categories