Первоначально появилось на: https://www.zeolearn.com/magazine/cracking-the-front-end-developer-interview
В. Что вы знаете о вызове, привязке и применении? В чем разница между вызовом и привязкой?
A. Мы читали об this в предыдущей части. Его значение определяется тем, как функция вызывается. Это была неявная привязка. Но мы также можем вызвать нашу функцию с помощью нашего собственного this, используя call или apply. Это называется явной привязкой.
var myMethod = function () { console.log(this); }; var myObject = { myMethod: myMethod }; var newObject = { myNewMethod: myMethod}; myMethod() // this === window myObject.myMethod() // this === myObject myMethod.call(newObject, args1, args2, …) // this === newObject myMethod.apply(newObject, [array of args]) // this === newObject
Хороший пример этого можно найти по адресу https://codepen.io/shoaibkhan94/pen/qxaEPw?editors=1010.
Итак, что делает метод call? Он вызывает функцию с заданным значением this и аргументами, предоставленными индивидуально. Что делает метод apply? Он аналогичен вызову с принципиальной разницей в том, что call () принимает список аргументов, а apply () принимает единственный массив. аргументов.
А как насчет метода bind? Метод bind () создает новую функцию, при вызове которой для ключевого слова this устанавливается указанное значение.
myNewMethod = myMethod.bind(myObject); myNewMethod(); // this === myObject
Это может показаться похожим на метод call (). Основные различия между bind () и call () заключаются в следующем:
call () сразу выполняет функцию, к которой он был вызван. bind () возвращает новую функцию вместе с this, установленным на this, указанное внутри bind (). .
В. Что такое закрытие и как / зачем вам его использовать?
A. В официальной документации MDN говорится: замыкание - это комбинация функции и лексической среды, в которой эта функция была объявлена. Чтобы понять замыкание, вы должны понимать область действия и цепочка областей действия (область действия я рассмотрел в предыдущей статье). Рассмотрим этот пример:
// We have two functions. One is named outer & other is named inner. //this is inner’s global scope function outer(name) { //this is inner’s outer scope var text = ‘Hello ‘ + name; var inner = function() { //this is inner’s local scope console.log(text); } inner(); } outer(‘John’); //Outputs ‘Hello John’.
Из приведенного выше примера мы видим, что внутренняя функция имеет доступ к трем цепочкам областей видимости. Внутренняя функция имеет доступ к своим собственным переменным через первую цепочку. Он может получить доступ к переменным и параметрам внешней функции через вторую цепочку. И третья цепочка позволяет ему получить доступ к глобальным переменным. При каждом добавлении функции мы добавляем новую ссылку в цепочку областей видимости.
Каждый раз, когда мы создаем функцию внутри другой функции, мы создаем замыкание. Другой пример :
function init(args){ var firstName = args.firstName; var lastName = args.lastName; function returningFunc(action){ return firstName + “ “ + lastName + “ “ + “is ” + action; } return returningFunc; } var data = {firstName: “Alan”, lastName: “Smith” }; var zombieOne = init(data); //when we initialize the outer function, it returns undefined. The outer function is dead, but… /* 20 lines of code later… */ zombieOne(“walking”); // Outputs ‘Alan Smith is walking’
Причина, по которой мы все еще можем получить доступ к имени этого «зомби», не связана с какой-либо научной фантастикой, а потому, что замыкания не сохраняют ценность. Значения статичны. Вместо этого они хранят ссылки на значения в своих цепочках областей видимости. Удивительно то, что если вы измените значение переменной в цепочке областей видимости перед возвратом замыкания, то будет обновлено то же значение, что и в замыкании. Пример:
function outer() { // Local variable that ends up within closure var num = 30; var say = function() { console.log(num); } num++; return say; } var sayNumber = outer(); sayNumber(); // logs 31
Подводя итог, закрытие в JavaScript похоже на сохранение копии всех локальных переменных, как это было при выходе из функции.
Использование закрытия:
Создание модульных шаблонов с частными переменными и функциями. Пример:
var zombieOne = (function(){ //private variables var firstName = “”; var lastName = “”; //private functions function init(data){ firstName = data.firstName; lastName = data.lastName; } function combineName(){ return firstName + “ “ + lastName; } function gerunding(action){ return firstName + “ “ + lastName + “ “ + “is ” + action; } //public functions return { getName: function(){ return combineName(); }, setName: function(data){ return init(data); }, setAction: function(action){ return gerunding(action); } }; })(); var data = {firstName: “Alan”, lastName: “Smith” }; zombieOne.setName(data); zombieOne.getName(); // “Alan Smith” zombieOne.setAction(“walking”);// “Alan Smith is walking”
Вы можете проверить этот код для лучшего понимания.
P.S. : В последнем примере мы создали анонимное закрытие или выражение немедленного вызова функции (IIFE). Они выполняются немедленно и позволяют нам управлять конфиденциальностью.
В. Что такое обещания? Каковы плюсы и минусы использования обещаний вместо обратных вызовов?
A. Обещание - это объект, который представляет значение, доступное в результате асинхронной операции: либо разрешенное значение, либо причину, по которой оно не разрешено (например, произошла сетевая ошибка).
Согласно MDN Promise находится в одном из следующих состояний:
- ожидает: начальное состояние, ни выполнено, ни отклонено.
- выполнено: означает, что операция успешно завершена.
- отклонено: это означает, что операция не удалась.
Это похоже на передачу обратных вызовов функции. Обещания возвращают объект, к которому вы можете прикрепить обратные вызовы. Если вы не уверены в том, что такое обратный вызов, это просто функция, вызываемая по завершении данной задачи.
Например, вы могли создать несколько обратных вызовов для обработки случаев успеха и ошибок, как показано ниже:
function getUserSuccess(user) { console.log(“The User is: “ + user); } function getUserFailure(error) { console.log(“Failed to get user: “ + error); } API.getUser(getUserSuccess, getUserFailure);
… С введением обещаний функции теперь возвращают обещание, к которому вы можете вместо этого прикрепить свои обратные вызовы:
let promise = API.getUser(); promise.then(getUserSuccess, getUserFailure);
или просто
API.getUser().then(getUserSuccess, getUserFailure);
Плюсы и минусы
У вас может быть пирамида гибели или ад обратного вызова, что является классическим случаем нескольких асинхронных операций, выполняемых одна за другой:
API.getUser(function(user) { API.getUserAddress(user, function(address) { // do something with address API.saveUserAddress(address, function(newAddress) { console.log(‘New User Address: ‘ + newAddress); }, failureCallback); }, failureCallback); }, failureCallback);
WithWith promises, мы можем прикрепить наши обратные вызовы к обещаниям, возвращаемым функциями, и сформировать цепочку обещаний:
API.getUser().then(function(user) { return API.getUserAddress(user); }) .then(function(address) { // do something with address return API.saveUserAddress(address); }) .then(function(newAddress) { console.log(‘New User Address: ‘ + newAddress); }) .catch(failureCallback); //One catch to handle all failures
Обещания также упрощают обработку ошибок. Учти это:
save().then(handleSuccess,handleError);
С помощью обещаний вы не должны обрабатывать ошибки внутри .then (). Кроме того, handleSuccess () может сам выдать ошибку. Поэтому это считается антипаттерном. Рекомендуется следующее:
save().then(handleSuccess).catch(handleError);
В приведенном выше примере .catch () будет обрабатывать отклонения либо от save (), либо от ошибок от handleSuccess (). Поэтому рекомендуется заканчивать все цепочки обещаний с помощью .catch ().
Хорошее общее правило в программировании - использовать для работы простейший инструмент. Это один из недостатков использования простых обратных вызовов вместо обещаний, поскольку обратный вызов - более простой инструмент, чем обещания, хотя проблема не менее проста. Для одиночного асинхронного запроса подойдет обратный вызов. Но для нескольких запросов одновременно обещания подходят больше, чем обратные вызовы.
В. В чем разница между наследованием классов и прототипов?
A. Наследование в JavaScript отличается от большинства других языков. Система объектов в JavaScript основана на прототипах, а не на классах.
Объекты в JavaScript - это просто набор пар имени (ключа) и значения.
Согласно MDN, когда дело доходит до наследования, в JavaScript есть только одна конструкция: объекты. Каждый объект имеет частное свойство, которое содержит ссылку на другой объект, называемый его прототипом. У этого объекта-прототипа есть собственный прототип, и так до тех пор, пока не будет достигнут объект с нулевым значением в качестве его прототипа. По определению, null не имеет прототипа и действует как последнее звено в этой цепочке прототипов.
Все объекты в JavaScript являются экземплярами Object, который находится на вершине цепочки прототипов.
function Greeter (name) { this.name = name || ‘John Doe’; } Greeter.prototype.hello = function hello () { return ‘Hello, my name is ‘ + this.name; } var george = new Greeter(‘George’); var msg = george.hello(); console.log(msg); // Hello, my name is George
Цепочка прототипов
Каждый раз, когда вы пытаетесь получить доступ к свойству объекта, сначала проверяются собственные свойства объекта. Если он не найден, то проверяется [[Prototype]], и поэтому проверка идет вверх по цепочке прототипов, пока не вернется к Object.prototype, который является корневым делегатом для большинства объектов.
Вот пример на Codepen, чтобы объяснить это дальше.
Итак, это была краткая информация о наследовании прототипов в JS, а теперь давайте вернемся к исходному вопросу о наследовании классов и прототипов.
Вот пример на Codepen, чтобы объяснить разницу между ними.
В приведенном выше примере следует отметить следующие моменты:
- Когда мы вызываем «new Point (3, 4)», по сути, мы создаем новый объект, который наследуется от Point.prototype, тогда конструктор Point () вызывается в контексте нового объекта. Так создаются и инициализируются свойства нового объекта.
- Когда мы вызываем «new Point3D (3, 4, 5)» для создания нового объекта, мы снова создаем новый объект, который наследуется от Point3D.prototype, который наследуется от объекта Point и вызывает конструктор Point3D () в контексте новый объект.
- Вот чем отличается прототипное наследование: мы напрямую создаем новый объект из существующего объекта, без понятия классов. Мы используем «Object.create» для создания нового объекта. Он принимает объект в качестве параметра, который будет прототипом для нового объекта.
- Обратите внимание, что «p2» и «point_3d» создаются одинаково. Однако мы расширяем point_3d дополнительными свойствами и методами.
- Важно отметить, что в JavaScript нет понятия наследования на основе классов. Мы просто псевдо-реализуем его, используя свойство «prototype» для построения цепочки наследования.
Наследование классов. Экземпляры наследуются от классов, которые являются планом (описанием) объекта. Экземпляры создаются с помощью функций-конструкторов с ключевым словом new.
Прототипное наследование: экземпляры наследуются напрямую от других объектов через прототип. Экземпляры создаются с помощью фабричных функций или Object.create (). Экземпляры могут состоять из множества различных объектов, что обеспечивает легкое выборочное наследование.
TL;DR:
- класс - это план.
- прототип - это экземпляр объекта.
В следующей статье я расскажу о вопросах HTML, CSS и Интернета.