Полное руководство по JavaScript-интервью

JavaScript
Полное руководство по JavaScript-интервью
Оригинальная ссылка:The Definitive JavaScript Handbook for a developer interview

автор:Gustavo Azevedo

Переводчик:Ю Болун

Первоначально перепечатано сfreeCodeCamp on medium, Пожалуйста, укажите источник.

в соответствии сStack OverflowСогласно опросу, с 2014 года JavaScript является самым популярным языком программирования. Неудивительно, что более трети должностей разработчиков требуют некоторого знания JavaScript. Так что, если вы планируете развиваться в ближайшем будущем, вы должны быть немного знакомы с этим очень популярным языком.

Цель этого поста — собрать воедино все концепции, связанные с JavaScript, которые часто упоминаются в технических интервью. С помощью этого руководства вы сможете просмотреть все, что вам нужно знать о JavaScript, в одном месте.

типы и их преобразования

В JavaScript есть 7 встроенных типов:null, undefined , boolean, number, string, objectа такжеsymbol (ES6).

КромеobjectКроме того, эти типы можно отнести кпримитивный тип.

typeof 0              // number
typeof true           // boolean
typeof 'Hello'        // string
typeof Math           // object
typeof null           // object  !!
typeof Symbol('Hi')   // symbol (New ES6) 
  • Нулевой и неопределенный

UndefinedУказывает, что еще не определено. Он используется как значение по умолчанию для представления неинициализированных переменных, непредоставленных параметров функции, отсутствующих свойств объекта. Когда функция не возвращает значение, она также возвращает значение по умолчанию.undefined .

Nullпредставляет нулевое значение. Его можно присвоить переменной для представления «нет значения».

  • неявное преобразование типов

Давайте посмотрим на следующий каштан:

var name = 'Joey';
if (name) {
  console.log(name + " doesn't share food!")  // Joey doesn’t share food!
} 

В этом случае строковая переменнаяnameбудет преобразован вtrue, поэтому мы можем вывести это в командной строке. Итак, как именно мы определяем преобразование этих истинных и ложных значений?

Значение типа Falsy означает, что оно будет преобразовано в логическое значение при приведении.falseзначение .

К значениям ложного типа относятся:"", 0, null, undefined, NaN, false.

Значения, отличные от Falsy, называются истинными значениями, они преобразуются вtrue.

Boolean(null)         // false
Boolean('hello')      // true 
Boolean('0')          // true 
Boolean(' ')          // true 
Boolean([])           // true 
Boolean(function(){}) // true 

Верно. Возможно, вы заметили. Пустые массивы, объекты и логические значения функций также преобразуются вtrue!

  • Строковые и числовые операции

Первое, на что нужно обратить внимание, это+оператор. Это сложный оператор, поскольку он работает как с числовыми операциями, так и с конкатенацией строк.

но,*,/и-Операторы предназначены для числовых операций. Когда эти операторы используются со строками, они приводят строку к значению типа number.

1 + "2" = "12"
"" + 1 + 0 = "10"
"" - 1 + 0 = -1
"-9\n" + 5 = "-9\n5"
"-9\n" - 5 = -14
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
null + 1 = 1
undefined + 1 = NaN 
  • == и ===

Большинство людей понимают, что==только сравнивает значения на равенство, в то время как===Тип также проверяется при сравнении. Однако такая интерпретация на самом деле неверна.

По факту,==результат после приведения сравнения, и===это прямое сравнение.

2 == '2'            // True
2 === '2'           // False
undefined == null   // True
undefined === null  // False 

Принуждение иногда может вызвать некоторую путаницу, давайте взглянем на следующий каштан:

let a = '0';
console.log(Boolean(a)); // True
let b = false;
console.log(Boolean(b)); // False 

Хорошо, основываясь на приведенных выше результатах, сообщите мне результат этого сравнения ниже:

console.log(a == b); //(1) 

Приведенный выше код на самом деле возвращает True, так почему же?

