Мы поговорили с Вячеславом Егоровым aka mraleph, инженером из Google, компиляторщиком до мозга костей, который работал над JavaScript движком под названием V8, встроенным в Chromium (и, как следствие в Chrome, Android версию браузера, облачную операционную систему Chrome OS) и в менее известный Maxthone.
JavaScript-программистам Вячеслав, скорее всего, известен как автор постов про внутренности V8 и как докладчик, увлеченно показывающий машинный код на конференциях для Web-разработчиков.
В настоящее время Вячеслав активно работает в Google над Dart VM.
В этом интервью он рассказал о том, что происходит внутри движка, выполняющего динамический JS-код и поделился примерами, как выполняются некоторые оптимизации и почему важно глубоко понимать работу движка, чтобы обеспечить быстрое выполнение кода.
Движок V8 был разработан датским подразделением компании Google и распространяется по лицензии BSD. Движок написан на C++ и поддерживает спецификацию ECMA-262.
Первая версия движка появилась в 2008 году. На текущий момент активная разработка движка продолжается по большей части в мюнхенском офисе Google.
Среди основных особенностей движка — компиляция javascript непосредственно в машинный код, адаптивная оптимизация и деоптимизация кода во время компиляции, быстрый сбор мусора.
— Расскажите, пожалуйста, в двух словах о себе и о работе в Google, связанной с JavaScript-движком V8?
— Закончил мехмат НГУ. Всегда интересовался компиляторами. Сначала работал в Новосибирской компании Excelsior, где люди делают свою собственную JVM с AOT-компилятором, потом ушел в Google. В Google сначала занимался V8, потом Dart VM, какое-то время даже починял различные баги в LuaJIT.
— Многие предпочитают машину V8 за её способность к оптимизации кода. В чём секрет?
— Основная сложность при оптимизации динамических языков программирования состоит в том, что для достижения пиковой производительности виртуальная машина должна, по сути дела, угадать намерение программиста и увидеть статическую структуру, скрытую в динамическом коде. Сильной чертой V8 является то, что она умеет достаточно хорошо эту самую структуру замечать.
К примеру, кто-то написал на JavaScript:
function len(p) {
return Math.sqrt(p.x * p.x + p.y * p.y);
}
и оказывается, что V8 вполне способен скомпилировать тело этой функции в достаточно компактный машинный код:
vmovsd xmm1,[rax+0x17]
vmulsd xmm1,xmm1,xmm1
vmovsd xmm2,[rax+0x1f]
vmulsd xmm2,xmm2,xmm2
vaddsd xmm1,xmm2,xmm1
vsqrtsd xmm1,xmm1,xmm1
Это совсем нетривиальная задача в условиях, когда все динамически типизировано и статически совершенно непонятно, что такое
p.x
или даже
Math.sqrt
.— А что касается слабых сторон движка?
— Иногда сильная стороны V8 становится её слабой стороной. Происходит это по двум причинам.
Во-первых, не в любом динамически типизированном коде V8 способна увидеть статическую структуру, которая, возможно, и была очевидна написавшему этот код программисту. Где-то это происходит, потому что в V8 что-то ещё не реализовано, где-то — потому что алгоритмически не всегда возможно.
Один из часто встречающихся примеров — это код в стиле:
function make(x) {
return { f: function () { return x } }
}
var a = make(0), b = make(0);
Здесь V8 не умеет замечать, что a.f и b.f имеют одно и тоже поведение (с поправкой на значение захваченных переменных), т.к. это функции, созданные из одного и того же функционального литерала (function literal).
Во-вторых, иногда оптимизирующий компилятор V8 просто не поддерживает какую-то конструкцию в коде, и поэтому компилятор отказывается смотреть на код. Например, Crankshaft (это первая реализация идеи адаптивной компиляции в V8) никогда не поддерживал try-catch/finally и поэтому отказывался оптимизировать функции, где он присутствовал. Сейчас функции, которые используют try-catch, компилируются через TurboFan (компилятор, разрабатываемый на замену Crankshaft), поэтому ситуация выправляется.
В-третьих, иногда издержки, затрачиваемые на выявление статической структуры, не окупаются. V8 тратит время, строит деревья скрытых классов, оптимизирует/ деоптимизирует/ оптимизирует снова код — а код лучше не становится, например, потому что объекты по большей части используются как словари. Это очень интересная проблемная область — как правильно балансировать издержки на оптимизацию кода и улучшение производительности от этой оптимизации. Как исполнять код с разумной скоростью, даже если этот код не попадает на очевидный fast path.
Работы над всеми этими проблемами ведутся, а потому в любых случаях, когда ваш код работает медленно на V8, следует жаловаться разработчикам V8 — хотя бы для того, чтобы они объяснили, почему код работает медленно или может быть даже починили что-то в самой V8.
Кстати, всё сказанное выше в той или иной степени относится к любому JavaScript движку, который хоть как-то старается оптимизировать ваш код. Все движки стараются угадать, что же программист хотел сказать своей программой, и стараются углядеть статическое в динамическом. У всех движков есть разделение на fast path и slow path и свои собственные особенности.
— Как вы считаете, имеет ли смысл оптимизация кода под определенный движок (в нашем случае — V8)? Ведь бывает так, что в каком-то браузере код тормозит. Что тогда делать?
— Здесь возможно два варианта (или их комбинация):
- ваш код «плохой». В этом случае обычно страдает производительность сразу под несколькими движками, и оптимизация под один движок улучшает производительность кода сразу под несколькими;
- код движка «плохой». В этом случае, как я отметил ранее, надо отсылать баг репорт разработчикам движка, которые могут либо починить сам движок, либо часто рекомендовать способ обойти этот баг.
— Можете ли вы перечислить наиболее часто встречающиеся «грабли», на которые наступают разработчики в своем коде (т.е., по сути, наиболее часто встречающиеся ошибки в коде, сильно влияющие на производительность при ориентации на V8 — в рамках упомянутой выше ситуации «код плохой»)?
— Бывает два типа грабель. Первый тип — грабли алгоритмические. Например, иногда люди итерируют по строке, используя цикл в стиле:
for (; s != ""; s = s.substring(1))
Многие компиляторщики начинают слегка подпрыгивать на стуле и потирать руки, когда видят подобный код, поскольку реализовать оптимизацию, которая бы такой цикл превращала бы во что-то вменяемое — это жутко интересная задача. Однако с точки зрения разработчика гораздо эффективнее понимать, сколько стоит s.substring в лучшем и худшем случае, и такого кода не писать. Потому что без хитрых оптимизаций внутри движка этот цикл имеет временную сложность O(n2).
Другой тип грабель — это грабли, связанные с оптимизацией динамических языков (я уже это затронул выше). Например, полиморфный код, т.е. код, который работает с разными типами объектов. Такой код для V8 часто как криптонит (кристаллическое радиоактивное вещество, фигурирующее во вселенной DC Comics. Криптонит знаменит благодаря тому, что является единственной немагической слабостью Супермена и других криптонцев — он способен оказывать на них воздействие, которое разнится в зависимости от цвета минерала. — прим. ред.) для Супермена. Тема это достаточно глубокая, и у меня на эту тему есть целый пост с картинками.
— Как же бороться с этими проблемами? Искать «правила написания кода под определенный движок» и проверять свой код на соответствие им?
— Здесь самое главное — осознать, что проблемы с производительностью нельзя решить наскоком. Допустим, вы услышали про полиморфизм и побежали, теряя тапки, переписывать весь свой код в мономорфном стиле. Ничего хорошего из таких начинаний обычно не получается. Здесь нужен совершенно другой подход, который очень хорошо описывается классической русской поговоркой «семь раз отмерь, один раз отрежь». Нужно выстроить в своей голове ментальную модель того, как работает VM и того, как работает ваш код, нужно хорошенько попрофилировать, понять куда у вас утекает производительность, и уже потом задаваться вопросами оптимизации. Часто оказывается, что V8 делает свою работу хорошо, а настоящее бутылочное горлышко совсем даже не в javascript коде.
— А если вернуться к упомянутым вами проблемами кода компилятора (ситуации «код движка плохой»)? Можно ли привести какие-то наиболее часто отмечаемые проблемы V8?
— Баги обычно не проявляются «часто» или «не часто». Обычно бывает, что баг заметил какой-нибудь разработчик, его быстренько починили, и на этом все закончилось. Баги при этом задевают только малую популяцию разработчиков, потому что встречаются в совершенно особенных местах при правильном стечении обстоятельств.
Как пример из практики: меня однажды попросили посмотреть на код, который почему-то иногда начинал работать очень медленно. Оказалось все из-за выражения Math.floor(x * y), в котором иногда x становился равным -1, а y равным 0. Казалось бы ничего особенного, но Math.floor(x * y) в таком случае равняется волшебному числу «отрицательный ноль» (который почти совсем как 0, но если поделить, скажем, 1 на ноль, то получается положительная бесконечность, а если поделить 1 на минус ноль, то получается отрицательная бесконечность). Crankshaft-же всегда предполагал, что результат операции floor — это число целое, и потому -0 — число не представимое в виде целого, вызывало деоптимизацию. Решением проблемы в данном конкретном случае было заменить Math.floor(x * y) на (x * y) | 0 (вообще, это не эквивалентное преобразование, но для кода, который нужно было разогнать, не играло роли). Кстати, недавно эту проблему в Crankshaft починили раз и навсегда.
Я специально выбрал этот баг в качестве примера, т.к. он достаточно загадочный («что за минус-ноль?», «что за деоптимизация?») в надежде убедить читателя в том, что знание о конкретных багах совершенно бесполезно. Я обнаружил, что код, на который я смотрел, наступает на этот баг не потому, что я знал, что Math.floor не терпит -0, и вооруженный этим знанием пошел заменять все Math.floor на |0, пока баг сам не исправился… Нет, я нашел этот баг, поскольку знал, как профилировать V8, с какими ключами надо ее запускать, чтобы V8 мне показала список деоптимизаций. Поэтому важнее всего понимать, как V8 (и другие JS VM) работают.
Обладая этим знанием, можно всегда выяснить, «что же пошло не так где-то в глубоких подземельях», и потом пожаловаться в вышестоящие инстанции.
Я об этом достаточно много пишу в своем блоге и даже сделал тулзу, которая позволяет смотреть информацию, выдаваемую V8 (compiler IR, deopts, etc), в более-менее удобной форме.
— Вы же сейчас занимаетесь Dart? Когда же этот новый язык заменит JS? Нужно ли уже сейчас разработчику переходить на новый язык? Какое его положение в индустрии?
— Взять и вот просто так заменить JS во всей нашей вселенной нельзя. А вот на отдельных рабочих местах — вполне можно. Люди заменяют его разными вещами, кто-то ClojureScript, кто-то TypeScript, а кто-то Dart. Языки программирования — это штука сложная и конфликтная. Все имеют на их счет свое мнение. Поэтому мой совет обычно простой — устали от JavaScript? Можно сходить на dartlang.org, скачать SDK (или поиграться с языком прямо в браузере) и решить для самого себя.
Из интересных вещей, которые сейчас происходят с языком за пределами Web, можно выделить flutter.io — это фреймворк для разработки кроссплатформенных (Android & iOS) мобильных приложений и dartino — маленький Dart для встраиваемых систем.
Благодарим за беседу!
Примеры, приведенные выше, наглядно иллюстрируют, что одного лишь перечня «простых рекомендаций под конкретный движок» для оптимизации кода не достаточно — важно понимать принцип его работы, а заодно и четко представлять, каких результатов хочется добиться от программы.
5 июня в Питере Вячеслав выступит на JavaScript-конференции HolyJS c докладом про производительность JavaScript. Приходите!
Комментарии (34)
AlexeyFrolov
19.05.2016 01:12а существует какой-нибудь аналог EXPLAIN что бы посмотреть, какие оптимизации применились к коду?
mraleph
19.05.2016 01:49Для разных машин существуют разные флаги — чтобы посмотреть на сгенерированный код. Тул IRHydra, который я написал, позволяет в более-менее удобоваримом виде изучать информацию, которую умеет дампить V8-овый Crankshaft. Что-то мне подсказывает, что и для других машин есть подобные способы, но разрабочики их не афишируют :)
csmile
19.05.2016 05:21А какой смысл в таких глубоких оптимизациях в JS? Ну понятно что какие-нибудь базовые и дешевые jump-to-jump или peephole делать можно и должно, но трансляция JS в native code представляется overkill. Вычиcлительная модель и типы данных как-то совсем далеки от модели и типов native code.
Не знаю как кому, но мне вот представляется что гораздо продуктивнее было бы иметь возможность сделать что-то типа inline C или Dart islands в JS:
"C" int ray_tracer(int a) {
... C code, compiled into native assembly ...
}
// normal JS
var r = ray_tracer(42);
Как-то представляется что это позволит получить более предсказуемые результаты. И уж точно исполняться будет эффективнее.
Да и сократить размер всего этого хозяйства опять же. А то вот получается что весь мой Sciter который есть фактически встраиваемый browser включающий JS superset по размеру меньше чем одна V8.dll ( sciter32.dll — 3.7mb, V8.dll — 4.7mb ).
mraleph
19.05.2016 13:59Смысл в глубоких оптимизациях JS очень простой — люди пишут JS-код, надо чтобы он исполнялся эффективно.
Если бы люди JS не писали, то его никто бы и не оптимизировал.
А так выбора нет :)
csmile
19.05.2016 23:22В JS скорость парсинга одинаково важна как и скорость исполнения. Скорость eval() опять же. Т.е. баланс — должной оптимизации и скорости сборки того же C или C++ не добиться — дорого во всех смыслах.
По идее JS в UI это клей связывающий выход одной native функции со входом другой. Т.е. в принципе JS это язык описания event routing / dispatching. Смысла JITить какой-нибудь click event handler немного если он срабатывает ровно один раз за время жизни страницы. А вот парсить их надо быстро.
Понятно что в условиях browser sandbox JS это единственная опция написать какой-нибудь ray tracer. Но вот эта единственность и есть основная проблема.
Представляется что гораздо эффективнее было бы иметь тот же embedded С например или вообще абстрактную bytecode VM типа JavaVM которую можно кормить compiled bytecodes + удобный interop из JS с этим хозяйством.
Т.е. путь того же Python — простой bytecode interpreter + удобная возможность встраивания native (или тех же bytecodes близких к native) для функций которым нужен CPU на 100%.
Как-то такое решение представляется более грамотным с инженерной точки зрения.
А так… создавать чисто интерпретирумый язык по своей изначальной природе, а потом героически преодолевать его интерпретирумость…mraleph
20.05.2016 13:55+1В JS скорость парсинга одинаково важна как и скорость исполнения.
Да скорость парсинга важна. Только ведь скорость парсинга тут вообще сбоку припёка — наличие, отсутствие оптимизирующего компилятора не влияет на нее.
Оптимизатор чаще всего сидит в отдельном потоке и оптимизирует только горячий код, как раз из соображения, что затраты на оптимизацию должны окупаться.
Конечно, не все так просто. Оптимизации могут полагаться на разного рода информацию собранную во время неоптимизированного исполнения — поэтому нужно так реализовывать сбор этой информации, чтобы производительность не страдала. Можете быть уверены — разработчики JS VMs все это стараются учесть и найти баланс между пиковой производительностью и startup.
Т.е. путь того же Python — простой bytecode interpreter + удобная возможность встраивания native (или тех же bytecodes близких к native) для функций которым нужен CPU на 100%.
Python — это очень странный пример. Люди ведь совсем даже не поголовно довольны его производительностью — существует целый ряд различных компляторов/реализаций, которые стараются решить проблему производительности. Начиная от Cython и заканчивая PyPy. DropBox вот тоже свой собственный JIT пилит.
А так… создавать чисто интерпретирумый язык по своей изначальной природе
Так уж сложилось, что люди которые JavaScript создали, и люди, которые «героически преодолевают его интерпретируемость», это совершенно разные люди.csmile
21.05.2016 20:19Про Python и его производительность.
Когда кто-то реально упирается в его производительность то пишут native extension. NumPy как пример.
То же происходит и в JS когда он работает в среде node.js. В обоих случаях есть относительно простой механизм встраивания native code.
Собственно я про это и говорю. Ну не надо писать ray tracers в JS. Гораздо продуктивнее было бы иметь возможность встраивания чего-то что лучше ложится на регистры и архитектуру CPU.
Jabberwok
24.05.2016 19:02Сложно сказать одинакова ли важна скорость парсинга как и скорость исполнения. По дву причинам, во-первых многие тяжелые веб приложения висят в памяти достаточно долго и скорость исполнения начинает выигрвывать заметно. Например, у меня вкладка с Google Inbox открыта постоянно. Если даже парсинг+компиляция заняли несколько секунд то выгодна от такой оптимизации давно себя окупила. Вторая причина в том что JS можно скомпилировать один раз и хранить в кэше, и проверять потом по хэшу. Сайты особенно с тяжелым JS не часто его обновляют (максимум несколько раз в день). Сколько раз в день пользователь обновляет в день условный Facebook? И сколько раз Facebook обновляет JS? В таком случае дорогая оптимизация тоже себя окупает. Ну а скоро и WebAssembly подъедет.
mraleph
24.05.2016 19:10вопрос о том, насколько Facebook/Inbox/etc выигрывает от того, что его код оптимизировали, решается отнюдь не всегда в пользу оптимизаций :)
Ну а скоро и WebAssembly подъедет.
и люди сразу перепишут UI Facebook на С++ вместо JS :)Jabberwok
24.05.2016 19:15Выигрывает или проигрывает пользователь. Если оптимизация действительно оптимизирует то для меня это лучше, значит Inbox батарейку меньше ест.
mraleph
24.05.2016 19:26Когда я говорю «насколько Facebook/Inbox/etc выигрывает», я как раз и имею ввиду «выигрывает по производительности», т.е. становится быстрее.
С кодом всяких разных web-приложений все очень не просто — много полиморфизма, сложно оптимизировать, классические оптимизации рассчитанные на мономорфный код часто не окупаются.Jabberwok
24.05.2016 19:39Полиморфизм тоже можно компилировать (см большинство компилируемых языков). Если отделить ООП от использования объектов как dictionary то большинство объектных структур будут вполне статисческими в приложении которое по несколько часов работает.
mraleph
24.05.2016 19:55Полиморфный код можно, конечно, компилировать, но это отнюдь не так просто, как вам кажется — если хочется добиться, действительно быстрого вызова методов. Это только в С++ у вас есть простые явные vtable-s и все просто, уже в Java начинаются всякие интересности если хочется сделать быструю реализацию интерфейсных вызовов (поищите в интернете статьи о реализации
invokeinterface
).
большинство объектных структур будут вполне статисческими
Это действительно так (хотя и здесь есть свои тонкости). Однако интерес тут представляет не сами объекты, а то, что с ними происходит… Для оптимизации методов, которые работают с этими объектами надо видеть статичность статичность метаструктуры, если можно так выразится. Выше в тексте статьи я приводил уже пример с
function make(x) { return { f: function () { return x } } } var a = make(0), b = make(1);
здесь не всякая VM может увидеть, чтоa
иb
имеют одинаковую метаструктуру — V8, например, не может.Jabberwok
24.05.2016 19:57А обо что именно спотыкается V8 (на замыкание с объектом?) и как упростить пример чтобы он узрел?
mraleph
24.05.2016 20:10Если мы хотим оптимизировать
function f(x) { return x.f() // (*) } f(a); f(b);
то V8 спотыкается о то, что она при отслеживании метаструктуры рассматривает функции как нечто неделимое, т.е. она будет считать, что вызов в точке(*)
полиморфный. А если бы она учитывала, что функция это тело+контекст, то было бы лучше.
Чинится очень просто: отказом от использования замыканий таким образом на горячих путях.
function Classic(x) { this.x = x; } Classic.prototype.f = function () { return this.x; } function make(x) { return new Classic(x); } var a = make(0), b = make(1);
Но это только один пример…
Можно рассмотреть полиморфизм другого толка:
function A() { this.x = "a"; this.a = 0; } function B() { this.x = "b"; this.b = 0; } var a = new A(), b = new B(); function use(o) { return o.x; } use(a); use(b);
Как скомпилироватьuse(o)
во что-то умнее чем (псевдокод):
function use(o) { if (o.hiddenClass === ClassA) { return o.x // load from some fixed offset } else if (o.hiddenClass === ClassB) { return o.x // load from some fixed offset } else { return /* either generic load or deopt */ } }
вопрос открытый
Wouw
19.05.2016 06:04Вячеслав, скажите, как вы оцениваете перспективы emscripten?
mraleph
19.05.2016 14:01+6Я не большой поклонник компиляции C++ в JS. Если WebAssembly получит распространение и поддержку во всех браузерах, то emscripten станет не нужен, потому что для clang есть уже экспериментальный backend, который умеет выдавать WebAssembly.
Вопрос нужен ли нам WebAssembly я оставлю за рамками — пусть на него отвечают те, кто этим WebAssembly занимаются.
DeLuxis
19.05.2016 07:54Когда v8 будет поддерживать Asm.js?
rumkin
19.05.2016 11:30Никогда, теперь есть WASM.
FForth
19.05.2016 18:37Есть такое мнение с www.forth.org.ru/news от ас
WebAssembly поддержит Форт
Цитата:
github.com/WebAssembly/design/blob/master/AstSemantics.md пишет: Multiple return value calls will be possible
— это приниципиальный момент в возможности поддержки Форта в этой новой виртуальной машине.
Expression trees offer significant size reduction by avoiding the need for set_local / get_local pairs in the common case of an expression with only one, immediate use.
— ну а это уже неприкрытый Форт Инфиксные выражения исходного языка, которые парсятся в expression trees, рекурсивно разворачиваются при компиляции, и полученный код работает со стеком последовательно без трактовки их в качестве именованных локальных переменных. Так же как locals в Форте не выполняют load/store, в/из стека куда-то ещё, а могут использоваться напрямую и «анонимно» в последовательном вычислении.
Ср. www.forth.org.ru/~ac/rationale/FORTH.TXT
P.S. Чем это полезно в плане производительности и как это лучше «утилизировать» в JS?
mraleph
19.05.2016 14:05+5Понятия не имею. Меня про asm.js вообще бесполезно спрашивать, я считаю, что это совершенно бесполезная (или даже вредная) «технология» и мир без нее будет лучше.
rumkin внизу правильно замечает, что на замену asm.js пришел WASM… Я, впрочем, не большой фанат WASM в его текущей форме.guai
19.05.2016 19:19А что конкретно вам там не нравится? В WASM
mraleph
20.05.2016 13:27+1В WASM мне не нравится то, что они не поддерживают ничего, что сделало бы этот самый WASM для меня полезным. Меня не интересует возможность запускать C++ браузере. Меня интересует возможность запускать динамические языки в браузере отличные по семантике от JavaScript. Причем я хочу это делать с минимальными издержками. Например, я не хочу компилировать мой написанный на C++ GC в WASM — я хочу полагаться на GC, который уже в браузере есть… при этом, кстати, GC не так-то и просто скомпилировать, потому что я хочу ходить по стеку в поисках корней, а WASM такого не позволяет. Аналогично, я не хочу писать свой JIT — я хочу полагаться на JIT, который в браузере есть — со всеми его мощьными фичами типа адаптивной спекулятивной оптимизации.
Поэтому от WASM я впервую очередь жду таких фич как интеграция с GC и JIT.guai
20.05.2016 14:02Да, это действительно повод быть им недовольным.
А я чота думал, там такое как раз и будет, типа некий сериализованный AST абстрактного языка. Типа лиспа. Видимо, не так я это понял.
Спасибо за ответ.
ReklatsMasters
19.05.2016 18:42+3Несколько раз пользовался вашей IRHydra, инструмент конечно полезный, но очень специфичный. Да, он показывает где деоптимизации, однако хотелось бы минимальные примеры правки этих деоптимизаций или ссылки на более подробное их описание. А то сейчас их даже скопировать нельзя — при отводе мыши блок с описанием скрывается
mraleph
20.05.2016 13:29+2В таких случая можно баги файлить на GitHub или посылать мне описания непонятных деоптимизаций. Я разьясню деоптимизацию — и куда-нибудь запишу ее как пример для будущих поколений.
fetis26
23.05.2016 16:49У меня тут спор возник с одним разработчиком. По поводу применения
const
с объектами.
Он утверждал, что смыслconst
это просто заменить данный идентификатор значением константы. И его нельзя использовать с объектами.
Это может быть верно для компиляции Си или Ассемблера, но в мире JS имхо делается фиксированная привязка на область памяти с данным значением. И в моем пониманииconst
скорее означает "я не хочу присвоить данному идентификатору что-то еще", нежели какие-то конкретные детали реализации.
Вот рассудите, как человек знающий потроха реализации JS.
ЗЫ. Про то, что свойства объекта-константы все равно можно менять, я знаю. Речь не про это.
faiwer
23.05.2016 17:57Дык это же новый js-holywar. Раньше про
;
и,
споры были. А теперь вот проconst
:) Правда если рассматриватьconst
как "заменить данный идентификатор значением константы", то он с этой задачей справляется довольно паршиво.
mraleph
23.05.2016 23:26+3Он утверждал, что смысл const это просто заменить данный идентификатор значением константы.
Разумеется в JavaScript семантика уconst
отнюдь не такая. Самый наглядный пример того, что это все-таки неизменяемая привязка переменной к значению:
(function () { console.log(f()); // prints <?> const X = '<!>'; console.log(f()); // prints <!> function f() { try { return X; } catch (e) { return '<?>' } } })();
С другой стороны виртуальная машина может и должна бы использовать это самое свойство неизменяемости, и уж по крайней мере при оптимизирующей компиляции стараться подставить значение «переменной» прямо в код, как константу.
V8 не использует, к сожалению. А JSC, например, использует. Код
const X = 10; function bar() { return X * X }
компилируется вreturn 100
по сути дела
sp1se
Отличная статья! А нет ли докладов, других статей от Вячеслава на русском?
mraleph
На русском у меня есть, но мало
Из более-менее свежего: http://mrale.ph/blog/2015/04/12/jsunderhood.html
Из старинного есть:
http://www.youtube.com/watch?v=tCG0aPNvkTs
http://s3.mrale.ph/TechForum2012.pdf