Давайте начнем новый год с понимания основных концепций того, как 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.
Если вы нашли эту статью полезной, это будет много значить, если вы поаплодируете ей и поделитесь ею, чтобы помочь другим найти ее! И не стесняйтесь оставлять комментарии ниже.