В механике JavaScript, когда вы берете значение другого типа иbooleanПри сравнении типов JavaScript преобразует это логическое значение вnumberТипы сравниваются. (2)

В этот момент проблема становитсяnumberиstringСравнение значений типов. Из того, что мы говорили ранее, JavaScript будетstringпреобразование типа вnumberType снова сравнивает два числа. (3)

Таким образом, окончательное выражение преобразуется в0 == 0, результат, конечно, True.

'0' == false   //(1)
'0' == 0       //(2)
 0  == 0       //(3) 

Если вы хотите получить полное представление о том, как работает это сравнение, вы можете прочитатьДокументация по ES5.

Вот также результат сравнения различных типов значений в JavaScriptСправочная таблица.

Вот несколько примеров специальных сравнений:

false == ""  // true
false == []  // true
false == {}  // false
"" == 0      // true
"" == []     // true
"" == {}     // false
0 == []      // true
0 == {}      // false
0 == null    // false 

назначение и ссылка

Простые значения (или примитивы)null, undefined , boolean, number, string, а такжеsymbol(ES6) присвоение типа.

Составные значенияobject,Включаютarrayа такжеfunctionвключены цитаты.

var a = 2;        // 'a' hold a copy of the value 2.
var b = a;        // 'b' is always a copy of the value in 'a'
b++;
console.log(a);   // 2
console.log(b);   // 3
var c = [1,2,3];
var d = c;        // 'd' is a reference to the shared value
d.push( 4 );      // Mutates the referenced value (object)
console.log(c);   // [1,2,3,4]
console.log(d);   // [1,2,3,4]
/* Compound values are equal by reference */
var e = [1,2,3,4];
console.log(c === d);  // true
console.log(c === e);  // false 

Когда вы хотите сделать копию составного значения, вам нужно сделать это каким-то образом.копировать(отличить здесьПоверхностная копия и глубокая копия). Таким образом, ссылка не указывает на одно и то же значение:

const copy = c.slice()    // 'copy' references to a new value
console.log(c);           // [1,2,3,4]
console.log(copy);        // [1,2,3,4]
console.log(c === copy);  // false 

сфера

Область действия относится к контексту, в котором выполняется код, который определяет область, в которой действуют переменные и функции.

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

локальная областьОтносится, например, к области внутри функции. Доступ к переменным, объявленным в локальной области видимости, возможен только внутри нее.

function outer() {
  let a = 1;
  function inner() {
    let b = 2;
    function innermost() {
      let c = 3;
      console.log(a, b, c);   // 1 2 3
    }
    innermost();
    console.log(a, b);        // 1 2 — 'c' is not defined
  }
  inner();
  console.log(a);             // 1 — 'b' and 'c' are not defined
}
outer(); 

Чтобы упростить понимание, вы можете представить области видимости как двери, расположенные от самой большой к самой маленькой. Самый низкий человек (приведенный выше код 1, 2, 3 указывает на рост) может войти в самую маленькую дверь (внутренняя граница), а также может пройти через самую большую дверь (внешняя граница).

А высокие люди будут застревать при проходе через дверь (то есть переменные, определенные внутри, не будут доступны снаружи).

Переменный подъем

Во время компиляции JavaScript автоматически конвертируетvarиfunctionАкт объявления движения наверх называетсяhoisting.

Объявления функций полностью подняты. Это означает, что вы можете вызвать функцию перед ее объявлением при написании кода:

console.log(toSquare(3));  // 9

function toSquare(n){
  return n*n;
} 

Переменные будут подняты только частично. Только для примераvarОбъявление поднимается, а присваивание — нет.

letа такжеconstЕго также не будут продвигать.

{  /* Original code */
  console.log(i);  // undefined
  var i = 10
  console.log(i);  // 10
}

