I am quoting this statement from the JavaScript Definitive Guide book in section 6.1.4 Object.create(). The following statement doesn't seem clear to me.
You can pass null to create a new object that does not have a prototype, but if you do this, the newly created object will not inherit anything, not even basic methods like toString() (which means it won't work with the + operator either)
var o2 = Object.create(null) // o2 inherits no props or methods.
At this point, I was thinking "Oh Wow". It doesn't inherit any basic methods, when you set Object.create(null). So, I tried to give it a try on console to see if this was really the behavior. I ran the script that is below, and got an unexpected result.
var o2 = Object.create(null);
o2.number = 1;
console.log(o2.number.toString()); // "1"
When I ran this code, I was thinking that the .toString was not going to work. I am bit confused or may not understand how things are working here. I was wondering if anyone could clear things up for me. Any help will be appreciated, and thanks for the time for reading my problem. :)
You're calling toString on the number property, which is not the object itself. If you were to try o2.toString(), it would not work.
toString works in your example because you're running it on a number, not the object itself.
It works because it's no different than this:
var n = 1;
console.log(n.toString());
To see the results of no prototype, try this instead:
var o2 = Object.create(null);
console.log(o2.toString());
When you do ...
o2.number = 1
... you're creating a property named number and adding that property to your o2 object.
When you do ...
o2.number.toString()
... you're executing toString not on o2, but on property o2.number.
If you do...
console.log(typeof o2.number)
... you'll see that o2.number has type number, and thus has all methods associated with numbers.
If you do ...
console.log(typeof o2)
... you'll see that o2 has type object.
If you try executing o2.toString, you'll get an error and see that this object indeed doesn't have any method named toString.
Note :
In my experience, you probably don't ever want to do something like ...
var o2 = Object.create(null);
o2.number = 1;
What you probably want instead, is something like ...
var o2 = Object.create(Object.prototype);
o2.number = 1;
... which can can be written more elegantly like ...
var o2 = {
number : 1
};
There is little to no advantage with creating objects that do not inherit from Object.prototype. And if other people end up using your code, you're likely to confuse the heck out of other developers when they're trying to call eg. hasOwnProperty or toString on your object and they're getting an error (as they these methods expect to exist for any object).
Related
I'm defining a JavaScript function like this:
function F() {
}
It gets some more properties and methods through a prototype object. When it's time to use the function, I'd like to see the object name, like for Array or other built-in objects. But I always see "Object" as the object type.
var i = new F();
console.log(i);
// Object { }
// wanted: something with "F" instead of "Object"
var a = [];
console.log(a);
// Array []
console.log(document);
// HTMLDocument about:newtab
What can I do to get the desired output?
I found several other questions here but none had the desired effect. I'm not too experienced with the whole prototype and new thing in JavaScript so I don't know the correct terms to find relevant results.
My function F must still be callable as a regular function, so I don't think I am interested in creating a class as supported in newer web browsers.
My code targets any web browsers that's in wide use today, which is probably Chrome, Firefox, Safari and a bunch of Android apps with unknown internals (Samsung?).
The display name of objects is controlled by their .toString() behavior. The default implementation of .toString() uses the value returned by the Symbol.toStringTag property (see here), which will be found on the Object prototype if not defined explicitly for your type. Thus it will have the value "Object" unless you override it.
function F() { }
let i = new F();
console.log(i.toString()); //[object Object]
F.prototype[Symbol.toStringTag] = "F";
console.log(i.toString()); //[object F]
So I was playing around the other day just to see exactly how mass assignment works in JavaScript.
First I tried this example in the console:
a = b = {};
a.foo = 'bar';
console.log(b.foo);
The result was "bar" being displayed in an alert. That is fair enough, a and b are really just aliases to the same object. Then I thought, how could I make this example simpler.
a = b = 'foo';
a = 'bar';
console.log(b);
That is pretty much the same thing, isn't it? Well this time, it returns foo not bar as I would expect from the behaviour of the first example.
Why does this happen?
N.B. This example could be simplified even more with the following code:
a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);
a = 'foo';
b = a;
a = 'bar';
console.log(b);
(I suspect that JavaScript treats primitives such as strings and integers differently to hashes. Hashes return a pointer while "core" primitives return a copy of themselves)
In the first example, you are setting a property of an existing object. In the second example, you are assigning a brand new object.
a = b = {};
a and b are now pointers to the same object. So when you do:
a.foo = 'bar';
It sets b.foo as well since a and b point to the same object.
However!
If you do this instead:
a = 'bar';
you are saying that a points to a different object now. This has no effect on what a pointed to before.
In JavaScript, assigning a variable and assigning a property are 2 different operations. It's best to think of variables as pointers to objects, and when you assign directly to a variable, you are not modifying any objects, merely repointing your variable to a different object.
But assigning a property, like a.foo, will modify the object that a points to. This, of course, also modifies all other references that point to this object simply because they all point to the same object.
Your question has already been satisfyingly answered by Squeegy - it has nothing to do with objects vs. primitives, but with reassignment of variables vs. setting properties in the same referenced object.
There seems to be a lot of confusion about JavaScript types in the answers and comments, so here's a small introduction to JavaScript's type system:
In JavaScript, there are two fundamentally different kinds of values: primitives and objects (and there is no thing like a 'hash').
Strings, numbers and booleans as well as null and undefined are primitives, objects are everything which can have properties. Even arrays and functions are regular objects and therefore can hold arbitrary properties. They just differ in the internal [[Class]] property (functions additionally have a property called [[Call]] and [[Construct]], but hey, that's details).
The reason that primitive values may behave like objects is because of autoboxing, but the primitives themselves can't hold any properties.
Here is an example:
var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);
This will output undefined: a holds a primitive value, which gets promoted to an object when assigning the property foo. But this new object is immediately discarded, so the value of foo is lost.
Think of it like this:
var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
You're more or less correct except that what you're referring to as a "hash" is actually just shorthand syntax for an Object.
In the first example, a and b both refer to the same object. In the second example, you change a to refer to something else.
here is my version of the answer:
obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"
// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"
// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
You are setting a to point to a new string object, while b keeps pointing to the old string object.
In the first case you change some property of the object contained in the variable, in the second case you assign a new value to the variable. That are fundamentally different things. The variables a and b are not somehow magically linked by the first assignment, they just contain the same object. That's also the case in the second example, until you assign a new value to the b variable.
The difference is between simple types and objects.
Anything that's an object (like an array or a function) is passed by reference.
Anything that's a simple type (like a string or a number) is copied.
I always have a copyArray function handy so I can be sure I'm not creating a bunch of aliases to the same array.
I'm trying my best to understand javascript. Here is a simple experiment in Chrome console which gets me very confused:
var foo=function(){this.p=1;}
foo.prototype.p=2;
var bar=new foo();
//foo{p:1,p:2} <- this is the output of Chrome console, from the last command above
The output of Chrome is what confuses me. It seems like bar is an object with 2 parameters, p:1 and p:2. Does this mean bar has 2 p??? What is the reasoning behind this?
Chrome DevTools console's inline (non-extended) object representation currently does not display any difference between own properties and inherited prototype properties.
Now let's break what's going on into smaller steps.
new foo() creates a new object whose internal proto property points to foo.prototype. This means this object can access all properties defined in foo.prototype. It's called prototype chain.
Now when you set a property of the same name in the object, it "shadows" the prototype's property by the same name, turning the latter inaccessible through regular property access (see #loxxy's answer using Object.getPrototypeOf(obj) to access the shadowed prototype property).
Once you add a function to the object or its prototype, the console allows you to display the extended object representation, which does differ own properties from prototype properties. In the next example I've added a q method to the prototype to allow this behavior. The properties inherited from the prototype are shown inside the object's proto internal property:
If you just want to have the number of instanced objects in the constructor's prototype, you can use:
var foo = function() {
Object.getPrototypeOf(this).p++;
}
foo.prototype.p = 0;
console.log(new foo()); //{p: 1}
console.log(new foo()); //{p: 2}
Or without the ES5 dependency:
var foo = function() {
foo.prototype.p++;
}
foo.prototype.p = 0;
console.log(new foo()); //{p: 1}
console.log(new foo()); //{p: 2}
Yes. Sort of.
bar has both:
A p property of its own.
bar.hasOwnProperty('p'); // true
bar.p; // 1
A p property still remaining on the prototype that it has through inheritance.
Object.getPrototypeOf(bar).p; // 2
Though, only 1 of them is accessible directly from bar at a time, with preference to the own property.
bar.p; // 1
delete bar.p;
bar.p; // 2
And, Chrome is showing both because it's traversing the prototype chain and looking for any enumerable properties.
The bar object has only one p with value 1
The earlier p with value 2 can be viewed in a readonly object which you can access with getPrototypeOf:
Object.getPrototypeOf(bar).p
You see both because the developer toolbar is designed to print an XML representation of the specified object which should intuitively show all the properties, whether directly accessible or not.
var foo=function(){this.p=1;} is constructor and executes after var bar=new foo();. So at the beginning p=2 and then p becomes 1. So:
var foo=function(){
// here this.p is equal to 2
this.p=1;
// here this.p is equal to 1
}
foo.prototype.p=2;
var bar=new foo();
EDIT:
JSON.stringify(bar);
When you acces a property, the javascript engine will seek it on the object instance, then on all its prototype chain.
So the meaning of p as a prototype property is to have a default value for p, wether you define it on any instance of the class or not. One example might be the number of wheel for a vehicle, that could default to 4, for instance.
If later you write to this property :
function Vehicle() {};
Vehicle.protoype.wheelCount = 4;
var myBike = new Vehicle();
myBike.wheelCount = 2 ; // this is a bike.
You won't change the value set on the prototype, but rather you'll create a new property on the instance, having the new value, so for instance :
var myCar = new Vehicle();
myCar.wheelCount // === 4
Now the very scenario you mention -setting a default value, and setting also an instance value in the constructor - doesn't make much sense, since you will have to use Object.getPrototypeOf to get to reach the default value. This is just a possibility that is of no use, just like there are many in all languages.
I've noticed this behavior when writing my JavaScript, and I haven't been able to figure out why:
Below is some code to reproduce the behavior in question.
var o1 = {
num: 1
}
var o2 = o1;
o2.num = 2;
alert(o1.num);
Expected Result: The browser alerts 1, because I only changed a property of the o2 object, not the o1 object.
Actual Result: The browser alerts 2, because it seems o1 is equal to o2.
I'm not really sure what's going on. How can I fix the code so it alerts 1 rather that 2 (assuming that o1 hasn't changed)?
Much thanks in advance.
Because both variables reference the same object. Objects are not cloned/copied on variable assignment. You would have to do this yourself.
JavaScript behaves the same way like any (most) other OO languages in this case.
By writing var o2 = o1; you're making o1 and o2 two references to the same object. What you want to do is to clone the o1 object and to store the cloned copy in o2. Search for cloning objects in JavaScript.
Because you are setting the objects to the same reference point. You need to clone the object. here is a piece of code from http://www.thespanner.co.uk/2008/04/10/javascript-cloning-objects/ that allows for cloning of objects with prototyping.
Object.prototype.clone = function() {
return eval(uneval(this));
}
alert("test".clone());
alert((3).clone());
alert(clone.clone());
So I was playing around the other day just to see exactly how mass assignment works in JavaScript.
First I tried this example in the console:
a = b = {};
a.foo = 'bar';
console.log(b.foo);
The result was "bar" being displayed in an alert. That is fair enough, a and b are really just aliases to the same object. Then I thought, how could I make this example simpler.
a = b = 'foo';
a = 'bar';
console.log(b);
That is pretty much the same thing, isn't it? Well this time, it returns foo not bar as I would expect from the behaviour of the first example.
Why does this happen?
N.B. This example could be simplified even more with the following code:
a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);
a = 'foo';
b = a;
a = 'bar';
console.log(b);
(I suspect that JavaScript treats primitives such as strings and integers differently to hashes. Hashes return a pointer while "core" primitives return a copy of themselves)
In the first example, you are setting a property of an existing object. In the second example, you are assigning a brand new object.
a = b = {};
a and b are now pointers to the same object. So when you do:
a.foo = 'bar';
It sets b.foo as well since a and b point to the same object.
However!
If you do this instead:
a = 'bar';
you are saying that a points to a different object now. This has no effect on what a pointed to before.
In JavaScript, assigning a variable and assigning a property are 2 different operations. It's best to think of variables as pointers to objects, and when you assign directly to a variable, you are not modifying any objects, merely repointing your variable to a different object.
But assigning a property, like a.foo, will modify the object that a points to. This, of course, also modifies all other references that point to this object simply because they all point to the same object.
Your question has already been satisfyingly answered by Squeegy - it has nothing to do with objects vs. primitives, but with reassignment of variables vs. setting properties in the same referenced object.
There seems to be a lot of confusion about JavaScript types in the answers and comments, so here's a small introduction to JavaScript's type system:
In JavaScript, there are two fundamentally different kinds of values: primitives and objects (and there is no thing like a 'hash').
Strings, numbers and booleans as well as null and undefined are primitives, objects are everything which can have properties. Even arrays and functions are regular objects and therefore can hold arbitrary properties. They just differ in the internal [[Class]] property (functions additionally have a property called [[Call]] and [[Construct]], but hey, that's details).
The reason that primitive values may behave like objects is because of autoboxing, but the primitives themselves can't hold any properties.
Here is an example:
var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);
This will output undefined: a holds a primitive value, which gets promoted to an object when assigning the property foo. But this new object is immediately discarded, so the value of foo is lost.
Think of it like this:
var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
You're more or less correct except that what you're referring to as a "hash" is actually just shorthand syntax for an Object.
In the first example, a and b both refer to the same object. In the second example, you change a to refer to something else.
here is my version of the answer:
obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"
// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"
// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
You are setting a to point to a new string object, while b keeps pointing to the old string object.
In the first case you change some property of the object contained in the variable, in the second case you assign a new value to the variable. That are fundamentally different things. The variables a and b are not somehow magically linked by the first assignment, they just contain the same object. That's also the case in the second example, until you assign a new value to the b variable.
The difference is between simple types and objects.
Anything that's an object (like an array or a function) is passed by reference.
Anything that's a simple type (like a string or a number) is copied.
I always have a copyArray function handy so I can be sure I'm not creating a bunch of aliases to the same array.