Aman's Blog

What's the deal with Object.prototype.hasOwnProperty.call()?

April 26, 2020

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:

  1. What is Object.prototype?
  2. How an object can borrow a function without implementing it or having it in its prototype chain?
  3. 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 prototye 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, and bind.
  • An object can borrow other functions without implementing them or having them in their prototype chain. This can be achieved by using call or apply method available on Function.prototype.
  • Use Object.prototype.hasOwnProperty.call(objRef, 'propName') to guard the TypeError when objRef has null prototype.

That’s all for now. Hope you have enjoyed reading this article and learned a few things. Go and share this achievement with others 😍.


Written by Amandeep Singh. Developer @ Thinkmill Sydney. Tech enthusiast and a pragmatic programmer.