{  /* Compilation phase */
  var i;
  console.log(i);  // undefined
  i = 10
  console.log(i);  // 10
}
// ES6 let & const
{
  console.log(i);  // ReferenceError: i is not defined
  const i = 10
  console.log(i);  // 10
}
{
  console.log(i);  // ReferenceError: i is not defined
  let i = 10
  console.log(i);  // 10
} 

Функциональные выражения и объявления функций

  • функциональное выражение

Функциональное выражение доступно только после того, как оно было выполнено, оно не поднимается (эквивалентно присвоению функционального выражения переменной).

var sum = function(a, b) {
  return a + b;
} 
  • объявление функции

Объявление функции может быть вызвано произвольно до или после ее определения, потому что в конечном итоге оно будет поднято.

function sum(a, b) {
  return a + b;
}

Метод объявления переменной: var let cost

До ES6 толькоvarСпособ объявления переменных. Доступ к переменным и методам, объявленным внутри функции, возможен только в пределах ее области действия.

некоторые в т.ч.ifилиforДоступ к переменным, объявленным в области блока оператора, можно получить за пределами содержащихся в них фигурных скобок.

Уведомление:Неиспользованныйvar,letилиconstПеременные, объявленные с помощью ключевого слова, автоматически становятся глобальными переменными.

function greeting() {
  console.log(s) // undefined
  if(true) {
    var s = 'Hi';
    undeclaredVar = 'I am automatically created in global scope';
  }
  console.log(s) // 'Hi'
}
console.log(s);  // Error — ReferenceError: s is not defined
greeting();
console.log(undeclaredVar) // 'I am automatically created in global scope' 

В ES6 у нас естьletиconstЭти два новых ключевых слова. Они не поднимаются и могут действовать в области блока:

let g1 = 'global 1'
let g2 = 'global 2'
{   /* Creating a new block scope */
  g1 = 'new global 1'
  let g2 = 'local global 2'
  console.log(g1)   // 'new global 1'
  console.log(g2)   // 'local global 2'
  console.log(g3)   // ReferenceError: g3 is not defined
  let g3 = 'I am not hoisted';
}
console.log(g1)    // 'new global 1'
console.log(g2)    // 'global 2' 

Еще одно распространенное заблуждение состоит в том, чтоconstявляется неизменным. На самом деле его просто нельзя переназначить, но значением, на которое оно указывает, можно манипулировать:

const tryMe = 'initial assignment';
tryMe = 'this has been reassigned';  // TypeError: Assignment to constant variable.
// You cannot reassign but you can change it…
const array = ['Ted', 'is', 'awesome!'];
array[0] = 'Barney';
array[3] = 'Suit up!';
console.log(array);     // [“Barney”, “is”, “awesome!”, “Suit up!”]
const airplane = {};
airplane.wings = 2;
airplane.passengers = 200;
console.log(airplane);   // {passengers: 200, wings: 2} 

Закрытие Закрытие

Замыкание — это комбинация функции и ее лексического окружения. Замыкания позволяют нам получить доступ к переменным внутри функции за пределами ее области видимости.

function sayHi(name){
  var message = `Hi ${name}!`;
  function greeting() {
    console.log(message)
  }
  return greeting
}
var sayHiToJon = sayHi('Jon');
console.log(sayHiToJon)     // ƒ() { console.log(message) }
console.log(sayHiToJon())   // 'Hi Jon!' 

1. Получить переменную во внешней области.

возвращениеgreetingметод имеет доступ к своей внутренней области видимостиmessageПеременная.

2. Доступ к переменным вне области видимости возможен даже после возврата из внешней функции.

sayHiToJonзвонитsayHiвремя методаgreetingцитаты. чтобы мы могли пройтиgreetingдоступ к недоступномуmessageПеременная.

Полезная вещь о замыканиях заключается в том, что мы можем использовать их для реализацииинкапсуляция данных. Есть некоторые идеи, что некоторые данные не должны предоставляться напрямую, давайте возьмем каштан для иллюстрации:

