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.
Related
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"
}
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.
So far I have relied on Object.prototype.toString.call(x) to distinguish between the different native object types in Javascript, arrays in particular.
If you subclass arrays, you get some strange behavior:
function Ctor() {}
Ctor.prototype = Object.create(Array.prototype);
var x = new Ctor();
x.push(1);
Object.prototype.toString.call(x); // [object Object]
Probably this is documented in the ES5 specs (and no longer an issue in ES6), but I consider it a quirk of the current version of the language. I adapted my corresponding functions as follows:
function objTypeOf(deep, type) {
return function _objTypeOf(x) {
do {
if (Object.prototype.toString.call(x).slice(8, -1).toLowerCase() === type) return true;
x = Object.getPrototypeOf(x);
} while(deep && x !== null);
return false;
};
}
var arr = objTypeOf(false, "array"),
arrP = objTypeOf(true, "array"); // array prototype
console.log(arr(x)); // false
console.log(arrP(x)); // true
objTypeOf checks the current object and the entire prototype chain until there is a type match. It accepts an object even if merely one of the prototypes matches the expected type. objTypeOf is not based on prototype identities, but on strings (lacking identity).
I wonder now if there are other edge cases when using Object.prototype.toString, that need special treatment?
Well your problem is not with Object.prototype.toString, but that you tried to subclass arrays. It just doesn't work, and toString correctly tells you that you failed to create an array. It's merely an object that has Array.prototype in its prototype chain (if that was what you cared for, use instanceof Array).
Regardless, to answer your title question:
What are the edge cases when using Object.prototype.toString?
Host objects. Everything that is not a native JS object, despite looking like one, might return any [[Class]] value that you didn't expect. There are even known cases where callable objects do not report Function.
If JavaScript will readily coerce between primitives and objects why adding properties to it results to undefined??
var a = "abc";
var b = a.length
console.log(b)//outputs 3
Does coercion allow me to assign values to primitives?If not why ?
Does coercion allow me to assign values to primitives?
Yes. The primitive is wrapped in an object, and a property is created on that. No exception will be thrown.
why adding properties to it results to undefined?
The adding itself does result in the value.
var str = "abc";
console.log(str.someProperty = 5); // 5
Yet, what you're asking for is getting a property from a primitive. This will return in undefined since the primitive is wrapped in a new wrapper object - not the one which you assigned the property on:
console.log(str.someProperty); // undefined
It only works for special properties like .length that are created with the object, or inherited ones like slice or charAt methods (see the docs for those).
If you wanted such a thing, you'd need to create the wrapper object explicitly and store it somewhere:
var strObj = new String("abc");
strObj.someProperty = 5;
console.log(strObj.someProperty); // 5
// coercion and the `toString` method will even make the object act like a string
console.log(strObj + "def"); // "abcdef"
Why don't you do it like this?
var abc=Number(3);
abc.foo="bar";
abc; //3
abc.foo; //bar
#Bergi indeed, sorry for this. I've missed the new statement. Here it is:
var abc=new Number(3);
abc.foo="bar";
abc; //3
abc.foo; //bar
At least it works just right. I don't know what else someone may need :)
primitives... coercion... blah.
So I am trying to learn javascript by learning how Mootools works internally. I am looking at these lines specifically:
var Type = this.Type = function(name, object){
if (name){
var lower = name.toLowerCase();
var typeCheck = function(item){
return (typeOf(item) == lower);
};
Type['is' + name] = typeCheck;
if (object != null){
object.prototype.$family = (function(){
return lower;
}).hide();
}
}
if (object == null) return null;
object.extend(this);
object.$constructor = Type;
object.prototype.$constructor = object;
return object;
};
//some more code
new Type('Type',Type);
What is happening here?
What is the object being assigned to in the constructor statement down at the bottom, the global window object?
Why is it being called as a constructor with the new statement when the Type function seems to only update the passed in object rather than creating a new one?
Specifically what is 'this' in the line object.extend(this); ? is it the global window object, if so is it adding all the key, value pairs of that object to the Type object?
Sheesh, questions lately seem to focus a lot more on mootools internals.
I will answer to the best of my knowledge as I am not a core dev.
Type in MooTools is pretty similar to Class (in fact, the Class constructor itself is a Type) but it focuses more on data / values and the Types of values. It also is meant to extend the native Types defined by ECMA spec and make them more flexible.
I guess there is no point in talking about data types in general (String, Number, Array, Object, etc). Why is there are need to extend them? Well, for starters, Duct Typing is a little quirky in js. typeof {}; // object, typeof []; // object, typeof new Date(); // object etc - not the most helpful, even if since all types inherit from object and is logical for them to be grouped together, it does not help you write code.
So, in the context of js objects, they are created from constructor objects...
What Type does is not replace the constructor functions but changes an existing constructor by adding new methods or properties to it.
eg. new Type('Array', Array);
This will turn the native Array constructor into a type object of sorts. You don't need to save the result or anything - it's a one off operation that mods the original and leaves it open for manipulation.
typeOf(Array); // type
So, what is different? Well, for starters, typeOf([]) is now able to actually tell us what it really is - array. And what really happens here is this: object.extend(Type);, the magical bit. It will copy to the target object all the properties defined on the Type object - you can see them here:
https://github.com/mootools/mootools-core/blob/master/Source/Core/Core.js#L211-232
So, immediately, your newly created Type gets the all important implement and extend methods.
More advanced, let's create a new type that is based on the native Array constructor:
var foo = new Type('foo', Array),
a = new foo();
// it's a real boy!
a.push('hi');
// ['hi'], foo, object
console.log(a, typeOf(a), typeof a);
But, what if we wanted a custom Type? One that is magical and special? No problem, because argument 2 can actually be a (anonymous) function.
var Awesome = new Type('awesome', function(name, surname) {
// console.log('I R teh construct0r!');
this.name = name;
this.surname = surname;
});
// extend it a little.
Awesome.implement({
getName: function() {
return this.name;
},
getSurname: function() {
return this.surname;
}
});
var Dimitar = new Awesome('dimitar', 'christoff');
console.log(typeOf(Dimitar)); // awesome
console.log(Dimitar.getName()); // dimitar
In another example, look at DOMEvent. It takes the above and makes it into a faster leaner Object type. https://github.com/mootools/mootools-core/blob/master/Source/Types/DOMEvent.js#L21
So why is that not a class? Because, Classes are more expensive and events happen all the time.
I hope this helps you somewhat. for a more detailed explanation, ask on the mailing list and hope for the best, perhaps a core dev will have time as it's not your standard 'how do I get the accordion working' type of question...