Я уверен, что вы могли видеть следующую строку кода, читая чей-то код или в библиотеке.

Object.prototype.hasOwnProperty.call(objRef, ‘propName’);

И теперь вам интересно, что, черт возьми, делает этот код. Вы начинаете сомневаться в своих навыках JavaScript. Не волнуйся. Вы находитесь в нужном месте. 😃

Я выбрал этот фрагмент кода для нескольких целей, и, демистифицируя его, мы поймем следующее:

  1. Что такое Object.prototype?
  2. Как объект может заимствовать функцию, не реализовав ее или не включив ее в цепочку прототипов?
  3. Почему мы получаем доступ к hasOwnProperty к прототипу объекта, а нет к самому экземпляру?

Если вам это кажется интригующим, приступим.

1. Object.prototype

Прототипное наследование - один из основных столпов языка JavaScript, который позволяет объекту наследовать методы и свойства своего прототипа. Вы можете думать о прототипе как о шаблоне.

Прототипное наследование позволяет объекту наследовать методы и свойства своего прототипа.

Лучше разобраться на примере:

var obj = {name: ‘aman’}
obj.hasOwnProperty(‘name’) // returns true

Как видите, мы не определили никакого hasOwnProperty для нашего obj объекта, но нам удалось его вызвать. Как это возможно? 🤔

Это возможно благодаря наследованию прототипов и принципу работы цепочки прототипов. Давайте копнем глубже.

Когда мы создали наш буквальный объект `obj`, его prototype был установлен на Object.prototype. Для проверки мы можем увидеть:

Object.getPrototypeof(obj) === Object.prototype // returns true

[[Prototype]] - это отношение наследования между объектами. В нашем случае это связь между obj и прототипом объекта.

Цепочка прототипов выглядит так:

// Prototype chain
obj — -> Object.prototype — → null

Когда мы пытаемся получить доступ к свойству объекта, интерпретатор сначала ищет его на самом объекте. Если ему не удалось найти свойство объекта, он будет перемещаться вверх, пока не найдет свойство в цепочке.

Таким образом, когда мы вызвали hasOwnProperty(), интерпретатор не смог найти его на obj, поэтому он проходит вверх по цепочке и находит его на Object.prototype.

Кроме того, мы можем настроить или переопределить цепочку прототипов, как захотим, используя метод Object.setPrototypeOf () или Object.create ().

Рассмотрим этот пример:

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. Заимствование функции

Представим, что у меня есть следующая функция и объект:

function sayHello() { console.log(`Greetings ${this.name}`) }
var person = {name: ‘peter’};

Как сделать объект personborrows sayHello и вызвать правильное имя в приветствиях? Мы не хотим, чтобы person реализовывала sayHello или чтобы она была где-нибудь в цепочке прототипов. 🤔

Ответ - через call и applymethod, доступные в Function.prototype.

В JavaScript каждая функция, которую мы создаем, наследуется от Function.prototype.
Как мы только что узнали, с помощью цепочки прототипов мы можем использовать callmethod для всех функциональных объектов. 💡

В JavaScript каждая функция, которую мы создаем, наследуется от Function.prototype.

Синтаксис вызова метода:

// ‘call’ method is available on Function.prototype
func.call(objRef, …args);

Первый аргумент - это объект, который хочет позаимствовать эту функцию, за которым следует список аргументов для этой функции.

Итак, чтобы person заимствовать sayHello, все, что нам нужно сделать, это использовать метод call для sayHelloпередачи person в качестве аргумента:

sayHello.call(person); // Greetings peter

3. Object.prototype.hasOwnProperty и instance.hasOwnProperty

После легкого руководства по функциям наследования и заимствования прототипов, наконец, пришло время прояснить, почему нужно использовать hasOwnProperty в Object.prototype, а не в экземпляре объекта.

Как мы уже упоминали выше, мы можем сами управлять цепочкой прототипов. Один из способов - использовать метод Object.create () при создании экземпляра объекта.

// 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

Попытка вызвать hasOwnProperty вызывает ошибку, поскольку такой метод недоступен для объекта или его цепочки прототипов. Цепочка прототипов была такой:

// Prototype chain
a — -> null

Вам может быть интересно, зачем кто-то создал такой объект. Но ирония заключается в том, что в JavaScript вы можете быть настолько сумасшедшими, насколько хотите 🔥.

Представьте, что вы создаете библиотеку и предоставляете функцию, которая принимает объект в качестве аргумента. Если ваша функция использует hasOwnProperty непосредственно на объекте, передаваемом извне, это может нарушить ваш код, если кто-то передаст объект с `null` в качестве его прототипа.

Следовательно, чтобы решить эту проблему, мы можем использовать метод заимствования функций, который мы изучили ранее. Переданный аргумент объекта может заимствовать hasOwnProperty из Object.prototype, как мы ранее узнали с помощью call метода 🚀😇.

// Will not break your code if ‘a’ has a null prototype. ✅
Object.prototype.hasOwnProperty.call(a, ‘name’); // true;

Резюме

  • Каждый литерал объекта наследуется от Object.prototype. Это позволяет вам вызывать некоторые из доступных методов, например hasOwnProperty.
  • Мы можем переопределить / создать цепочку прототипов с помощью метода Object.setPrototypeOf и с помощью Object.create (prototype).
  • Каждая функция наследуется от Function.prototype, что позволяет использовать такие методы, как call, apply и bind.
  • Объект может заимствовать другие функции без их реализации или включения их в цепочку прототипов. Этого можно добиться с помощью метода call или apply, доступного на Function.prototype.
  • Используйте Object.prototype.hasOwnProperty.call(objRef, ‘propName’) для защиты TypeError, когда objRef имеет прототип null.

На этом пока все. Надеюсь, вам понравилось читать эту статью и вы узнали несколько вещей. Иди и поделись этим достижением с другими 😍.