Например, в следующем коде мы можем передать толькоelementaryэкземпляр для доступа и управленияSpringfieldSchoolВнутреннийstaffпеременную, не беспокоясь о том, что она будет изменена в другом месте.

function SpringfieldSchool() {
  let staff = ['Seymour Skinner', 'Edna Krabappel'];
  return {
    getStaff: function() { console.log(staff) },
    addStaff: function(name) { staff.push(name) }
  }
}

let elementary = SpringfieldSchool()
console.log(elementary)        // { getStaff: ƒ, addStaff: ƒ }
console.log(staff)             // ReferenceError: staff is not defined
/* Closure allows access to the staff variable */
elementary.getStaff()          // ["Seymour Skinner", "Edna Krabappel"]
elementary.addStaff('Otto Mann')
elementary.getStaff()          // ["Seymour Skinner", "Edna Krabappel", "Otto Mann"] 

Давайте перейдем к решению одного из самых распространенных вопросов интервью с замыканиями:

Что не так с кодом ниже? Как вывести желаемый результат?

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log(`The value ${arr[i]} is at index: ${i}`);
  }, (i+1) * 1000);
} 

В выводе выполнения этого кода каждый разiоба равны 4. Это потому, чтоsetTimeoutК моменту выполнения метода цикл уже завершился.

Мы можем решить эту проблему, немедленно выполнив функцию IIFE, которая создает отдельную область для хранения каждого входящегоiзначение .

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(`The value ${arr[j]} is at index: ${j}`);
    }, j * 1000);
  })(i)
} 

Другой более краткий ответ - использоватьletключевые слова для объявления переменныхi, вы можете получить тот же результат:

const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log(`The value ${arr[i]} is at index: ${i}`);
  }, (i) * 1000);
} 

Немедленно выполнить функциональное выражение IIFE

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

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

Закрывающие скобки указывают на вызов функции.

var result = [];
for (var i=0; i < 5; i++) {
  result.push( function() { return i } );
}
console.log( result[1]() ); // 5
console.log( result[3]() ); // 5
result = [];
for (var i=0; i < 5; i++) {
  (function () {
    var j = i; // copy current value of i
    result.push( function() { return j } );
  })();
}
console.log( result[1]() ); // 1
console.log( result[3]() ); // 3 

С помощью IIFE вы можете:

  • Позволяет добавлять личные данные в метод
  • Создайте новую ролевую среду
  • Предотвращение загрязнения глобального пространства имен

контекст

Понятие контекста часто путают с масштабом. Чтобы оставаться организованным, нам нужно обратить внимание на две вещи:

  • Контекст определяется при вызове функции. Обычно это относится к значению в определенной части вашего кода.
  • Значение области видимости — это область, в которой можно получить доступ к переменной.
// 作用域
var param = 1;      // global scope
function myScope(){
    var param = 2;     // local scope
}
// 上下文
this.prop = 1;      // global context
function myContext(){
    this.prop = 2;   // local context
}
var myInstance = new myContext(); 

Например, в приведенном выше примере вызовthisразные локации,thisУказатели тоже разные, что означает разные контексты, а область видимости — это то, что мы используем при написании кода.varключевое слово для определения.

Метод вызова функции: call apply bind

Эти три метода могут измениться при вызове функцииthisРазница в том, когда вызывается функция.

  • .call()Функция вызывается немедленно и требует, чтобы вы передавали аргументы один за другим по порядку.
  • .apply()Функция также вызывается сразу, но аргументы нужно передавать в виде массива.
  • .call()и.apply()Утилита практически одинаковая, все они могут быть использованы для вызова метода в объекте, как использовать зависит от того, как удобнее передавать параметры в вашем сценарии использования.

Пример приложения:

const Snow = {surename: 'Snow'}
const char = {
  surename: 'Stark',
  knows: function(arg, name) {
    console.log(`You know ${arg}, ${name} ${this.surename}`);
  }
}
char.knows('something', 'Bran');              // You know something, Bran Stark
char.knows.call(Snow, 'nothing', 'Jon');      // You know nothing, Jon Snow
char.knows.apply(Snow, ['nothing', 'Jon']);   // You know nothing, Jon Snow 

