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

Концепция парсера JavaScript:

Каждый раз, когда мы пишем какой-либо фрагмент кода javaScript, например var myName = «John», он преобразуется в токены. Токен - это не что иное, как строковое представление вашего кода, поэтому ваш код будет преобразован в «var», «myName» ‘=’, «John». Поэтому, если этот синтаксис совпадает с грамматикой javaScript, которая является определенной парадигмой механизма javaScript, тогда javaScript компилирует его, чтобы он стал действительным синтаксисом javaScript.

Если вы напишете что-то вроде Integer m = 10, которое в конечном итоге будет 'Integer', 'm', '=', '10', тогда javaScript сообщит нам, что синтаксис недействителен, потому что определение типа 'Integer' не существуют в словаре javaScript.

AST (абстрактное синтаксическое дерево):

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

Давайте возьмем для примера то, что мы определили ранее.

var myName = “John”

Вот если мы вставим этот код в проводник AST https://astexplorer.net/, то получим что-то вроде этого:

Давайте попробуем разобраться в древовидной структуре, чтобы получить четкое представление о том, как синтаксический анализатор анализирует синтаксис javaScript. Как видно из приведенной выше диаграммы, синтаксический анализатор возвращает нам большой объект с такими свойствами, как ‘start’, ‘end’, ‘body’,, ’sourceType’. Свойства 'Start' и 'End' - не что иное, как обозначение того, где начинается и заканчивается индекс первого символа, и это длина синтаксиса. Здесь он начинается с 0 и заканчивается на 19. «Body» - это массив объекта, который обозначает тип объявления и идентификатор, которым в данном случае является «myName», а также сообщает нам значение, которое содержится в «value». ». Это var, который может быть любым типом данных.

Области применения парсера JavaScript:

Когда парсеры JavaScript начинают синтаксический анализ, они также охватывают переменные. Что касается приведенного выше примера.

let globalVariable = 10;
function abc(){
  let localVariable = 5;
  console.log("Variable is " + localVariable);
}

Если мы запустим указанную выше функцию, синтаксический анализатор будет использовать переменные globalVariable и localVariable. функция напечатает 5, так как переменная localVariable сначала попытается найти свое объявление, и она найдет 5, так как ее область заблокирована.

А если взять пример ниже.

let globalVariable = 20;
function foo(){
  let localVariable = 15;
  console.log("variable is " + localVariable);
  console.log("variable is " + globalVariable);
}
console.log("variable is " + localVariable);
console.log("variable is " + globalVariable);

В приведенном выше примере, когда функция foo выполняется, она напечатает 15, так как сначала она найдет свое объявление вверх по цепочке, а globalVariable определена глобально, поэтому при перемещении вверх она найдет свое объявление.

Но console.log («переменная» + localVariable); выведет undefined, поскольку не найдет объявление localVariable в своем контексте.

Техника Pre-Parser для оптимизации производительности:

Движок JavaScript V8 имеет специальный тип алгоритма синтаксического анализа, который называется предварительным анализатором, который в два раза быстрее, чем полный синтаксический анализ, поскольку он анализирует только тот код, который необходимо проанализировать во время выполнения или запуска, и только определяет область действия оператора. Парсеры JavaScript анализируют код, создают AST, переменные области видимости и находят ошибки. Когда мы определяем функции в нашем контексте выполнения, они занимают некоторую память и ждут вызова функции. Но в некоторых случаях мы определяем функции, которые не вызываем сразу, и мы можем вызывать их позже, но их анализ задерживается. наша память.

Решение:

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

function avenger1() {
  console.log("I am avenger 1")
}
function avenger2() {
  console.log("I am avenger 2")
}
avenger1();

В приведенном выше примере мы вызвали avenger1, тогда как avenger2 определен, но не вызывается в данный момент и может быть вызван позже. Таким образом, javaScript полностью проанализирует обе функции при загрузке и останется в своей памяти, даже если avenger2 еще не вызван. Это создает избыточное выделение памяти, и если вы создаете проект, в котором объявляете 20–30 таких методов, которые вы не вызываете немедленно, это вызовет серьезную задержку в ваших браузерах, если ваше приложение масштабируется, и вы можете повысить производительность, указав синтаксический анализатор javaScript не должен анализировать функцию в этот момент.

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

(function abc(){
 console.log("I am fully parsed or eagerly parsed");
  function xyz() {
    console.log("I am lazily parsed");
  }
})();

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

Проблема с синтаксическим анализом вложенных функций:

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

Взяв ниже пример, чтобы понять более глубоко.

(function abc(){
  let x = 1;
  let y = 2;
  function xyz(){
    return x;
  }
return xyz;
})

В приведенном выше примере функция abc анализируется полностью, поскольку она заключена в круглые скобки, а внутренняя функция xyz анализируется частично, поскольку она определена, но еще не вызвана.

функция «xyz» возвращает значение x, равное 1, поскольку она имеет доступ к переменной своей родительской функции. Поэтому, когда функция «xyz» разбирается, она ищет переменную x в своей области видимости, а поскольку не находит ее, ищет ее до родительской области. Здесь переменная x сохраняется в контексте функции abc и сохраняется до вызова функции «xyz».

Когда мы вызываем внутреннюю функцию «xyz», она не находит x в своем контексте, поэтому ищет своего родителя, а поиск контекста в контексте функции обходится дороже, чем поиск в стеке вызовов.

Спасибо за прочтение! Если у вас есть какие-либо вопросы, обращайтесь по адресу [email protected], свяжитесь со мной в LinkedIn или подпишитесь на меня в Medium и Twitter.

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