Please help me understand prototype inheritance in this case:
Why does 'abc'.hasOwnProperty('length') return true but 'length' in 'abc' throws an error?
The expression 'abc'.hasOwnProperty('length') is interpreted by JavaScript as
(new String('abc')).hasOwnProperty('length')
Every (capital-S) String instance has its own length property, which gives the length of the string.
JavaScript (lower-case s) string instances are primitives and do not have any properties at all. The use of a string primitive as the left-hand side of the . operator causes the string primitive to be implicitly wrapped in a String object (at least conceptually; the runtime doesn't really have to instantiate a transient object) and that's where the .length property comes from.
The expression length in 'abc' throws an exception because there's no implicit promotion of the primitive 'abc' to a String instance with the in operator. Thus since a primitive cannot have any properties, and the concept makes no sense, it's an exception.
Related
As per MDN the in operator returns true if the property exists and accordingly the first example logs true. But when using a string literal, why is it throwing an error instead of logging false?
let let1 = new String('test');
console.log(let1.length);
console.log('length' in let1)
var let1 = 'test';
console.log(let1.length);
console.log('length' in let1);
In a sense it is a matter of timing. String literals do not have any properties. The reason that you can call methods and lookup properties on primitive strings is because JavaScript automatically wraps the string primitive in a String object when a method call or property lookup is attempted. JavaScript does not interpret the in operator as a method call or property lookup so it does not wrap the primitive in an object and you get an error (because a string primitive is not an object).
See Distinction between string primitives and String objects
Also, the same docs referenced in your question specifically note that using in on a string primitive will throw an error.
You must specify an object on the right side of the in operator. For
example, you can specify a string created with the String constructor,
but you cannot specify a string literal.
It throws an error because in is an operator for objects:
prop in object
but when you declare a string as `` (` string(template) literals) or "" '' (",' string literals) you don't create an object.
Check
typeof new String("x") ("object")
and
typeof `x` ("string").
Those are two different things in JavaScript.
JavaScript operator in only applicable to an Objects instances.
When you using constructor new String('abc') this will causing creating of a String object instance.
In other side, when you using only string literals or call function String('abc') without new it creates an string primitive. (like Number and Boolen)
Some behaviour of primitives and objects is differrent, look at this simple example's output:
console.log(typeof (new String('ddd'))) // "object"
console.log(typeof ('ddd')) // "string"
console.log(eval('1 + 2')) // 3
console.log(eval(new String('1 + 2'))) // {"0":"1","1":" ","2":"+","3":" ","4":"2"}
In code where you use methods on string primitives javascript engine automatically wraps primitives with corresponding objects to perform methods call.
But in it is not an method call, its language operator an in this case wrapping is not applied.
PS: Sorry for my english.
typeof('test') == string (string literal)
typof(new String('test')) == object (string object)
you can't use in with a string literal.
The in operator returns true if the specified property is in the specified object or its prototype chain.
The in operator can only be used to check if a property is in an
object. You can't search in strings, or in numbers, or other primitive
types.
The first example works and prints 'true' because length is a property of a string object.
The second example doesn't work and gives you an error because you are trying to look for a property length in something (a string) that is not an object.
I have the following code:
String.prototype.isLengthGreaterThan = function(limit){
return this.length > limit;
}
console.log("John".isLengthGreaterThan(3));
Number.prototype.isPositive = function(){
return this > 0;
}
console.log(5.isPositive(3)); //error
The 1st example work, the other doesn't. Why?
From my understanding, primitives are not objects – though they do have access to their function constructor's prototype (Number, String, etc.). So, you can add methods directly to the prototype. In my example above it didn't work in one instance.
I am looking for "under the hood" answer, to understand what happens when you do, for example:
var a = 1;
How does it really have an access to its prototype if it isn't an object?
The dot thing isn't to do with primitives, it's just the syntax of writing numeric literals. In a number, the . is a decimal point, whereas of course that's not the case for a string. To use a method on a literal number, you have two choices:
Parens:
console.log((5).isPositive(3));
Two dots (yes, really):
console.log(5..isPositive(3));
In the second case, it works because the first dot is a decimal point; this means that the dot following can't be a decimal point, and so it's the property accessor operator.
How does it really have an access to it's prototype if it isn't an object.
It's promoted to an object automatically by the JavaScript engine when you do the propety access. In the case of numbers, it's as though you called new Number(5) or similar. In the case of strings, it's as though you called new String("the string"). When the property access is complete, the temporary object is then discarded immediately when the expression is complete. Naturally, the JavaScript engine can optimize the object allocation out, but conceptually that's what happens.
So conceptually:
We do (say) var n = 1; The variable a contains the primitive number value 1.
We do n.isPositive(3); to create a string from it using Number.prototype.toFixed. That's a property access operation:
The engine evaluates the left-hand side, n, and gets the result 1 (a primitive number); this is the base it'll use for the property access operation.
The engine evaluates the right-hand side (isPositive) to determine the property key (name) to look up. In this case it's a literal, so the key is the string "isPositive". This is the property key for the property access operation.
The engine goes to look up the property on the base. Since the base we have is a primitive, the JavaScript engine promotes it (coerces it) to an equivalent Number object.
Since that object doesn't have a "isPositive" property, the engine looks at its prototype, Number.prototype, and finds it; it's a reference to a function.
Various things happen that aren't really germane, and ultimately isPositive is called with this being an object coerced from the primitive value 1. It does its work and generates its return value.
Since the temporary object isn't referenced by anything anymore, it's eligible for garbage collection.
The mechanism by which this happens is a bit scattered in the specification:
The runtime semantics of the property accessor operator return something called a Reference specification type, which basically is a purely abstract specification holding object that will get evaluated later. It says that the base will be the result of evaluating the left-hand side of the property accessor, and the property anme will be the result of evaluting the right-hand side.
The GetValue operation on the Reference type, which says (step 5) that if it's a property reference and the base is primitive, it's coerced via the specification's ToObject operation.
ToObject, which defines the rules for doing that.
There are plenty of discussions about what null in JavaScript actually is. For example, Why is null an object and what's the difference between null and undefined?.
MDN lists null among primitive values and states that it is:
a special keyword denoting a null value; null is also a primitive
value
(The above emphasis is mine)
My last reference will be to Programming JavaScript Applications book by Eric Elliott, which in its Chapter 3. Objects says the following:
In JavaScript, ... even primitive types
get the object treatment when you refer to them with the property
access notations. They get automatically wrapped with an object so
that you can call their prototype methods.
Primitive types behave like objects when you use the property access notations, but you can't assign new properties to them. Primitives
get wrapped with an object temporarily, and then that object is
immediately thrown away. Any attempt to assign values to properties
will seem to succeed, but subsequent attempts to access that new
property will fail.
And indeed the following statements will execute without a problem:
"1".value = 1;
(1).value = "1";
false.value = "FALSE";
while his one
null.value = "Cannot set property of null";
throws Uncaught TypeError. See JS Fiddle.
So at least in this regard, null behaves differently than other primitives.
Is null considered a regular primitive in JavaScript?
Yes it's an actual primitive.
The exceptions for property access are null and undefined, because they have no wrapper type like strings, booleans and numbers do.
ECMAScript 5, Section 4.3.2 primitive
value
member of one of the types Undefined, Null, Boolean, Number, or String
as defined in Clause 8.
NOTE A primitive value is a datum that is represented directly at the
lowest level of the language implementation.
in firebug console :
>>> a=12
12
>>> a.__proto__
Number {}
>>> (12).__proto__
Number {}
>>> a.constructor.prototype === (12).__proto__
true
>>> a.constructor.prototype.isPrototypeOf(a)
false
the final line causes me a great confusion as compared to the other lines. also see Constructor.prototype not in the prototype chain?
When you use the . operator with a primitive, the language auto-boxes it with the appropriate Object type (in this case, Number). That's because simple primitive types in JavaScript really are not Object instances.
Thus, the actual left-hand side of
a.__proto__
is not the number 12 but essentially new Number(12). However, the variable "a" continues to be the simple number value 12.
edit — Section 8.7 of the spec "explains" this with typical ECMA 262 moon language. I can't find a clear paragraph that describes the way that a primitive baseValue is treated as a Number, Boolean, or String instance, but that section directly implies it. I think that because those non-primitive synthetic values are ephemeral (they're only "real" while the . or [] expression is being evaluated) that the spec just talks about the behavior without explicitly requiring that an actual Number is constructed. I'm guessing on that however.
#Pointy has explained it very well. Basically, if you want your last statement to be true, you would have to write it like:
a.constructor.prototype.isPrototypeOf(new Number(a));
In JavaScript primitives do not have a prototype chain. Only objects do. A primitive value includes:
Booleans
Numbers
Strings
Null
Undefined
Hence if you call isPrototypeOf with a primitive value then it'll always return false.
If you try to use a boolean, number or string as an object then JavaScript automatically coerces it into an object for you. Hence a.constructor evaluates to new Number(a).constructor behind the scenes. This is the reason you can use a primitive value as an object.
If you wish to use a variable storing a primitive value as an object often then it's better to explicitly make it an object. For example in your case it would have been better to define a as new Number(12). The advantages are:
JavaScript doesn't need to coerce the primitive to an object every time you try to use it as an object. You only create the object once. Hence it's performance efficient.
The isPrototypeOf method in your case will return true as a will be an instance of Number. Hence it will have Number.prototype in its prototype chain.
This question already has answers here:
Why can't I access a property of an integer with a single dot?
(5 answers)
Closed 5 years ago.
If you look at the ECMAScript 3 specification you will see that primitive value types Null and Undefined don't have accompanying Null and Undefined Objects.
>> Null
ReferenceError: Null is not defined
The other primitive value types Number, String and Boolean types do have accompanying Number, String and Boolean objects which you can reference from global scope.
>>Number
function Number() { [native code] }
>>Boolean
function Boolean() { [native code] }
The purpose for these primitive value types is to provide methods such as toString and valueOf for their respective primitive value types:
>>var n = 1;
>>n.toString();
"1"
is the same as
>>var n = 1;
>>Number.prototype.toString.call(n);
"1"
Booleans and strings also work that way:
>>var b = true;
>>b.toString();
"true"
>>Boolean.prototype.toString.call(b);
"true"
You can see that the primitive value objects are using the methods of their accompanying object when you try to mix types:
>>Boolean.prototype.toString.call(n);
TypeError: Boolean.prototype.toString is not generic
>>Number.prototype.toString.call(b)
TypeError: Number.prototype.toString is not generic
Interestingly enough for boolean and string literal types, you can call these methods directly from the literal:
>>true.toString();
"true"
>>Boolean.prototype.toString.call(true)
"true"
>>"moo".toString();
"moo"
>>String.prototype.toString.call("moo")
"moo"
Primitive values null and undefined, since they don't have accompanying Null and Undefined objects cannot do these things:
>>Null
ReferenceError: Null is not defined
>>null.toString()
TypeError: Cannot call method 'toString' of null
Primitive value type number behaves like a mix of the two. You can call toString on a literal if you directly use the Number's prototype object's method:
>>Number.prototype.toString.call(1);
"1"
But you cannot access the method from the literal itself like you can with strings and booleans:
>>1.toString()
SyntaxError: Unexpected token ILLEGAL
Why is it that number literals behave differently from boolean and string even though there's a Number object?
You can access it the same way, it's a different parsing issue here, to do it, use a slightly different syntax:
(1).toString()
Numbers can have decimals, so the syntax for ending in a decimal is a bit ambiguous when you go to parse the code, use parenthesis to be valid. It's a bit clearer when you see that this is also valid:
(1.).toString()
However with just 1.toString() it's trying to parse as a number with a decimal, and it fails.
I think you'll find an answer to your question in this answer to another Stack Overflow question. To summarize Theo's answer:
[T]he parser expects a number followed
by a dot to be a floating point
literal. [...] [Y]ou only have to add another dot to make it work[.]
Null (capital N) is a variable name. Reserved words are case sensitive. null is the object null. typeof null === "object" //true