Уведомление:если вы используете.call()Когда передается параметр в виде массива, он будет использовать весь массив в качестве параметра.

Однако в ES6 можно попробовать метод расширения оператора для передачи параметров:

char.knows.call(Snow, ...["nothing", "Jon"]);  // You know nothing, Jon Snow 

.bind()Вместо прямого запуска метода он возвращает новый метод на основе параметров и контекста, которые вы передаете. Используйте, когда вы хотите вызвать метод в некотором контексте позже в программе..bind()Сюда.

Это очень полезно, когда мы используем некоторые асинхронные операции или обработчики событий..bind()Форма параметра аналогична.call()Вам нужно передать аргументы через запятую:

const Snow = {surename: 'Snow'}
const char = {
  surename: 'Stark',
  knows: function(arg, name) {
    console.log(`You know ${arg}, ${name} ${this.surename}`);}
  }
const whoKnowsNothing = char.knows.bind(Snow, 'nothing');
whoKnowsNothing('Jon');  // You know nothing, Jon Snow 

это ключевое слово

в JavaScriptthisЭто сложнее понять.

thisНа что он указывает, определяется контекстом выполнения функции (на самом деле в предыдущем разделе уже много каштанов).

ключевые словаthisПохоже на заполнитель. Он будет указывать на объект, который конкретно вызывает метод.

Следующий контрольный список используется для определенияthisПравила оформления заказа:

  • new bindingкогда используешьnewКогда ключевое слово вызывает функцию,thisбудет привязан к вновь созданному объекту.
function Person(name, age) {
  this.name = name;
  this.age =age;
  console.log(this);
}
const Rachel = new Person('Rachel', 30);   // { age: 30, name: 'Rachel' } 
  • явная привязкакогда используешьcallилиapplyКогда метод вызывает функцию,thisукажет на объект, который мы передали.bindКак мы видели ранее, возвращается новая функция, а не прямой вызов.
function fn() {
  console.log(this);
}
var agent = {id: '007'};
fn.call(agent);    // { id: '007' }
fn.apply(agent);   // { id: '007' }
var boundFn = fn.bind(agent);
boundFn();         // { id: '007' } 
  • неявное связываниеКогда мы вызываем функцию в определенном контексте,thisбудет указывать на объект, которому принадлежит эта функция.
var building = {
  floors: 5,
  printThis: function() {
    console.log(this);
  }
}
building.printThis();  // { floors: 5, printThis: function() {…} } 
  • привязка по умолчаниюЕсли функция не подчиняется всем вышеперечисленным правилам при вызове, тоthisбудет привязан к глобальному объекту (в браузере этот глобальный объектwindow).

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

function printWindow() {
  console.log(this)
}
printWindow();  // window object 

Уведомление:Даже в другой области видимости вызов такой независимой функции не меняет ееthisуказывает на:

function Dinosaur(name) {
  this.name = name;
  var self = this;
  inner();
  function inner() {
    alert(this);        // window object — the function has overwritten the 'this' context
    console.log(self);  // {name: 'Dino'} — referencing the stored value from the outer context
  }
}
var myDinosaur = new Dinosaur('Dino'); 
  • привязка синтаксисакогда вы берете=>Вызов метода в виде стрелочной функции эквивалентен передаче в текущем контексте выполнения.thisценность.
function Cat(name) {
  this.name = name;
  console.log(this);   // { name: 'Garfield' }
  ( () => console.log(this) )();   // { name: 'Garfield' }
}
var myCat = new Cat('Garfield'); 

строгий режим

Мы можем использовать"use strict"Директива для включения строгого режима для JavaScript. Это добавит больше ограничений и обработки ошибок в ваш код.

