I am sure you might have seen the following line of code either while reading someone’s code or in a library.
Object.prototype.hasOwnProperty.call(objRef, 'propName');
And now you are wondering what on earth this code is doing. You start doubting your JavaScript skills. Don’t worry. You are at the right place.
I have chosen this piece of code for a few purposes, and by demystifying this, we will understand the following things:
- What is Object.prototype?
- How an object can borrow a function without implementing it or having it in its prototype chain?
- Why do we access
hasOwnProperty
on the Object's prototype and not on the instance itself?
If this sounds intriguing to you, let’s get started.
1. Object.prototype
Prototypal inheritance is one of the main pillars of JavaScript language which allows an object to inherit methods and properties on its prototype. You can think of the prototype as a template.
Prototypal inheritance allows an object to inherit methods and properties on its prototype.
It’s better to understand with an example:
var obj = {name: 'aman'}
obj.hasOwnProperty(‘name’) // returns true
As you see, we haven’t defined any hasOwnProperty
on our obj
object but we managed to invoke it. How is it possible? 🤔
This is possible due to the prototypal inheritance and the way prototype chain works. Let’s dig a bit deeper.
When we created our literal object obj
, its prototype was set to Object.prototype. To verify we can see:
Object.getPrototypeof(obj) === Object.prototype // returns true
[[Prototype]] is an inheritance relationship between objects. In our case, it’s the relation between the obj and Object’s prototype.
The prototype chain looks like:
// Prototype chain
obj —-> Object.prototype ——> null
When we try to access a property on an object, the interpreter first looks for it on the object itself. If it couldn’t find the property on the object, it will traverse up until it finds the property in the chain.
Thus, when we invoked hasOwnProperty()
, the interpreter couldn't find it on the obj
, so it traverses up in the chain and finds it on Object.prototype.
Additionally, We can set up or override the prototype chain however we want by using Object.setPrototypeOf() method or by using Object.create().
Consider this example:
var person = {name: 'peter'};
var PersonPrototype = {getName: function(){ return this.name; }};
// Setting person's prototype
Object.setPrototypeOf(person, PersonPrototype);
// Trying to access getName() method will cause a prototype chain lookup (aka prototype delegation)
// and finds it on PersonPrototype.
person.getName(); // 'peter'
2. Borrowing a function
Let's imagine If I have a following function and an object:
function sayHello() { console.log(`Greetings ${this.name}`) }
var a = {name: 'peter'};
How would you make the object a
borrow sayHello
, and invoke the correct name in the greetings? We don't want a
to implement sayHello
or have it anywhere on its prototype chain. 🤔
The answer is via call
and apply
method available on Function.prototype.
In JavaScript, every function we create inherits from Function.prototype.
And as we have just learned that via prototype chain, we can use call method on all function objects. 💡
In JavaScript, every function we create inherits from Function.prototype.
The syntax of call method is:
// 'call' method is available on Function.prototype
func.call(objRef, ...args);
The first argument is an object who wants to borrow this function followed by the list of arguments for that function.
So, for a to borrow sayHello
, all we have to do is use call
method on sayHello
passing a as an argument:
sayHello.call(a); // Greetings peter
3. Object.prototype.hasOwnProperty vs instance.hasOwnProperty
After a lightweight tutorial on Prototypal inheritance and borrowing functions, finally, it's time to demystify why one would use hasOwnProperty on the Object.prototype and not on the object instance.
As we mentioned above that we can control the prototype chain ourselves. One way is to use Object.create() method while creating Object instance.
// Object.create() accepts an argument which becomes
// the prototype for newly created object.
var a = Object.create(null); // Setting `null` as prototype for 'a'.
// Adding a 'name' property on the instance
a.name = 'peter';
// Using `hasOwnProperty` method would cause an error
a.hasOwnProperty('name'); //🚫 throws a TypeError: a.hasOwnProperty is not a function
Trying to invoke hasOwnProperty
throws an error as there's no such method available on the object or its prototype chain. The prototype chain was like:
// Prototype chain
a ---> null
You may be wondering why someone would create an object like this. But the irony is that in JavaScript you are allowed to be as crazy as you want 🔥.
Imagine you are creating a library and exposing a function that accepts an object as an argument. If your function makes use of hasOwnProperty
directly on the object being passed from outside, it could break your code if someone passes an object with null
as its prototype.
Consequently, to guard this problem we can use function borrowing technique we previously learned. The passed-in object argument can borrow hasOwnProperty
available on Object.prototype as we previously learned via call
method. 🚀😇.
// Will not break your code if 'a' has a null prototype. ✅
Object.prototype.hasOwnProperty.call(a, 'name'); // true;
Summary
- Every object literal inherits from Object.prototype. This allows you to invoke some of the methods available like
hasOwnProperty
. - We can override/create the prototype chain with the help of Object.setPrototypeOf method and via Object.create(prototype).
- Every function inherits from Function.prototype allowing you to consume methods like
call
,apply
, andbind
. - An object can borrow other functions without implementing them or having them in their prototype chain. This can be achieved by using
call
orapply
method available onFunction.prototype
. - Use
Object.prototype.hasOwnProperty.call(objRef, 'propName')
to guard the TypeError when objRef hasnull
prototype.
That's all for now. I hope you have enjoyed reading this article and learned a few things. Go and share this achievement with others 😍.
Top comments (6)
crazy, just the post I was looking for.. thanks for taking the time to include great examples and a very intuitive explanation.
great post, really usefull!
Very well explained
I think we can use more explanation about this binding in hasOwnProperty function when we write this statement Object.prototype.hasOwnProperty.call(objRef, 'propName') ?
Thanks a lot, useful!
Thanks for the post.