Use cases of Object.create(null)? - javascript

If you create a regular javascript object using say var obj = {}; it will have the object prototype. Same goes for objects created using var obj = new MyClass(); Before Object.create was introduced there was no way around this. However nowadays it's possible to create an object with no prototype (respectively null as its prototype) using var obj = Object.create(null);.
Why is this important? What advantages does it bring? Are there any real world use cases?

It's a completely empty object (nothing inherited from any .prototype including Object.prototype), so you can be guaranteed that any property lookup will only succeed if the property was explicitly added to the object.
For example, if you want to store some data where you won't know the keys in advance, there's a possibility that a provided key will have the same name as a member of Object.prototype, and cause a bug.
In those cases, you need to do an explicit .hasOwnProperty() check to rule out unexpected inherited values. But if there's nothing inherited, your testing can be simplified to a if (key in my_object) { test, or perhaps a simple truthy test if (my_object[key]) { if appropriate.
Also, with no prototype chain, I would imagine that lookups of properties that turn out to not exist would be faster, since only the immediate object would need to be checked. Whether or not this pans out in reality (due to optimizations) would be determined with performance testing.

The only difference here between creating an object with {} and Object.create(null) is that the Object prototype will not be inherited from.
This means that any of the methods we typically assume we can call on any object will not exist, such as toString and valueOf. A list of these can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
From a performance perspective, creating an object simply by {} is actually much faster, so unless you specifically cannot have the functions under the Object prototype you should not create objects in that manner.
http://jsperf.com/null-vs-empty-object-performance

Related

Is the following explanation of `prototypes` in Javascript valid?