Преимущества использования строгого режима:

  • Легче отлаживатьВы можете увидеть больше ошибок, например, когда вы пытаетесь присвоить значение глобальному объекту или свойству, доступному только для чтения.
  • Предотвращение случайного создания глобальных переменныхСообщается об ошибке при присвоении значения необъявленной переменной.
  • Недопустимые операции удаления запрещеныВылетают ошибки при попытке удалить переменные, функции, неудаляемые свойства.
  • Повторяющиеся имена свойств и параметров запрещены.Если есть повторяющиеся имена свойств или значения параметров, будет сообщено об ошибке.
  • Сделайте вызовы eval() более безопаснымисуществуетeval()Переменные и функции, определенные внутри методов, не загрязняют другие области видимости.
  • запретить этому указывать на глобальный объекткогдаthisЗначение null или undefined больше не будет использоваться по умолчанию для глобального объекта. Это означает, что внутри функцииthisбольше не будет по умолчаниюwindowобъект.

новое ключевое слово

ключевые словаnewэто особый способ вызова функции, который передаетсяnewФункция, называемая ключевым словом, называетсяКонструктор.

такnewКакие действия были предприняты?

  1. Создается новый объект.
  2. Прототип нового объекта наследуется от прототипа конструктора.
  3. с новым объектомthisВыполнить конструктор.
  4. Вернуть новый объект. Если конструктор возвращает объект, этот объект заменяет весьnewрезультаты.
// In order to better understand what happens under the hood, lets build the new keyword 
function myNew(constructor) {
  var obj = {}
  Object.setPrototypeOf(obj, constructor.prototype);
  return constructor.apply(obj, [...arguments].slice(1)) || obj
} 

затем при вызове функции используйтеnewВ чем разница между ключевыми словами или нет?

function Bird() {
  this.wings = 2;
}
/* invoking as a normal function */
let fakeBird = Bird();
console.log(fakeBird);    // undefined
/* invoking as a constructor function */
let realBird= new Bird();
console.log(realBird)     // { wings: 2 } 

Прототип и наследование

Прототипы — одна из самых запутанных концепций в JavaScript. Одна из причин заключается в том, чтопрототипСлово используется в двух контекстах:

  • Отношения прототипа

Каждый объект имеет свой собственный объект-прототип и наследует все свойства своего прототипа.

ты можешь пройти.__proto__Этот нестандартный механизм получения прототипа объекта (в ES6, в ES5 стандарт также черезObject.getPrototypeOf()способ получения).

Обычные объекты также наследуют.constructorуказывает на его конструктор. Когда вы используете конструктор для создания объекта, его.__proto__Свойство будет указывать на конструктор.prototypeАтрибуты.

  • Свойства прототипа

Каждая определенная функция имеет функцию с именем.prototypeхарактеристики.

Это объект, который наследует все свойства в цепочке прототипов. Этот объект также включает по умолчанию.constructorсвойство, указывающее на исходный конструктор.

Все объекты, созданные с помощью конструктора, также наследуют указатель на эту функцию..constructorСвойства (Легче понять, набрав объект в консоли, см. пример ниже).

function Dog(breed, name){
  this.breed = breed,
  this.name = name
}
Dog.prototype.describe = function() {
  console.log(`${this.name} is a ${this.breed}`)
}
const rusty = new Dog('Beagle', 'Rusty');

/* .prototype property points to an object which has constructor and attached 
properties to be inherited by objects created by this constructor. */
console.log(Dog.prototype)  // { describe: ƒ , constructor: ƒ }

/* Object created from Dog constructor function */
console.log(rusty)   //  { breed: "Beagle", name: "Rusty" }
/* Object inherited properties from constructor function's prototype */
console.log(rusty.describe())   // "Rusty is a Beagle"
/* .__proto__ property points to the .prototype property of the constructor function */ 
console.log(rusty.__proto__)    // { describe: ƒ , constructor: ƒ }
/* .constructor property points to the constructor of the object */
console.log(rusty.constructor)  // ƒ Dog(breed, name) { ... }

