Я уверен, что вы могли видеть следующую строку кода, читая чей-то код или в библиотеке.
Object.prototype.hasOwnProperty.call(objRef, ‘propName’);
И теперь вам интересно, что, черт возьми, делает этот код. Вы начинаете сомневаться в своих навыках JavaScript. Не волнуйся. Вы находитесь в нужном месте. 😃
Я выбрал этот фрагмент кода для нескольких целей, и, демистифицируя его, мы поймем следующее:
- Что такое Object.prototype?
- Как объект может заимствовать функцию, не реализовав ее или не включив ее в цепочку прототипов?
- Почему мы получаем доступ к 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’};
Как сделать объект person
borrows sayHello
и вызвать правильное имя в приветствиях? Мы не хотим, чтобы person
реализовывала sayHello
или чтобы она была где-нибудь в цепочке прототипов. 🤔
Ответ - через call
и apply
method, доступные в Function.prototype.
В JavaScript каждая функция, которую мы создаем, наследуется от Function.prototype.
Как мы только что узнали, с помощью цепочки прототипов мы можем использовать call
method для всех функциональных объектов. 💡
В 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.
На этом пока все. Надеюсь, вам понравилось читать эту статью и вы узнали несколько вещей. Иди и поделись этим достижением с другими 😍.