I have been trying to learn about prototypes since I needed quite a bit of clarity on them. I have been using this MDN article and its related articles for reference.
After some reading, I got a bit of clarity on prototypes and tried putting them in my own words and would like to know if it is right and would love to know if and where I am wrong
START OF EXPLANATION
Every object is created using a constructor function of some sort. Say, we create an object as follows.
let obj = new Object();
Here, Object is the constructor function. The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them. This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function. You can check the prototype property as follows:
console.log(Object.prototype);
The above piece of code will return an object with a bunch of methods that any object created using new Object() can use.
In the above example, if the wording is too confusing, you can replace all occurrences of Object with any other constructor function such as Array, Date or even custom constructor functions such as Person or something else that you may have defined.
END OF EXPLANATION
Is my understanding right? If not, can you point me where I went wrong?
Is my understanding right? If not, can you point me where I went wrong?
In big picture terms, yes, your understanding is mostly correct, but the explanation is incomplete, and there are some specifics that are incorrect.
Every object is created using a constructor function of some sort.
That's not quite correct, JavaScript also has literal forms ({} [object], [] [array], and // [regular expression]) that create objects without using a constructor function. Those forms assign Object.prototype, Array.prototype, and RegExp.prototype (respectively) to the objects they create, even though the constructor itself isn't invoked.
There are also other ways of creating objects that don't go through constructor functions at all. For instance, there's Object.create, which creates an object and assigns the prototype you supply to it:
const p = {};
const obj = Object.create(p);
console.log(Object.getPrototypeOf(obj) === p); // true
(There are also more obscure ways of creating objects through implicit conversion.) You can also change the prototype of an existing object by using Object.setPrototypeOf.
The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them.
Not quite, arrow functions and class methods do not have a prototype property and cannot be used as constructors:
const arrow = () => {};
class X {
method() {
}
static staticMethod() {
}
}
console.log("prototype" in arrow); // false
console.log("prototype" in X.prototype.method); // false
console.log("prototype" in X.staticMethod); // false
This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function.
Correct. (Constructor functions can mess with what they return, but that's the usual, standard behavior.)
At this point in an explanation I'd probably point out the distinction between the prototype property on functions and the prototype of an object. Beginners sometimes think setting a prototype property on an object will change its prototype; it doesn't, that name is only significant on functions, and it's not the function's prototype, it's just a property that (as you said) will be used to assign the prototype of an object created using new with that function. The prototype of an object is held in an internal field of the object called [[Prototype]]. That field isn't directly accessible, but you can access it via Object.getPrototypeOf and change it via Object.setPrototypeOf (you can also use the deprecated __proto__ accessor property, which is just a wrapper around those functions — but don't use __proto__, use the functions directly).
But aside from all that, there's a big unanswered question in your explanation: What are prototypes for? What do they do? Why have them?
The answer is that they provide JavaScript's inheritance mechanism. When you get the value of a property on an object and the object doesn't have a property of its own with the given key, the JavaScript engine looks at the object's prototype to see if it has the property (and the prototype of the prototype, and so on through the chain):
const parent = {
a: "a property on base",
};
const child = Object.create(parent);
child.b = "a property on child";
const grandChild = Object.create(child);
grandChild.c = "a property on grandChild";
console.log(grandChild.a); // "a property on base"
console.log(grandChild.b); // "a property on child"
console.log(grandChild.c); // "a property on grandChild"
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`hasOwn(grandChild, "a")? ${hasOwn(grandChild, "a")}`); // false
console.log(`hasOwn(grandChild, "b")? ${hasOwn(grandChild, "b")}`); // false
console.log(`hasOwn(grandChild, "c")? ${hasOwn(grandChild, "c")}`); // true
Those example property values are strings, but this is widely used where the property values are functions, providing a means of inheriting methods from parent objects.
The property access process is asymmetrical, though; it only works as described above for getting a property's value. If you set a property's value on an object, it always sets it on the object itself, not on its prototype:
const parent = {
prop: "parent",
};
const child = Object.create(parent);
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`[Before] child.prop: ${child.prop}`);
// => "[Before] child.prop: parent"
console.log(`[Before] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[Before] hasOwn(child, "prop")? false"
child.prop = "child";
console.log(`[After] child.prop: ${child.prop}`);
// => "child.prop: child"
console.log(`[After] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[After] hasOwn(child, "prop")? true"
(This difference between getting and setting the property value applies to data properties [the kind we mostly create]; accessor properties work differently because getting and setting the property result in function calls, and the accessor's setter function can do whatever the author wants it to do.)

Why do some array methods rely on the global Array object?

I'm going through the MDN docs on arrays and when we want to test whether or not an object is an array we use isArray(). However, it's usage is very different to most of the other methods. When you use the regular syntax an error pops up:
console.log([1,2,3].isArray()); // TypeError: [1, 2, 3].isArray is not a function
Whereas this does work:
console.log(Array.isArray([1,2,3]))
I don't understand why isArray() (and a couple of other methods) rely upon some global object rather than just being accessible via the object in question. Why do some methods require the global array object?
rather than just being accessible via the object in question.
Because the whole purpose of Array.isArray is to be called on unknown objects. You don't know whether it's an array, you wouldn't know whether the method was accessible on it. The static method even works with values like null, which would inevitably throw an exception when you tried to invoke a method on them.
Digression:
isArray being callable as an instance method can work. In languages where everything is an object, like Smalltalk, Ruby or Io, even things like nil can have a isNil method. This approach has other problems of course, as with dynamic method dispatch every arbitrary object could overwrite the method and claim to be an array - on the other hand, that's exactly what we want for duck typing.
We could even simulate this in JS using Object.prototype.isArray = () => false; and Array.prototype.isArray = () => true;. Apart from failing on null and undefined, it still wouldn't work with objects that don't inherit from (our realm's) Object.prototype. And JavaScript "properties" that mix data fields and methods don't help either (consider the object parsed from the JSON string {"isArray":"maybe"}). We would always have to expect an exception from either .isArray not being a function, or from it being overwritten with a method that throws.
If we want to go for duck typing in JS, checking whether an object has an integer .length property is usually the way to go. Or more advanced, trying to follow the symbol-based iteration protocol. (That's what Array.from uses, for example).
But since arrays are a bit special in JS (with their magic .length property), we want a built-in reliable way to detect them, and that's what Array.isArray does.
Regarding other static Array methods: Array.of is pretty obvious, it's a factory function (like a constructor) and it can't be an instance method because there is no instance to work with in the first place. For Array.from the situation is a bit more like with isArray, a duck-typing Object.prototype.toArray approach could have worked as well but was dismissed for practical and design reasons.
See also Why were ES5 Object methods not added to Object.prototype?, Why use Object.prototype.hasOwnProperty.call(myObj, prop) instead of myObj.hasOwnProperty(prop)? and Why is it Object.defineProperty() rather than this.defineProperty() (for objects)? for similar discussions.
.isArray() is a static method, it exists on the Array "class" but not on each particular instance. Static methods are called like this: Array.isArray(). Object.create() is another example.
Methods like .map or .slice are not static, so they exist on each instance of the Array "class".
Array (denoted with a captial "A") indicates the JavaScript native Array object. [...] represents an instance of an array. As with many languages, some properties/methods are static. That is, they don't exist on instances only the type. This is often done when the value of the property or behavior of the method doesn't vary from instance to instance, so there's no need for it to be "instance-specific". Figuring out if something is an array, doesn't change from instance to instance (and frankly, doesn't make a lot of sense to ask an array instance if it is an instance of an Array).

Is there a way to make 'constructor' a valid key in a JS object?

I'm currently making a chat AI that uses markov chains. One of the backend objects is an object that maps a string to any chain that it is a part of, such that:
var words = {}
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ]
Now, I have a problem. 'constructor' is a perfectly valid word, but it appears as though you can't map it as I have described above, as 'constructor' is a property of every object in JS (along with other things, of course). So to my question: is there a way to construct this map?
constructor isn't a property of every object. Though by default every object created from literal syntax will have constructor in its prototype chain. This is because those objects inherit from Object.prototype, which does have that property.
One solution is to use objects that have no prototype chain.
var words = Object.create(null);
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ];
Now there will be no inherited properties to confuse things. This won't work in IE8 and lower though.
Another solution is to use .hasOwnProperty() to see if the constructor property is on the object itself or inherited. If inherited, then it's not the one you want.
if (words.hasOwnProperty("constructor"))
console.log(words.constructor);
If the condition passes, then we know we're not using the inherited property, but rather the object's own property.

understanding Prototype in javascript [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does JavaScript .prototype work?
Coming from Java background, I'm trying to understand javascript.
Please let me know if these are right.
Like in java, there is a supreme Object, from which all other objects inherit.
The prototype property is like a pointer that points to the parent object (classes in java)
For "Object" object, the prototype is null.
prototype property's values are strings denoting the objects nomenclature and aren't pointers like in C. The pointer concept is implemented using the hidden attribute,[[PROTOTYPE]] that is not accessible in script.
I'm using node.js instead of browser to learn JS.
I tried,
var human = Object.create(null); // same as var human;
console.log(Object.getPrototypeOf(human)); //null
var man = Object.create(human);
console.log(Object.getPrototypeOf(man));
//{}
//expected 'human'
var person = Object.create(Object.prototype); // same as var person = {}
console.log(Object.getPrototypeOf(person));
//{}
//expected 'object'
Yes, with the exception of Object.create(null) which doesn't inherit from anything.
Not sure what "parent object" is, but the prototype of a function consists of functions that are accessible by instances. Because Object.prototype.toString exists, so does {}.toString - it is inherited.
Yes, the prototype chain ends here.
No, .prototype (or the value returned by Object.getPrototypeOf) is an object which you can extend with properties, so that those properties are inherited by instances.
Your examples:
var human = Object.create(null); // same as var human;
No, it creates an empty object that doesn't inherit from anything. var human; sets human to undefined - which is not an object but a primitive value (not everything is an object in JavaScript).
var man = Object.create(human);
console.log(Object.getPrototypeOf(man));
//{}
//expected 'human'
Object.getPrototypeOf(man) returns the object human. This is an empty object; node shows it as {}. It is not a string. In fact, objects can be assigned to multiple variables. An object does not have a variable bound to it, so by design it is not possible at all to get a string. To check equality, you could do Object.getPrototypeOf(man) === human which yields true.
var person = Object.create(Object.prototype); // same as var person = {}
console.log(Object.getPrototypeOf(person));
//{}
//expected 'object'
This is indeed the same as {} - an empty object that inherits directly from Object. As said above, the prototype is Object.prototype and not a string. It looks empty but that's because Object.prototype's functions are available by default and hidden.
First, you need to read about the create method before you attempt to use it.
Second, invoke objects in the following manner:
var human = {}, man = {}, person = {};
Third, this is not Java. In Java classes and an understanding of classes are pretty important. Prototypes are completely optional in JavaScript. The only performance benefit offered by prototypes comes when you code makes heavy reuse of functions by reference in different scopes. If this style of coding does not apply to you then you likely do not need to use prototypes.
Prototypes are inherited from the parent, or some ancestor, object. A JavaScript parser will first look for local assignments and if none exist will then examine the current object's prototype for the presence of the named reference. In a nutshell this all you need to know at this early point in writing JavaScript.
Firstly, IMO, don't start off with node. Node is going to drag you into a pool of quicksand if you don't know javascript yet. Secondly, when I started off with function based code, then OO and only then I moved to prototypes. But if you really want to know, this question has already been answered over here. Feel free to ask me if you have any more questions!

Is instantiating a object really just inheritance in some way in javascript?

I was reading a JavaScript book and as I was reading a chapter about inheritance, I wondered if every time you create a instance of a object is it really inheritance that happens, especially since they are similar.
Examples: Prototype Chaining - someObject.prototype = new someOtherObject();
Instantiating an object var myArray = new Array()
Similar right?
Additionally the book(JavaScript for web developers, By:Nicholas Zakas) stated, a link exists between an instance and a constructor's prototype.(The link is usually known as __proto__)
So can one argue that instantiating a object is like inheritance?
No. You are confused because one of the methods of accomplishing inheritance (viz. prototypal inheritance) takes, as the prototype, a given object. And one method of acquiring objects is via constructors.
In other words, there is nothing special about someObject.prototype = new SomeOtherObject(). You could just as well do
someObject.prototype = { myObjectLiteral: "isJustAsGoodAsYourConstructoredInstance" };
or even someObject.prototype = false. The two operations, very literally, have nothing to do with each other.
EDIT To address the quote OP found in his book:
"Recall the relationship between
constructors, prototypes, and
instances: each constructor has a
prototype object that points back to
the constructor, and instances have an
internal pointer to the prototype."
Unfortunately, this is misleading at best, and plain wrong at worst. Consider what "each constructor has a prototype object that points back to the constructor" would mean. It would mean that, for example,
Array.prototype === Array
Function.prototype === Function
function CustomConstructor() { }
CustomConstructor.prototype === CustomConstructor
But all of these statements output false if you type them in your console.
Now consider "instances have an internal pointer to the prototype." This is true, but does not fit with the terminology he is using. What this means is:
var myProto = {};
function CustomConstructor() { }
CustomConstructor.prototype = myProto; // inherit from the myProto object.
var x = new CustomConstructor(); // x is an "instance"
Object.getPrototypeOf(x) === myProto; // Object.getPrototypeOf fetches the "internal pointer"
But as you can see, I didn't inherit using a constructor, as I tried to point out above. Instantiating an object is not inheritance in any way. It is simply that the prototype property of constructors is how prototypal inheritance is accomplished, just like classical inheritance is accomplished by placing extends BaseClass after a class definition in Java. In turn, this difference is present because prototypal inheritance lets you inherit a prototype object instance, not a base class.
The JavaScript Garden has a few good sections that might help if you want more information. Relevant are "The Prototype," "Constructors," and "The instanceof Operator."
Without using objectType.inherits() if I got your concept of inheritance then it is if you take just the right part:
new someOtherObject()
This is instantiation of someOtherObject.
What happens here is you are creating an object of type someOtherObject that inherits structure from an empty object (provided you have not set a prototype on this). Because the default value of prototype is an empty object {}.
You are putting this object as prototype of someObject:
someObject.prototype = new someOtherObject();
Now if you create an instance of someObject this will happen:
You create someObject that inherits the structure of someOtherObject in that point in time, which inherits the structure of (empty)object when it has been instanced.
Maybe a better explanation is saying that the instance gets extended with the structure of its prototype at that point in time, and leave all of the inheriting wording aside, since just using that can mislead. Since classic inheritance uses base classes (types) and proto uses instances of those. Now it all depends on how you look at inheritance, which to argue about does not exists at all in javascript (object types are mutable at runtime) as by Karl Marx argues that real communism is not possible at all in the concept. If you agree with this last statement then comparing and asking about it does not matter, because neithercase is.

Categories

Resources