Сеть прототипов

Цепочка прототипов описывает отношения между объектами, ссылающимися друг на друга.

При получении свойств объекта механизм JavaScript начинает просматривать сам объект. Если он не найден, он будет обращаться к свойству своего прототипа, пока не будет найден в первый раз. Последний объект в цепочке прототипов — встроенныйObject.prototypeИ его прототипnull(то есть конец так называемой цепочки прототипов). Движок JavaScript вернется, когда найдет свойство на этом уровне и не найдет его.undefined.

Собственное и унаследованное имущество

Существует два типа свойств объекта: собственные и унаследованные.

Собственные свойства — это свойства, определенные внутри объекта.

Унаследованные свойства — это свойства, полученные через цепочку прототипов. Унаследованные свойства не являются перечислимыми (т.for/inв цикле не видно).

function Car() { }
Car.prototype.wheels = 4;
Car.prototype.airbags = 1;

var myCar = new Car();
myCar.color = 'black';

/*  Check for Property including Prototype Chain:  */
console.log('airbags' in myCar)  // true
console.log(myCar.wheels)        // 4
console.log(myCar.year)          // undefined

/*  Check for Own Property:  */
console.log(myCar.hasOwnProperty('airbags'))  // false — Inherited
console.log(myCar.hasOwnProperty('color'))    // true

Object.create(obj)Методы могут указывать прототипы для создания объектов:

var dog = { legs: 4 };
var myDog = Object.create(dog);

console.log(myDog.hasOwnProperty('legs'))  // false
console.log(myDog.legs)                    // 4
console.log(myDog.__proto__ === dog)       // true 

наследование по ссылке

Унаследованное свойство — это ссылочная копия свойства объекта-прототипа.

Если свойство прототипа объекта изменено, объекты, наследующие это свойство, будут затронуты таким же образом. Но если свойство будет полностью заменено, это не повлияет на унаследованный объект:

var objProt = { text: 'original' };
var objAttachedToProt = Object.create(objProt);
console.log(objAttachedToProt.text)   // original

objProt.text = 'prototype property changed';
console.log(objAttachedToProt.text)   // prototype property changed

objProt = { text: 'replacing property' };
console.log(objAttachedToProt.text)   // prototype property changed 

Наследование классов и прототипное наследование

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

Но у шаблона наследования классов есть следующие недостатки:

  • жесткая иерархия
  • сильно связанный
  • Слабый базовый класс
  • дублирование кода
  • И легендарный вопрос о горилле/банане: «Ты хотел банан, а получил гориллу, держащую банан, и целые джунгли».

В прототипном шаблоне наследования объект может напрямую наследовать другой объект. мы можем пройтиObject.create()Или другие методы объекта и фабричные функции для создания объектов.

Ниже приведены три различных формы прототипного наследования:

  • Агент-прототипМы обычно называем те объекты, которые используются для предоставления моделей другим объектам, как прокси-прототипы. При наследовании от прокси-прототипа новый объект может получить ссылку на свойства прокси-прототипа. Эта операция обычно используетсяObject.create()Что нужно сделать.
  • наследование сплайсингаНаследование путем копирования свойств объекта без создания ссылочной связи, эта операция обычно используетObject.assign()продолжать.
  • функциональное наследованиеЭтот метод осуществляется через заводскую функцию (заводская функция означает не черезnewключевое слово create и вернуть объектную функцию), чтобы создать объект, а затем добавить новые свойства непосредственно к объекту. Преимущество этой формы в том, что мы можем использовать замыкания для инкапсуляции данных.
const person = function(name) {
  const message = `Hello! My name is ${name}`;
  return { greeting: () => console.log(message) }
}
const will = person("Will");
will.greeting();     // Hello! My name is Will 

состав и наследование

Разработчики согласны с тем, что в большинстве случаев следует избегать наследования классов. Потому что в этом режиме вам нужно очень четко определить, что они из себя представляют, разработав свой код.

