I understand that writing
var x = Number("7"); // makes a number, a primitive
var y = new Number("7"); // makes a Number object
and I'm aware of the usual cautions against option 2, but what is going on behind the scenes? I was under the impression if a function is a constructor, it should not return a value via a return statement: it just sets up its implicit object, this, and returns it.
So how come Number, String, and Boolean constructors are able to return either a primitive or an object? Does the JavaScript engine parse those expressions differently as a special feature in the language? Or can a programmer also "overload" a constructor function to either return a primitive or an object, depending on whether the constructor is called with/without "new"?
Using the constructor the Number object will be an object, using the function Number instead will return the conversion of an object to its representation as a numeric value.
So, basically within the Number object, the function is validating how was called. This is possible by checking the object this.
Something interesting is coercing the Number object to a numeric value as follow:
var x = Number("7"); // makes a number, a primitive
var y = new Number("7");
console.log(x === +y)
Go and read about the specification
https://www.ecma-international.org/ecma-262/5.1/#sec-15.7
It has nothing to do with syntax and there's nothing special about those constructors. The Number() and other constructors simply test to see whether this is bound before proceeding.
You can do it too:
function MyConstructor() {
if (!this) return new MyConstructor();
// stuff ...
}
Now calling MyConstructor() will behave exactly like new MyConstructor().
Also, a constructor can return something. If it returns an object, then that's used instead of the implicitly-constructed object that new creates. Thus you could also implement a "new-is-optional" constructor another way:
function MyConstructor() {
let object = this || Object.create(MyConstructor.prototype);
// stuff ...
return object;
}
So the Number() constructor takes a different tack. In most runtimes it may or may not actually be implemented as JavaScript, but if it were it might look something like this:
function Number(n) {
if (this) {
// invoked with "new"
this.fantasyValueSetter(n); // cannot really do this
return this;
}
return +n; // plain number primitive if not invoked with "new"
}
Related
This question already has answers here:
Messing with this in prototype's method
(2 answers)
Closed 1 year ago.
I'm manipulating the prototype of Object so that I can add some extension methods.
I've found out that typeof operator always returns object in case the operand is this:
Object.prototype.logType = function () { console.log(typeof this); }
"Hello".logType()
output of code above is object instead of string. I know that in JavaScript everything is indeed an object, However I need to know the exact type of the value of this. How can I achieve that?
When you call a method on a primitive, JS automatically wraps that primitive in its associative object wrapper, so:
"Hello".logType()
becomes:
new String("Hello").logType()
hence, this inside of your logType function refers to the wrapped primitive value, giving you an object. You can call .valueOf() to grab the primitive value wrapped in the object:
Object.prototype.logType = function () { console.log(typeof this.valueOf()); }
"Hello".logType(); // string
(1).logType(); // number
true.logType(); // boolean
1n.logType(); // bigint
Symbol().logType(); // symbol
(() => {}).logType(); // function
({}).logType(); // object
Or, you can use strict mode as suggested in the comments, as that keeps this as the original primitve:
Object.prototype.logType = function () { "use strict"; console.log(typeof this); }
"Hello".logType(); // string
(1).logType(); // number
true.logType(); // boolean
1n.logType(); // bigint
Symbol().logType(); // symbol
(() => {}).logType(); // function
({}).logType(); // object
When passed as this outside of strict mode, you have the very rare case that you encounter a primitive in their wrapped object form ("boxed"), as instance of String:
You can therefore check if your method was called on a string using this instanceof String instead of typeof this === 'string'. If you want to differentiate between strings and objects that inherit from String, you could use this.constructor === String instead.
To get a "regular" string back (or number for Number, or boolean from Boolean, etc.), you can call this.valueOf()
(This means you could also write typeof this.valueOf() - but note that this may be misleading because any object could return, say, a string from its valueOf method without actually having been a string originally.)
Note: You cannot differentiate between 'abc'.yourMethod() and new String('abc').yourMethod() this way because you get an instance of String either way.
(Also interesting: Seems like for newer types like BigInt or Symbol you cannot manually create such a wrapper instance, new BigInt(1n) will fail. But (function () { return this }).call(1n) would achieve the same, although I have no idea why anybody would want that...)
All that said: The easiest way to get the exact behavior you want (this being the actual string) is by defining your function in a context that is in strict mode:
(function {
'use strict'
Object.prototype.logType = function () { console.log(typeof this); }
})()
Now it will work as intended.
I'm building a DSL which would benefit from being able to hack some JS internals. I understand this is a very bad idea in general JS usage, but for my purposes it's okay. The following code works fine:
var str = new String("blah");
str.valueOf = function() { return 10 }
console.log(str * 10); // outputs 100
But this doesn't:
var str = "blah";
str.valueOf = function() { return 10 }
console.log(str * 10); // outputs NaN (because str === "blah")
Can someone who understands the internals a bit explain what's happening here? What's the underlying difference between these two examples?
And now what if I want to change the String prototype itself, so I can set the valueOf method of all strings, no matter when/where/how they are created? Is this possible? Unfortunately this doesn't seem to work:
String.prototype.valueOf = function() { return 10 }
console.log("blah" * 10); // NaN
Though this does:
String.prototype.valueOf = function() { return 10 }
console.log("blah".valueOf() * 10); // 100
And so does this:
String.prototype.valueOf = function() { return 10 }
console.log(new String("blah") * 10); // 100
Why does the JS engine treat "blah" and new String("blah") differently? Thanks!
By the way, here is a good article that sort of led me to explore this stuff.
When you do
var str = "blah";
you're creating a string primitive, but when you do
var str = new String("blah");
you're invoking the constructor and creating a String object.
When you have an object, javascript internally calls valueOf when trying to use that object where a primitive should be inserted.
For primitives it's the opposite, to be able to chain on methods, javascript needs an object, and internally primitives are wrapped with new String when calling object methods on the primitive.
In other words, when you have a string primitive, and you try to call str.valueOf, javascript will internally do new String(str) before it calls valueOf and returns the value.
However when you try to use the string direcly you still just have the primitive, and valueOf isn't called, the primitive value is inserted direcly.
From MDN
Note that JavaScript distinguishes between String objects and
primitive string values. (The same is true of Boolean and Numbers.)
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.
JavaScript automatically
converts primitives to String objects, so that it's possible to use
String object methods for primitive strings.
In contexts where a
method is to be invoked on a primitive string or a property lookup
occurs, JavaScript will automatically wrap the string primitive and
call the method or perform the property lookup.
Stoyan Stefanov in his excellent book 'Object-Oriented JavaScript' says:
Any value that doesn't belong to one of the five primitive types listed above is an object.
With five primitive types he means Number, String, Boolean, Undefined and Null. However in Google Chrome console it seems like number is not primitive type at all (compared to C primitive types like int). It looks like the primitive number has methods:
var a = 2.2;
console.log(a.toFixed()); // logs "2"
Thus I assumed that I can work with number as with an object, so I tried to assign a property to it:
var a = 2;
a.foo = 'bar';
console.log(a.foo); // logs undefined
I don't understand that behavior. If number has a method, it should behave like object, shouldn't it? It even has a prototype:
Number.prototype.foo = 'bar';
var a = 2;
console.log(a.foo); // logs 'bar'
So what is the magic behind this? How JavaScript treats objects versus primitive types? I would rather not use the word primitive and substitute it with simple objects. As I see it those are objects which can't be extended with new properties, however they are constructed through their constructor and also have prototype which can be extended like with normal objects.
[...] It looks like the primitive number has methods
The primitive, does not actually has its own properties. It gets coerced to an object in order to be able to access "its" properties. The coerced object is not accessable outside the called Method *(In strict mode even not inside the method)*. As such, the referenced variable is always a primitive.
Consider this simple example:
Number.prototype.myTypeInAMethod = function () {
console.log (typeof this.valueOf ()) //"number" => The primitive is wrapped in an object.
return typeof this;
}
var num = 123;
typeof num; //number
num.myTypeInAMethod () //object
side note: In ES5s strict mode,this would be a primitive and the type would be number
Since the variable num is a primitive, you can not assign values to it.
num.foo = "bar";
num.foo //undefined
If you instead create a number (or string) via its object constructor, its type indeed is an object.
A quick check by adding a property shows it can actually be assigned.
var objNum = new Number(123);
typeof objNum ; //"object"
objNum.foo = "bar";
objNum.foo //"bar"
So what is the magic behind this? How JavaScript treats objects versus primitive types?
This process is described in ES5 ยง8.7.1 GetValue
For an object:
If Type(V) is not Reference, return V.
Let base be the result of calling GetBase(V).
If IsUnresolvableReference(V), throw a ReferenceError exception.
If IsPropertyReference(V), then
If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal method of base, otherwise let get be the special [[Get]] internal method defined below.
Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
Else, base must be an environment record.
Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.
For a primitive:
The following [[Get]] internal method is used by GetValue when V is a property reference[1] with a
primitive base value. It is called using base as its this value and with property P as its argument.
The following steps are taken:
Let O be ToObject(base).
Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.
If desc is undefined, return undefined.
If IsDataDescriptor(desc) is true, return desc.[[Value]].
Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
If getter is undefined, return undefined.
Return the result calling the [[Call]] internal method of getter providing base as the this value and providing no arguments.
NOTE The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. The only situation where such an actual property access that uses this internal method can have visible effect is when it invokes an accessor function.
[1] IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.
There is a way to add a member-function or member-property to Number, String, ect...-Variables with the help of the prototype-property:
Number.prototype.member = function(){ console.log('number-member-function called'); };
or with help of the proto-property of the variables themselves:
var num = 7;
num.__proto__.member = function(){ console.log('number-member-function called'); };
Just like to any other kind of JavaScript-types.
But what is the difference of the implementation of Primtives and Objects in JavaScript so that the code below does not work for Numbers but for Objects?
var num = 7;
num.member = function(){ console.log('number-member-function called'); };
num.member(); // TypeError: num.member is not a function
var obj = {};
obj.member = function(){ console.log('object-member-function called'); };
obj.member(); // object-member-function called
Does anybody know approximatly how JavaScript Primitves and Objects are implemented under the hood? Because all JavaScript-implementations in all browsers must be done identically or almost for there is an error thrown with the Number-Function called member.
When you use the literal notation for the boolean, string and number types, the constructors (Boolean, String, Number) are not used. The primitive types still behaves almost like they had been instantiated with the new operator, but are truly primitive types.
Only when you are trying to use them as objects or you are trying to use a property of a constructor, the JavaScript interpreter creates a wrapper object around them behind the scenes.
After this, the wrapper objects gets discarded and you are dealing again with the primitive type.
So:
var num = 7; //primitive
// You are trying to use num as an object: a wrapper object gets created and discarded just after the assignment
num.member = function(){ console.log('number-member-function called'); };
// This will throw an error, because here num is primitive again (member was defined on the discarded wrapper)
num.member();
More on this topic can be found on the book "JavaScript Enlightenment" by Cody Lindley, based on the ECMA-262, Edition 3 specification.
I was experimenting with the new keyword and I can't find an explanation for this behavior.
Let's say we have a function returning an integer:
(In firebug)
>>> function x() { return 2; }
>>> x()
2
>>> new x()
x { }
But if the function returns an array :
>>> function y() { return [2]; }
>>> y()
[2]
>>> new y()
[2]
Why is that ?
The new operator has an interesting behavior: It returns the object created by the operator unless the constructor function returns a different object. Any non-object return value of the constructor function is ignored, which is why when you return 2 you don't see this.
Here's what happens when you say new x():
The interpreter creates a new blank object.
It sets the object's underlying prototype to x.prototype.
It calls x with this set to the new object.
In the normal case, x doesn't return anything and the result of the new expression is the new object created in step 1. But, if x returns a non-null object reference, then that object reference is the result of the new expression rather than the object created in step 1. Any other kind of return value (null, primitive numbers, primitive strings, undefined, etc.) is ignored; it has to be a non-null object reference to take precedence over the object new created.
This special treatment given to object references by the new operator lets you substitute a different object for the one new created. This can be handy in some limited situations, but the vast majority of the time, a function designed to be used with new (called a constructor function) shouldn't return anything at all.
For some light reading (hah!), this is covered by Section 13.2.2 ("[[Construct]]") of the specification (HTML; PDF), which is referenced by Section 11.2.2 ("The new operator").
Because an array is an object but 2 is not.
If you call a function with the new keyword, it has to return an object. If you don't do that explicitly, it automatically returns this (which is an empty object that inherits from funcName.prototype).