Шаблоны композиции, с другой стороны, позволяют вам определить, что ваш код будет делать, повышая гибкость и возможность повторного использования вашего кода (Реагировать компонентыочень хороший каштан).

JavaScript асинхронный

JavaScript — это однопоточный язык программирования. Это означает, что движок JavaScript может обрабатывать только один фрагмент кода за раз. Один из его основных эффектов заключается в том, что когда JavaScript сталкивается с фрагментом кода, который требует много времени для обработки, он блокирует выполнение последующего кода.

JavaScript использует структуру данных, называемуюстек вызовов. Вы можете думать о стеке вызовов как о стопке книг. Каждая книга, которую вы положите, будет находиться поверх предыдущей стопки книг. Книги, положенные последними, будут взяты первыми, а книги, положенные первыми, будут взяты последними (LIFO).

Метод, используемый для решения проблемы неблокирующего выполнения больших блоков кода, называется асинхронной функцией обратного вызова. Такие функции могут быть отложены.

Асинхронный процесс начинается с асинхронной функции обратного вызова, которая размещается в куче или в памяти. Вы можете думать о куче какменеджер по корпоративным мероприятиям. Стек вызовов имеет доступ к диспетчеру событий для выполнения определенных функций только при возникновении события. Когда происходит событие, менеджер событий перемещает функцию обратно в очередь вызовов.Уведомление:Когда диспетчер событий обрабатывает функцию, последующий код не блокируется и может продолжать выполняться.

Стек вызовов постоянно проверяет, свободен ли он. Когда он бездействует, очередь вызовов проверяется на наличие функции, ожидающей вызова. Когда есть функция, ожидающая выполнения, первая функция в очереди помещается в стек вызовов и выполняется. Этот процесс называется циклом выполнения внутри цикла событий.

Давайте использовать каштан, чтобы объяснить:

const first = function () {
  console.log('First message')
}
const second = function () {
  console.log('Second message')
}
const third = function() {
  console.log('Third message')
}

first();
setTimeout(second, 0);
third();

// Output:
  // First message
  // Third message
  // Second message 
  1. Инициализируйте консоль браузера, очистите стек и менеджер событий.
  2. first()Присоединяйтесь к стеку вызовов.
  3. console.log("First message")Присоединяйтесь к стеку вызовов.
  4. console.log("First message")выполняется и печатает «Первое сообщение» в консоли браузера.
  5. console.log("First message")удален из стека вызовов.
  6. first()удален из стека вызовов.
  7. setTimeout(second, 0)Присоединяйтесь к стеку вызовов.
  8. setTimeout(second, 0)выполняется и управляется менеджером событий. Через 0 мс менеджер событийsecond()Перейдите в очередь звонков.
  9. setTimeout(second, 0)Завершает выполнение и удаляется из стека вызовов.
  10. third()Присоединяйтесь к стеку вызовов.
  11. console.log("Third message")Присоединяйтесь к стеку вызовов.
  12. console.log("Third message")выполняется и печатает «Третье сообщение» в консоли браузера.
  13. console.log("Third message")удален из стека вызовов.
  14. third()удален из стека вызовов.
  15. Стек вызовов свободен,second()Ожидание выполнения в очереди вызовов.
  16. Менеджер мероприятия будетsecond()Переход из очереди вызовов в стек вызовов.
  17. console.log("Second message")Присоединяйтесь к стеку вызовов.
  18. console.log("Second message")выполняется и печатает «Второе сообщение» в консоли браузера.
  19. console.log("Second message")удален из стека вызовов.
  20. second()удален из стека вызовов.

Уведомление:ты дляsetTimeoutПараметр времени, переданный методом, неsecond()Время задержки выполнения, но время ожидания диспетчера событий для перемещения функции в стек вызовов.

Очень тяжело дочитать до конца. Добро пожаловать для обмена и обсуждения в области комментариев.