JavaScript
Google Chrome
Debugging
Comments 205
+7

Прикольно. Но будем честны, большинство веб приложений на NodeJS стараются делать stateless. Т.е. в какой-то момент ссылки не будет и gc все почистит.
Однако, буду иметь ввиду, если вдруг появится стейт. Спасибо!
Багу на v8 не заводили? Мне кажется, по хорошему, gc должен со временем реально копировать только часть строки и прочищать родительские ссылки.

+5
bugs.chromium.org/p/v8/issues/detail?id=2869 (в 2013 открыта, в 2019 закрыта без каких-либо изменений в коде V8). Там же есть ссылки на другие похожие посты.

А вообще у mraleph есть отличная статья с картиночками, написанная с другой целью, но организацию строк и ссылки на исходные строки немного освещающая: mrale.ph/blog/2016/11/23/making-less-dart-faster.html (про утечки искать в конце первой части по строке «it leads to surprising memory leaks»).
+1
Но будем честны, большинство веб приложений на NodeJS стараются делать stateless.

Да я бы не сказал.
При сравнительно медленном движке (не Go, не Java) оставлять код работать как можно дольше, а не очищать все на каждый запрос — это нормальная возможность ускорить ваш сервис.

+2
Бенчмарки V8 и Node.js в целом поспорят с утверждениями о медленном движке.
+2

А что такое бага? Если интерпретатор роняет хост, неожиданным образом это бага или нет? В среке такого нет, что надо чистить строки, наоборот, строка примитив и, по идее, это не забота програмиста, что конкретные реализации языка ведут себя подобным образом. Поведение iframe в ios тоже не бага, но тоже удивительно, когда блок, плевав на явно указанные размеры, увеличивает сам себя.

-2

Хороший вопрос, что такое бага на самом деле.
Где граница между фичей, требующим специального подхода и багой?
Я написал, что это не бага, потому что:
1) хром написали так, чтобы он работал быстро, и как -бы разработчики "не виноваты", что у пользователя не хватает памяти.
2) количество занимаемой памяти определяется объемами строк, с которыми работает пользователь

+1

1) Памяти свободной у пользователя может быть много, но Хром не пытается ею всю использовать в подобных кейсах.
2) Не пользователь, а программисты сайта, на который пользователь зашёл.

+6
Спасибо за статью, не знал об этом. Из статьи подумал, что у строки может быть только один родитель, тогда конкатенация с другой строкой должна решить проблему. Но, оказывается, все ещё сложнее: внутри V8 строки представляют собой ориентированый граф из узлов, у каждого из которых есть упорядоченное множество родителей. Тогда конкатенация делается очень быстро добавлением общего корня к двум узлам. Но иногда это может ухудшить производительность и есть библиотека github.com/davidmarkclements/flatstr, которая делает этот граф одноуровневым.

К счастью, во фронтэнд разработке редок сценарий, когда откуда-то приходят большие строки, из которых накапливаются кусочки, но в любом случае знать об этом крайне важно. Удивлён, что не встречал это в статьях типа «10 вещей о JS, которые вы не знали»
+3
Работа с картинками в base64, сталкивался с такими строками(3~5МБ) буквально вчера, только там не было необходимости изменять их.
+2
Столкнулся с этим при разработке браузерного расширения. Все условия соблюдены: оно постоянно висит в памяти, делает периодически ajax, и складывает кусочки в объект-кэш (чтобы не повторять ajax по одним и тем же адресам).
-54
Ну а что можно требовать от средства разработки, которое забацали на коленке без ТЗ, чёткого понимания целей и задач, возможностей расширения в будущем и кучи прочих тонкостей, о которых разработчики средств для веб-программирования даже и не подозревают. В итоге имеем то, что имеем, а за неимением лучших средств выдаем эту поделку уровня третьего курса провинциального вуза за better of best, надуваем щёки, проводим конференции, улыбаемся и машем, проклиная всё, когда завтра сдавать проект, а тут косячки, там косяки и вот тут громадный косячище, который за оставшиеся 8 часов никак не поправить. Вот.
+29

V8 написали на коленке без тз студенты третьего курса? Риали? Откуда дровишки?

0
Не студенты, конечно, но вот то, что без понимания целей — так это факт. Никто там в момент создания проекта не задумывался, что когда-то будет на нём нода, куча всяких поделок на Хромиуме и т.д. Это прямо вот чувствуется прямо вот даже по публичной апишке V8.
+7

почему сразу студенты и на коленках? Это очень эффективный и производительный способ работы со строками. Использование счетчика совместных ссылок очень положительно сказывается на производительности. Более того, в ряде случаев позволяет неплохо экономить память.

+5
Ого, оптимизированного аглоритмоемкого монстра назвали поделием на коленке. Если что, v8 не фронт-энд разработчики писали :)
+11
Не думаю, что он что-то имел ввиду: он просто хейтер, судя по его комментариям.
+2
Явно речь не о V8 а о языке. Но есть доля правды — то как был разработан и быстро внедрен язык сильно повлияло и ограничило разработчиков движков. Чем гибче и «проще» (для новых разработчиков) язык — тем сложнее (а значит и потенциально глючнее) его движки. За те 2 недели написания языка был заложен фундамент (или ТЗ, смотря как посмотреть) на котором уже приходится строить все движки, фреймворки, браузеры и т.д.
-1
Про JS надо помнить эпоху когда он создавался — тогда никто, даже в смелых мечтах (или унылых кошмарах) не мог бы представить то в чем мы сейчас варимся :)

п.с. К слову — в то время тоже было много споров/докладов/холиваров о MVC :)
+7
Бесспорно. Валить все шишки на JavaScript определенно нельзя. Как по мне — это серия неудачных решений, «коротких путей» и недальновидных дизайнов. Взяли по-быстрому накатали язык. Потом он быстро пошел в массы и вскоре стал де-факто стандартом в web. Потом накатали движков и завоевали рынок одним браузером (а стало быть и одним главным движком JS, который имеет приоритеты в развитии сильно совпадающими с интересами одной большой коммерческой компании). А потом еще и додумались все это чудо зафигачить на сервера в качестве удобного и быстрого языка для прототипирования и небольших стартапов. Ну а дальше и это детище начало разивиаться, обретать популярность и оставаться на продакшене даже после того как проект переростает этап стартапа.
Вот и приплыли туда где мы есть сейчас, к серверам которые не умеют даже строки толком подчищать… и это я не говорю уже о бессмысленности побитовых операций а также о том что любой JS код хранит в себе еще и СТРОКУ СО ВСЕМ КОДОМ ВКЛЮЧАЯ КОММЕНТАРИИ И ПЕРЕВОДЫ СТРОК!!! (это если код чистый, не, прости господи, скомпилированный)

P.S. вероятно мой коммент заминусуют, но уж сильно меня печалит то к чему приводит вся история с JS и как этот достаточно нишевый и несколько спорный язык программирования пихается во все возможные щели (уже даже на микропроцессорах есть прошивки где можно на JS кодить!!!)
0
А какие есть неудачные решения есть в JavaScript, которые однозначно всем сильно мешают жить, и все однозначно согласны, что они плохие и лучше бы их не было?

Например, typeof null на практике сильно не мешает, а прототипы или динамическая типизация — вы не сможете сказать, что все разработчики однозначно против.
+1
Когда забываешь слово var? Кажется, за всё, вообще всё время это ни разу ничего у меня не ломало :). Более того, такие штуки видно сразу, а если в вашем коде видно их не сразу, например, двадцать вложенных функций каждая со своими локальными переменными, то у вас в любом случае проблемы.
Нет, я согласен, что есть море неудачных решений, которые при определённых обстоятельствах делают всё плохо, но кажется, что это вот примерно настолько же плохо, как typeof null.
0
Запишем в не очень мешающее жить решение.
Я спросил, что (очень мешает жить) && (все согласны, что это плохо).
0
Любое решение можно оправдать и найти сторонников/защитников

Если вы изучали много разных языков программирования то могли заметить что многие современные языки (да и старые тоже) разрабатываются с точки зрения начального удобства и минимизации возможных ошибок. Так, например, в Python правильное выравнивание кода является обязательным и код будет выдавать ошибку в обратном случае. В Go табуляция принята как единственная система чтобы закрыть холи-вары (хоть я и приверженец пробелов). Также современные языки отказываются от таких принципов как перегрузка операторов, инструкция GOTO: и #define а также от множественного наследования. Все это помогает минимизировать количество потенциальных ошибок допускаемых программистами нового языка.
Возможность в non-strict режиме создать переменную которая будет публичной, невозможность НОРМАЛЬНЫМИ способами объявить приватную функцию или переменную, прототипное наследование, хранение всего кода функции в виде строки (включая переводы строк и даже комментарии) в переменной самой функции а также недостаточная проработанность языка возлагающая ответственность за оптимизацию и реализацию большинства моментов на разработчиков JS-машины (в следствии чего и произошла та проблема что описана в статье), да и вообще возможность сделать в JS практически что пожелаешь и выстрелить себе в ногу любым самым изощренным методом — это все не направлено на минимизацию потенциальных ошибок у программистов, даже напротив
0
>прототипы или динамическая типизация — вы не сможете сказать, что все разработчики однозначно против

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

Прошу заметить что то что я пишу это лишь мое мнение (которое наверняка разделит часть IT-сообщества) и оно сформировано во многом оттого что я приверженец «старой школы» (т.е. С/С++ и т.д.). Однако по моему мнению важные проблемы кроются именно в том что язык интерпретируемый и нетипизированный. Это бесспорно помогает молодым стартапам и вообще любым прототипам, PoC, MVP, MSP и т.д., но мне обидно видеть что это же используется и в финальной разработке. Когда Slack с их то масштабами грузит комп в десятки раз больше любого видео-плеера который производит потоковое декодирование, или когда очередная экранная клавиатура под Android/iOS весит больше чем весь Windows 95 — это, как по мне, просто не правильно
Тут не только JS виноват, конечно же. Но просто JS это для меня как яркий флаг символизирующий все движение в целом

А сторонники и противники появятся всегда. Тот же JS дал большой толчок в популяризации программирования в целом, т.к. начальный порог вхождения не велик, а учиться ему можно даже дольше чем тому же С (а значит и «рости» по должности и требовать ЗП повыше можно еще долго). К тому же я уже говорил что для определенных ниш этот язык и даже тот же node.js да и Electron вполне себе подходят и очень хорошо справляются со своей целью. Проблема не в том что эти решения есть а в том как и где их используют
0
Классика. Говорить, что язык объективно плохой, за то, что некоторые пишут на нём плохой код :)

Это вообще ооооочень странно — выдавать медлительность Gmail / Slack за аргумент, почему js плохой. Я вот работал с ANSYS, такой очень распространённый инженерный софт. Он умеет подвисать на минуту при right-click (нет, там точно не нужны сложные расчёты чтобы открыть контекстное меню с тремя пунктами) и тому подобных действиях. А ведь он написан, кажется, на C++. На сильно статически типизированном! И даже без Qt и т.п. штук.

Почему Slack или Gmail столько кушает — я не знаю. Правда. Всё, что я когда-либо писал, в принципе не могло столько есть. Я серьёзно не представляю, как можно написать такой простой интерфейс очень тяжёлым.

Вот это всё, надеюсь, проиллюстрирует то, что я хочу сказать.

Ну а про сторонников — нет, просто вы говорите про наличие критических всем мешающих недостатков как про что-то объективное, и с чем никто не спорит, мне интересно, что именно вы имеете в виду :). И одно дело если с этим не согласны два с половиной джуниора, другое — много процентов разработчиков.
+1
Классика. Говорить, что язык объективно плохой, за то, что некоторые пишут на нём плохой код :)
Отрицание конечных результатов в оценке языка — такая же точно классика. Хотя ей обычно больше хаскелисты страдают.

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

Я серьёзно не представляю, как можно написать такой простой интерфейс очень тяжёлым.
Тем не менее на средние JS-проекты требуют сильно больше ресурсов, чем средние C++-проекты. И даже тот же ANSYS — то, что он тормозит, мы уже поняли, а вот сколько памяти он требует? Тот же гигабайт памяти, как и открытая у меня сейчас вкладка с GMail'ом?

Практика показывает, что как раз количество памяти потребляемое программой — гораздо сильнее зависит от языка, чем скорость работы. И вот тут у JS — всё плохо: написанные с его помощью проекты жрут либо много памяти, либо очень много памяти. При этом LUA какая-нибудь — таких ресурсов не требует. Несмотря на свою динамическую типизированность и сборщик мусора… Да — она гораздо медленнее, но как раз на суперскорость она и не претендует…
+1
Допустим, но как вы докажете, что большинство проектов на js едят много памяти и тормозят? :) По моим данным, всё совершенно наоборот — на js пишут высоконагруженные штуки вроде сервера сообщений вк, и вообще js наверное самый оптимизированный из динамически типизированных интерпретируемых языков.
И более того, если у вас кушает много памяти что-то в браузере или в electron — почему вы уверены, что проблема в языке, а не в том самом Chrome, который вообще-то известен своей прожорливостью? К слову, на чём он там написан, на плюсах? Давайте ругать плюсы за прожорливость хрома и гмейла в частности).

ANSYS, кажется, и памяти ел больше, чем хром в брачный период. Но я точно не скажу, это было давно и в страшном сне.

Про Lua совершенно очевидно напрашивается вывод: это трейдофф память -> скорость. Разработчики V8, вероятно, решили, что работать быстро важнее, чем есть мало памяти. Это мои домыслы, но тем не менее. И высоконагруженные сервера пишут не на требующем малых ресурсов Lua, а, как ни странно, на js.
0
вообще js наверное самый оптимизированный из динамически типизированных интерпретируемых языков
Возмножно. Но все эти оптимизации всё равно не позволяют достичь скорость написанной на статически типизированном язуке — однако требуют кучу дополнительных ресурсов.

К слову, на чём он там написан, на плюсах?
Частично.

Давайте ругать плюсы за прожорливость хрома и гмейла в частности).
Не получится: версия десятилетней давности, написанная чисто на плюсах, особенно много и не жрёт (по современным меркам). А вот версия, в которой за последние лет 10 перенесли кучу всего с C++ на JavaScript — совсем другое дело.

И высоконагруженные сервера пишут не на требующем малых ресурсов Lua, а, как ни странно, на js.
Высоконагруженные сервера (сотни тысяч и миллионы QPS) до сих пор пишут на C++. На js пишут сервера, при создании которых хочется использовать армию дешёвых JS-программистов — а потом начинаются приседания, когда оказывается, что железо-то всё-таки небесплатное…
-1
Но все эти оптимизации всё равно не позволяют достичь скорость написанной на статически типизированном язуке — однако требуют кучу дополнительных ресурсов.

Странный аргумент. Давайте писать на ассемблере, раз нам важна в первую очередь скорость.
К слову, программисты гугла считают иначе, и ровно по этой причине они не захотели поддерживать asm.js

А вот версия, в которой за последние лет 10 перенесли кучу всего с C++ на JavaScript — совсем другое дело.

Аргумент был бы валиден если бы это было единственное, что изменилось в движке за 10 лет. Но нет, как вы понимаете, там хренова туча всего.

Высоконагруженные сервера (сотни тысяч и миллионы QPS) до сих пор пишут на C++. На js пишут сервера, при создании которых хочется использовать армию дешёвых JS-программистов — а потом начинаются приседания, когда оказывается, что железо-то всё-таки небесплатное…

И на плюсах тоже пишут. Например, все сообщения в вк на сервере, кажется, работают на ноде. Попробуйте сказать, что это малонагруженный сервис.
Что касается армии дешёвых js-программистов: а у вас есть какие-то достоверные данные, что всё происходит именно так, и что js-программисты дешевле остальных? Вы говорите так, будто претендуете на объективность, но при этом само мнение звучит как нечто очень сомнительное, как мнение совершенно поверхностно разбирающегося человека.
0
вас есть какие-то достоверные данные, что всё происходит именно так, и что js-программисты дешевле остальных?

Смотря, что за программист, и насколько дешевле.
Кто-то большие и сложные продукты пишет, и получает на уровне с Jav'ерами.
А кому-то мозгов не хватило, и он стал «кодящей обезьяной» за 50круб.
0
Давайте писать на ассемблере, раз нам важна в первую очередь скорость.
Ассемблер уже давно не позволяет достичь максимальной скорости. Выход новых поколений процессоров требует переделки кода для утилизации новых возможностей. В случае с каким-нибудь C++ — это перекомпиляция, в случае с ассемблером — переписывание.

Так что на ассемблере можно писать только очень маленькие, критически важные куски — как, собственно, и делают.

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

Однако они согласислись с самой концепцией и запилили-таки WebAsm

Но нет, как вы понимаете, там хренова туча всего.
Совершенно верно. Не вся прожорливость современного Хрома — из-за JS. Но свою толику он внёс тоже.

Например, все сообщения в вк на сервере, кажется, работают на ноде. Попробуйте сказать, что это малонагруженный сервис.
Смотря с чем сравнивать. Когда Pokemon Go запустили, то нагрузка на Гугловые push-сервера неожиданно выросла на миллион QPS в секунду (чуть меньше, там что-то вроде плюс 850 тысяч QPS было). Матюков было много, но в основном потому что планировалось «всего» плюс 100 тысяч QPS, а получили почти миллион. За несколько часов перенастроили сервера и всё пришло в норму. А сколько сообщений пересылает VK в секунду?

Что касается армии дешёвых js-программистов: а у вас есть какие-то достоверные данные, что всё происходит именно так, и что js-программисты дешевле остальных?
Они, на самом деле, часто даже дороже. Но когда речь идёт о бизнес-решениях, то «восприятие важнее реальности»: их считают более дешёвыми и потому пытаются применить — где надо и где не надо. Какую зарплату они при этом реально получают — не так принципиально.

Вы говорите так, будто претендуете на объективность, но при этом само мнение звучит как нечто очень сомнительное, как мнение совершенно поверхностно разбирающегося человека.
Уверяю вас: бизнес-люди, решающие использовать Node.JS или Electron — разбираются в технических вопросах ещё хуже меня.
+1
Мне кажется вы не внимательно прочли что я написал. Про Gmail я не писал ни слова. Про Slack я четко написал что «тут не только JS виноват, конечно же». Про причины тормозов Slack (а точнее того сколько всего тянет за собой Electron и тому как он работает с процессами браузера на каждую команду писали на хабре уже много раз)
Проблема которую я описал не столько в языке сколько в том как и где его применяют. Любой язык имеет свою нишу, универсальных и идеальных не бывает. Кроме С++, конечно же :D (если что это шутка, не вырывайте из контекста, С++, к сожалению, и своих проблем хватает и применимость ограниченная)

«Некоторые пишут плохой код» — это, к сожалению, крайне частая проблема программистов JS. Отчасти дело кроется в «низком пороге входа», а отчасти в том, что, как мне кажется, чтобы написать хороший код на JS нужно потратить на его изучение и набивание шишек намнооого больше времени и усилий чем на низкоуровневых языках. Вход проще, а обучение дольше. Ну и желания доходить до конца обучения мало у кого есть, ведь ЗП у JS программистов сейчас высокие и обоснованны больше спросом а не качеством предложения, потому ЧСВ у новых программистов повышается и они не считают что им что-то еще надо. Подучить больше фреймворков, больше либ, больше подходов… и дописать в резюме строчку Senior спустя год-два программирования.
Я лично провел несколько сотен собеседований за свою жизнь и могу точно сказать что среди тех кого я собеседовал раздутое чаще всего именно у JS разработчиков. Причины я описал выше. Не считаю что это их вина или что у них плохой потенциал (честно, никого не хочу задеть или обидеть), просто пытаюсь показать как низкий порог вхождения влияет на подход и виденье индустрии в целом

Возвращаясь к JS — извините, но я твердо уверен что JS не подходит для высоконагруженных серверов с миллионами запросов в секунду и bigdata на борту. Да, построить такую систему можно (путем горизонтального скейлинга и больших серверных затрат), но зачем?
А именно так часто и получается когда продукт и нагрузка растет постепенно и момента когда «есть время чтобы все переписать» так и не наступает
-1
если что это шутка, не вырывайте из контекста, С++, к сожалению, и своих проблем хватает и применимость ограниченная
На самом деле как раз эта «ограниченная применимость» и приводит к написанию качественного кода.

чтобы написать хороший код на JS нужно потратить на его изучение и набивание шишек намнооого больше времени и усилий чем на низкоуровневых языках
Не совсем так. На JS просто можно написать плохой код — и он вам это «простит». C++ гораздо более жесток: если вы не можете внятно описать ваш дизайн, то добиться того, чтобы ваша программа не падала при малейшем шевелении мышкой — будет очень сложно.

Соотвественно ваша программа будет либо хорошей, либо не доживёт до стадии релиза. Такое «программирование по бразильской системе».
-2
На самом деле как раз эта «ограниченная применимость» и приводит к написанию качественного кода.


Не могу с вами согласиться. У JS есть своя ниша, но это не мешает всем пропихивать ее где только можно и нельзя. Качественность кода достигается качеством программистов, и вовсе не применимостью

Не совсем так. На JS просто можно написать плохой код — и он вам это «простит». C++ гораздо более жесток: если вы не можете внятно описать ваш дизайн, то добиться того, чтобы ваша программа не падала при малейшем шевелении мышкой — будет очень сложно.

Эм… с чего вы взяли что на С++ нельзя написать плохой но рабочий код? Еще и как можно, и полно такого. А JS «простит» весьма спорно. То что он не упадет а просто закончит выполнять функцию и напишет сообщение об ошибке в консоль (которое потом никто не прочтет а просто забьет) — так это непредсказуемое выполнение вместо полного падения. Что предпочтительнее — тема весьма спорная. Но называть это «простит» — я бы точно не стал

ИМХО, но написать ХОРОШИЙ код на JS куда сложнее чем на С++. Любой код, или такой который, как вы выразились, будет «прощать» — конечно же проще на JS, но это не хороший
-1
А JS «простит» весьма спорно. То что он не упадет а просто закончит выполнять функцию и напишет сообщение об ошибке в консоль (которое потом никто не прочтет а просто забьет) — так это непредсказуемое выполнение вместо полного падения.
Очень много вещей, который в C++ просто не пропустит компилятор в JS породят… нечто. Не всего понятно, что, то функция не упадёт.

Например {} - [] — почему это равно -0? Да, ответ можно прочитать в спецификации языка, но практического смысла тут никакого — и в C++ программа, содержащая подобное просто не скомпилируется.

ИМХО, но написать ХОРОШИЙ код на JS куда сложнее чем на С++.
Конечно. Чем больше «странного» и «дурацкого» кода является валидным и как-то работает и что-то таки делает — тем сложнее попасть в узкое подмножество «хорошего» кода…
+1
Очень много вещей, который в C++ просто не пропустит компилятор в JS породят… нечто
Ровно и наоборот. Напишите #define true false, и у вас это благополучно скомпилируется.
Скажете, такой код сознательно не напишешь? Я тоже сознательно не пишу {} - [], ну ничего себе :)

Конечно. Чем больше «странного» и «дурацкого» кода является валидным и как-то работает и что-то таки делает — тем сложнее попасть в узкое подмножество «хорошего» кода…
Опять киваю на дефайн. И миллион других возможностей выстрелить в себе ногу, например, переопределение операторов.

На самом деле нет, скажу совершенно непопулярное мнение: да, js очень гибкий, и я считаю это плюсом. В моём опыте было море случаев, когда это спасало. Чаще в целях отладки, конечно (например, чтобы посмотреть, что делает библиотека на канвасе, не копаясь долго в её коде, можно руками переопределить прототип CanvasRenderingContext2D, встроив туда логгирование), хотя однажды залезающий в приватные поля чужого прототипа код даже думали выкатить в прод, потому что это был просто единственный вариант.
Но я не говорю, что такое должно оказываться в продакшене и вообще в коде. Это скорее дополнительная возможность, которая иногда облегчает жизнь — как тот же самый дефайн у плюсовых разработчиков. А чтобы писать именно для продакшена именно хороший код, у нас есть многочисленные правила и многочисленные линтеры включая тайпскрипт (который по сути тоже продвинутый линтер).

И я понимаю, почему это плохо. Привычная функция может оказаться работающей непривычно (опять киваю на дефайн), или же просто логика одного объекта будет раскидана по всему проекту, по разным модулям, и её сложно будет собрать в одно. Но, кажется, это более общая проблема, проблема организации проекта и т.п.: в любом языке я могу раскидать класс и его методы по всему проекту, и будет непонятная запутанная схема. Никакой возможности переопределять прототипы Number мне для этого не нужно.
0
Я тоже сознательно не пишу {} — [], ну ничего себе :)
Вам не нужно «сознательно» писать {} - []. Достаточно того, чтобы функция вместо числа вернула пустой массив или объект — и вы получите чушь. Скажем 2 - {} — это NaN. А дальше вы уже получаете что NaN и не меньше двух и не больше двух… закона ислючённого третьего больше нет, то есть вся логика программы летит к чертям.

Напишите #define true false, и у вас это благополучно скомпилируется.
И как вы можете этого добиться случайно?

Опять киваю на дефайн. И миллион других возможностей выстрелить в себе ногу, например, переопределение операторов.
И все эти возможности считаются опасными и требуют особого внимания при рассмотрении (review). Что предлагаете запретить/ограничить в JS? Возврат значений из функции? Сравнение значений переменных? Сложение-вычитание?

В C++, действительно, есть «сложные места», которые могут привевести к проблемах — и их рекомендуется избегать. JS весь состоит из «сложных мест» — и избежать их не получится.

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

Никакой возможности переопределять прототипы Number мне для этого не нужно.
Вы правы — плохой код можно писать на любом языке. Однако на JS у вас гораздо больше возможностей случайно выстелить себе в ногу. Потому что опасные места в C++ известны (#define, переопределение функций, использование чисел с плавающей запятой, множественное наследование и так далее) — и их можно избегать и/или использовать дозированно.

Но как вы можете избежать использования чисел с плавающей запятой в языке, где никаких других чисел просто нету? Как вы можете избежать проблем с отношением порядка, если никакого способа гарантировать то, что у вас не будет в данных типов, позволяющих сделать чудеса типа a > b && b > c && c > a, у вас нет?

Только попытками превратить JS в язык с типизацией (типа Closure или TypeScript)… но зачем — если можно сразу взять более вменяемый язык?

Тот факт, что, в какой-то момент, у индустрии не было выбора (браузеры поддерживали только JavaScript, а программы для браузера делать очень хотелось) — вся индустрия будет раслёбывать ещё долгие годы. Можно обсуждать почему это произошло и почему Mozilla, когда у них была возможность это безумие прекратить, от этого сознательно отказалась — но факт имеет место быть… JavaScript используется не потому что он хорош (из распространённых языков разве что PHP более ужасен), а потому что долгие годы у разработчиков не было выбора.

Ну ничего: как-то засилье фортрана в 70е и бейсика в 80е пережили — и JavaScript к катастрофе не приведёт…
0
Mozilla, когда у них была возможность это безумие прекратить, от этого сознательно отказалась


Можете пояснить, о чём здесь речь? Я, вероятно, не знаю какую-то часть истории.
0
Когда 11 лет назад NaCl только появился, то, разумеется, первое, что было сделано — с его помощью прикрутили разные другие языки к браузеру: TCL, Python (сайта сейчас не вспомню) и прочее.

Однако Mozilla «встала в позу» сказала «нет, в браузере будет только JavaScript» (там ещё было много шума по поводу «открытости Web'а» и прочего). После чего Google ушёл и вернулся с новой, «прогрессивной» моделью работы с браузером — PPAPI. В которой, в частности, сделали невозможным напрямую обращаться к DOM'у.

Причём сам интерфейс-то оставили: без него, как бы, Flash не полнофункциональным оказывается.

Но все другие языки — стали языками «второго сорта». Потому что всем остальным (кроме флеша) доступ к DOM'у «не положен».

Ну и чтобы окончательно убить платформу затеяли возню с PNaCl'ом — который намертво привязывает вас к LLVM (в то время как научить компилировать в NaCl можно любой более-менее вменяемый компилятор за пару недель… в PNaCl можно компилировать только через LLVM).

После чего альтернативы особо не осталось: либо вы используете JavaScript — либо нечто, построенное «поверх» JavaScript.

Так что вот. Шанс был… — но пролюбили. Ну а Java в браузере (в те же годы были разработки) — тут всё проще и понятней: Oracle. Оно даже до публичной демонстрации не дожило.
0
А PHP-то почему использовали и используют? Там выбор точно был и есть. И многие, скажем так, неоднозначные решения в нём аналогичны JS. Может не всё так однозначно?
-2
А кто вам сказал, что его активно используют? Лет 5-10 назад — да, было дело.

А сейчас — это лютое легаси, в основном. Примерно как Cobol или Fortran. Умереть он явно не умрёт, но и сказать, что на нём так уж много пишут таки нельзя.
0
А кто вам сказал, что его активно используют?

Практически любой хостинг за копейки предоставляет связку Apache + Nginx + PHP + MySQL. А куда мне идти со своим модным NodeJS или Go?
Умереть он явно не умрёт, но и сказать, что на нём так уж много пишут таки нельзя.

Сам язык развивается, фреймворки развиваются, новые библиотеки появляются. Конечно, это не Javascript со своими 10 фреймворками в день, но называть его не активно развивающимся я бы не стал.
0
Практически любой хостинг за копейки предоставляет связку Apache + Nginx + PHP + MySQL.
Где вы будете использовать всё тот же Wordpress со всё теми же Wordpress'ом и Drupal'ом, что и 10 лет назад.

А куда мне идти со своим модным NodeJS или Go?
Ну если у вас нашлись деньги на оплату NodeJS или Go программиста — то, уж наверное, и на простенький VPS найдутся.

Да, у PHP была своя ниша: вот именно «хостинг за копейки» для тех, кому VPS — дорого. Но с тех пор цены на VPS упали, а число желающих иметь свой сайтик стало и вовсе мизерным (Facebook, VK, YouTube и прочие — отлично работают, зачем ещё какой-то «хостинг за копейки»?), так что эта ниша, в общем, больше не критична.

Сам язык развивается, фреймворки развиваются, новые библиотеки появляются. Конечно, это не Javascript со своими 10 фреймворками в день, но называть его не активно развивающимся я бы не стал.
Дык и Cobol развивается (последняя версия — Cobol 2014) и Fortran (у него вообще последняя версия — Fortran 2018), языки на которых написаны миллионы (а то и миллирды) строк кода — так просто не умирают!
0
Где вы будете использовать всё тот же Wordpress со всё теми же Wordpress'ом и Drupal'ом, что и 10 лет назад.

Только сам Drupal сейчас на Symfony, и совсем не тот, что был 10 лет назад.
Ну если у вас нашлись деньги на оплату NodeJS или Go программиста — то, уж наверное, и на простенький VPS найдутся.

Ага, а так же на админа всего этого. Плюс программисту я могу заплатить один раз, а за хостинг и его администрирование придётся платить постоянно.
языки на которых написаны миллионы (а то и миллирды) строк кода — так просто не умирают!

Отличие PHP в том, что на нём и сейчас активно пишут миллионы строк нового кода и начинают новые проекты.
0
Только сам Drupal сейчас на Symfony, и совсем не тот, что был 10 лет назад.
Symfony уже скоро 15 лет исполнится и даже Drupal на неё переехал 5 лет назад. Вряд ли это можно всё назвать сильно новыми технологиями.

Отличие PHP в том, что на нём и сейчас активно пишут миллионы строк нового кода и начинают новые проекты.
Кто и где? Я вижу, что проекты, начатые 5-10-15 лет назад всё ещё поддерживаются, а вот новых, слава богу, не видно и не слышно особо.
0
Symfony уже скоро 15 лет исполнится и даже Drupal на неё переехал 5 лет назад. Вряд ли это можно всё назвать сильно новыми технологиями.

Symfony за это время прошёл большой путь.
Кто и где?

Любой местячковский форум, блог и просто сайт.
+1
Любой местячковский форум, блог и просто сайт.
И вот они будут что-то писать на PHP? Да бросьте. В лучшем случае возьмут что-то готовое.
+1
А это готовое никто не пишет? Большое наличие готовых решений это признак развитой экосистемы.
0
Большое количество готовых решений и отсутствие развития — это признак Legacy.

Это не хорошо и не плохо — это просто констатация факта.

С которого, собственно, и началась дискуссия.
+1
То есть попытка сделать из JS нечто подобное статически типизированному языку — только медленее и с большим расходом памяти.

Можно подробнее? Откуда у вас взялась такая интересная неправильная информация, что TS медленнее и больше ест памяти, чем JS?
-4
А вы вышеприведённую цитату читать не пробовали? С каким пор JS — это статически типизированный язык?

Я говорю просто о том, что какие бы поезда и самолёты вы не использовали — но доставка товара по маршруту Токио->Сингапур->Лондон->Москва->Владивосток будет медленнее и дороже, чем прямым рейсом.

TS — в любом случае надстройка над JS и вы платите за отсутствие статической типизации в последнем, даже если не пользуетесь преимуществами.

Если вы пишите frontend — ну, тут ничего не поделать, кроме как посоветовать сделать нативное приложение, если возможно.

Но зачем тянуть JS и TS на backend?
0
Вам не нужно «сознательно» писать {} — []. Достаточно того, чтобы функция вместо числа вернула пустой массив или объект — и вы получите чушь. Скажем 2 — {} — это NaN. А дальше вы уже получаете что NaN и не меньше двух и не больше двух… закона ислючённого третьего больше нет, то есть вся логика программы летит к чертям.


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

По мне так называть язык плохим только по тому, что есть плохие примеры его использования — неправильно.
+1
> На самом деле нет, скажу совершенно непопулярное мнение: да, js очень гибкий, и я считаю это плюсом. В моём опыте было море случаев, когда это спасало. Чаще в целях отладки, конечно (например, чтобы посмотреть, что делает библиотека на канвасе, не копаясь долго в её коде, можно руками переопределить прототип CanvasRenderingContext2D, встроив туда логгирование)

В Python доступна такая же гибкость. Но бессмысленные действия типа {} + [] он не допускает, автоматической конверсии с потерей важных данных не делает, undefined на несуществующий ключ объекта по умолчанию не возвращает.
-2
Имхо, это мелочи, которые не доставляют никаких проблем. А возвращать undefined на несуществующий ключ объекта это и вовсе полностью валидная логика, что с ней не так? о_О
+2

Не так с этой логикой то, что в огромном числе случаев этот самый undefined не сможет быть нормально обработан, в итоге возврат undefined лишь маскирует реальную ошибку.


В то время как Python сообщает нам что-то вроде "AttributeError: 'Foo' object has no attribute 'bar'", Javascript заявляет нам "очень понятное" "TypeError: undefined is not a function"

-1
> А возвращать undefined на несуществующий ключ объекта это и вовсе полностью валидная логика, что с ней не так? о_О

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

Это была первая из причин, почему я когда-то сбежал с Перла на Питон: когда a.b при несуществующем b даёт исключение, а если нужно что-то вернуть по умолчанию — то есть синтаксис типа a.get(b, 0) — это в разы удобнее, чем писать явно if not exists $a->['b'] на каждую проверку, где надо защититься от кривизны.

> Имхо, это мелочи, которые не доставляют никаких проблем.

Это мнение опровергается первыми двумя днями отладки типа «а какого чёрта оно не работает?», когда в отчаянии начинаешь расставлять такие проверки на валидность доступа и корректность аргументов операций чуть более, чем везде.
0
двумя днями отладки типа «а какого чёрта оно не работает?»

Ну, в тех случаях, когда сообщение «Uncaught TypeError: Cannot read property 'f' of undefined» недостаточно информативно, — поможет только статическая типизация.
+3

Так а откуда этот undefined там взялся-то? В этом чаще всего проблема, я так подозреваю.

0
Проблема именно в этом всегда, а не «чаще всего».
Откуда он там взялся, если не рассматривать простые случаи, без хорошего статического анализа чёрт ногу сломит.
-1
Утрируете.

Статическая типизация не нужна, достаточно, чтобы генерация stray undefined вообще не выполнилась, тогда и это «Cannot read property 'f' of undefined» не возникнет.
Я не идеализирую тот же Питон, но количество таких WTF в нём в разы меньше именно за счёт того, что есть возможности избежать появления всех подобных null и undefined как можно раньше, а заодно просто не пускать складывать паровозы с апельсинами. Примерно то же, да, даёт TypeScript — но не сам по себе JavaScript.

Статическая типизация или хотя бы высококачественный статический анализ с учётом type hints и автовыводом, где возможен и хинты не заданы — да, ещё лучше. Я подсчитал недавно, что наш толстый проект на Python по сравнению с тем, что он был бы написан на Java или аналоге, требует раза в 2 больше времени разработки просто за счёт того, что проблемы типизации ловятся на компиляции (а при правильном использовании IDE будет ещё больше разницы). Если что-то ещё более современное — эффект был бы ещё выше.
0

Может помочь во многих случаях строгая типизация, пускай и динамическая, при попытке присвоить not-null де-факто значению null или undefined значение. А статическая нестрогая такие ошибки, как видно на примере C/C++, не ловит обычно.

0
Чем больше «странного» и «дурацкого» кода является валидным и как-то работает и что-то таки делает — тем сложнее попасть в узкое подмножество «хорошего» кода…

Ну а разве процент «хорошего» кода на выхлопе — не хороший показатель качества языка?
Если отбросить вопрос ниши и другие качественные характеристики (например скорость исполнения)

Языки (как и вообще практически что угодно) нельзя сравнивать в целом, однако можно пробовать сравнить по критериям
Скорость выполнения — один из них (и тут JS не сильно блещет)
Скорость написания — другой фактор. И тут С++ часто уступает конкурентам
Простота найма специалистов — еще один. И тут JS довольно на высоте
Ниша — это уже не совсем качественный показатель… тут все же скорее вопрос категории применимости
Качество кода на выходе — еще один показатель, и, как мне кажется, у JS он не на самом хорошем месте

так можно еще много критериев придумывать. просто не стоит забывать что язык — лишь инструмент, и нужно понимать и принимать все его плюсы и минусы
+1
Те кто минусанул — я то не против, все честно, но можно пояснить в комментах в чем причина?
Дискутировать очень полезно, так расширяется кругозор))

Спасибо
+1
много процентов разработчиков

как по мне количество тут не столь важно, как качество
можете побить меня палками, но я не считаю эффективным вести обсуждения с девелоперами которые писали всю жизнь на одном языке программирования (или только на одном типе языков) о том плох или хорош JS. И опять же — я в принципе не считаю такое обсуждение логичным, т.к. у JS есть своя ниша которую он успешно закрывает

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

За себя могу сказать что я несколько лет писал на Java, еще несколько — Objective-C, пару лет на JS и на ActionScript
По мелочи на asm, C++, Perl, Python, Go, C#, bash
Также уже много лет варюсь между тех миром и миром бизнеса, потому могу сказать что именно бизнес двигает IT-рынок в сторону таких решений как node.js. Программисты все меньше влияют на развитие IT

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

0
Программисты все меньше влияют на развитие IT

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

0
И да и нет. Мне, как человеку все же больше техническому чем бизнес, это весьма печально и я полностью согласен что бизнес-люди не могут строить долгосрочные эффективные планы на развитие технологий. С другой стороны (и это уже во мне говорит моя бизнес-сторона) — технологии это лишь инструменты, а значит должны подстраиваться под тех кто их использует и под те задачи которые перед ними ставят. Без бизнеса развитие технологий было бы крайне медленным и нацеленным на гос.сектор (а тогда пути развития технологии могли бы быть не менее прискорбными, чего только запрос «выдать ключи» от ассиметричных алгоритмов шифрования стоят)
Как и все в мире тут есть несколько сил движущих в разные стороны но тем не менее их вектор идет на общее развитие. Как по мне то лучшее что мы, как технари, можем сделать — это стараться хорошо разбираться в большом количестве технологий (читай «инструментов») и понимать сильные и слабые стороны каждой (понимать и принимать, а не хейтить или бездумно защищать). Тогда в те моменты когда от нас что-то зависит мы можем применять подходяющую технологию в подходящем месте. Например заранее планировать переход на новую архитектуру и технологию при достижении числа юзеров выше X, или числа запросов к серверу выше Y, или к базе выше Z… короче планировать заранее и стараться доносить такую потребность до бизнеса. В общем искать взаимовыгодные компромисы

Например Slack — я четко понимаю что решение написать единую платфрому на JS и для десктопа использовать Electron было вполне неплохим решением на начальных этапах. Однако сейчас у них столько денег что они могут себе позволить написать и поддерживать нативного клиента, что значительно уменьшило бы его нагрузку, перестало жрать батарею а также хоть немного ускорило бы инициацию звонков
0
А я вот считаю, что бизнесу не должно быть никакого дела, какими инструментами пользуются исполнители. Насколько я понимаю, почти никто не контролирует, каким набором ключей сантехник будет затягивать гайки — и это нормально, так и должно быть.

Впрочем, здесь проблема больше не в том, что кто-то пытается продвигать неподходящие под задачу инструменты — здесь скорее ситуация, когда сначала для того, чтобы занять рынок, бизнесмены всегда ставят жесткие сроки, и в результате что бы ни писали программисты — получится не более чем прототип. А как только рынок занят — бизнес перестаёт шевелиться, открывает пасть и почивает на лаврах, а на исправление прототипа средств не выделяет — мол, работает — не трожь. На UX, фактически, просто всем наплевать — но и пользователи тоже хороши, так как очень мало голосуют ногами к конкурентам (отчасти ещё и потому, что у конкурентов зачастую тот же прототип, но ещё хуже, ведь недаром они не на первом месте). Круговорот, в общем.
0
А я вот считаю, что бизнесу не должно быть никакого дела, какими инструментами пользуются исполнители.

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


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

0
Пусть даже материалы — в этом случае, в аналогии с сантехникой, фокус с гаечных ключей смещается на сами гайки. Тут по традиции bikeshedding'а находится уже намного больше желающих подсказать, но станете ли вы сильно возражать против идеи, что на самом деле требования от бизнеса к программисту лучше выразить на намного более высоком уровне, чем список из «одобренных технологий», который программистам передаёт человек, для которого это всё зачастую пустой набор символов сам по себе, зато он слышал, что у соседей тоже гайки с напылением сплава цинка с вольфрамом и изнутри по всей длине труб идёт тонкая полоса из серебра, для очистки воды от микробов?

Что до стоимости будущей поддержки — даже самим программистам оценить зачастую нереально, но вот конкретно по этому параметру большинство ныне модных технологий будут не на первых местах, т.к. в первую очередь они заточены на уменьшение Time To Market, а после хоть трава не расти. Отсюда и вытекает мой тезис выше, что бизнес на самом деле™ не волнует будущая поддержка и всё из этой оперы.
0
Есть такая должность как тех.директор, или CTO/CIO
Он является связующим звеном между бизнесом и технологиями. Бизнесу в принципе все равно, лишь бы тех.решения потом не вызвали проблем (любых). А вот тех.лид или тех.директор как раз и заботится часто о том чтобы технологии потом не создали бизнесу проблем (из-за скейлинга, из-за нагрузок, из-за персонала, из-за лицензий или еще чего)
Т.е. бизнес это не волнует пока это не создает проблем :)
0
все однозначно согласны, что они плохие и лучше бы их не было?
Так не бывает. У любого, самого идиотского решения, даже того, про которое сам автор признал, что они идиотское и хорошо бы его исправить — всегда находятся защитники. Стокгольмский синдром во всей красе.

Например, typeof null на практике сильно не мешает, а прототипы или динамическая типизация — вы не сможете сказать, что все разработчики однозначно против.
Тут есть некоторая проблема в том, что идеальных языков вообще не бывает, а их практическая применимость — очень сильно зависит от задач. И, в частности, количество попыток прикрутить в JavaScript'у статическую типизацию показываает, что режим тяп-ляп-и-в-продакшн уже многих не устраивает… а поделать с этим ничего нельзя.
0
Я разумеется не имею в виду, что все 100% разработчиков должны быть согласны, а какой-нибудь несогласный стажёр всё ломает. Я имею в виду, что не нужно выдавать субъективность за объективность, а открытые вопросы за решённые. Вы не можете выдать за объективную истину «пробелы лучше табов» или «статическая типизация лучше динамической» или «прототипы лучше классов», потому что примерно 50% (или 30% или 20% или 10%) с вами не согласятся, и однозначного ответа, почему одно лучше другого, не найдено.

количество попыток прикрутить в JavaScript'у статическую типизацию показываает, что режим тяп-ляп-и-в-продакшн уже многих не устраивает

Ага, а количество попыток затащить JS на сервер показывает, что все остальные серверные языки уже многих не устраивают.

Ничего это не показывает. Только количество разработчиков, которые попытались пересесть со статически типизированных языков на js, и ощутили, что им не нравится писать без типов. Это даже легко доказать: если человек изучает программирование начиная с js, ему и в голову не придёт тащить в него типы :)
0
Ага, а количество попыток затащить JS на сервер показывает, что все остальные серверные языки уже многих не устраивают.
Именно так. Несмотря на то, что «не устраивают» они, по большому счёту, ровно по одному показатели: наличию достаточно дешёвой рабочей силы — это именно так.

Это даже легко доказать: если человек изучает программирование начиная с js, ему и в голову не придёт тащить в него типы :)
Я видел массу контр-примеров. Обычно идёт «путешествие с возвратом»: вначале человек попадает, так или иначе, в проект, где тысячи программистов пишут миллионы строк кода (ни одного такого успешного проекта, написанного на языке с динамической типизацией мне лично не известно… думаю что они всё же есть — но их очень мало), а потом уже возвращается в JavaScript с осознанием того что есть статическая типизация и для чего она нужна…
+1
JS на сервер затащили во многом потому что нужна рабочая сила, а JS на подъеме и разработчиков проще найти или научить. Сейчас очень много молодых стартапов живущих по циклу: написать PoC, получить первые инвестиции, на них написать MVP/MSP, получить инвестиций побольше, расширить команду и писать конечный продукт
В таком подходе, а также в современных Agile и т.п. на начальных этапах JS выглядит как неплохое бизнс-решение. А если те же кодеры смогут писать еще и сервер = то вообще шик!!!
0
Тогда порог вхождения стал бы ещё ниже, и говнокода стало бы ещё больше.
0
Про JS надо помнить эпоху когда он создавался — тогда никто, даже в смелых мечтах (или унылых кошмарах) не мог бы представить то в чем мы сейчас варимся :)
Представить-то как раз могли. Более того — JavaScript им ровно для этого и понадобился. Чтобы можно было использовать один язык и на клиенте и на сервере (в NAS). Вот только клиент взлетел, сервер умер… и пришлось ждать ещё очень долго пока совсем другие люди первоначальную идею реализуют…
0
Методы хранения строк в памяти ортогональны этим (вполне реальным и грустным) проблемам JavaScript.

Когда-то такое же разделение строк было в Java. В 8-й версии решили эту «оптимизацию» выкинуть, видимо, по тем же причинам.
0
Откровенно говоря, понять что имеется в виду под «средств для веб-программирования» очень сложно. Особенно дико этот коммент смотрится в топике про особенности оптимизаций v8 :)
+2
Ну а что можно требовать от комментатора, который пришел сюда без статьи, четкого понимания современных технологий и разработки.
В итоге имеем то, что имеем — пустобрешие.
0
Это не косяк же, а особенность поведения, вот если бы в один прогон было 30 мегов, а другой 15 байт, то тогда да, был косяк.
+1

Я предлагаю использовать такой способ:


function clearString4(str) {
  return (str + '\0').substr(0, str.length)
}

Не совсем понятно почему в clearString3() строка занимает в 2 раза больше памяти. По сути мы создаем новую строку с пробелом вначале. Когда делаем slice(1), то должна вернуться ссылка на созданную строку (' ' + str) со смещенным началом на 1 символ. Т.е. (' ' + str).splice(1) должен ссылаться на (' ' + str) и оверхед должен быть в 1 символ.
P.S. У меня почему-то версия c .substr() всегда чуть быстрее чем версия с .slice(). Странно почему так получается… В данном случае они должны быть идентичны.

+4
Я бы рекомендовал всё-таки разбирать строку на символы и «собирать» обратно.

Все вот эти «быстрые» способы — это игра с огнём. Там вверху приведен случай, когда один «быстрый» алгоритм очистки работал-работал, а потом вдруг перестал… И тут то же самое тут может случится.

На сервере же, где вы всё можете контролировать, можно просто «вытащить» соответствующую низкоуровневую функцию из V8 и вызывать её…
0
И однажды оптимизатор начнёт выбрасывать конструкции split+join, как не меняющие строку.
+1
Это всё-таки очень спицифическая оптимизация. Такой код почти невозможно написать случайно — так что разработчики будут понимать, что это «очистка» строки.

Хотя было бы неплохо где-нибудь хотя бы оффициальную рекомендацию увидеть. Типа «делайте так — и мы гарантируем, что это не выбросят». Это да.
+1
Оно, увы, специфично не для v8, а конкретно для Node.js.

В браузере его не применить…
0
Или сделать библиотечную функцию, которая гарантированно будет всегда работать.
+1
Ого, интересно, спасибо!

А что, если просто написать функцию, которая будет побайтово копировать нужные символы из строки?
+2
Будет медленнее, чем встроенные функции. Но можете попробовать, напишите свою clearString() — протестируем, сравним скорость. Собственно, .split('').join('') — это и есть побайтовое копирование.
+1
Я бы не был так уверен, тот же '.split('').join('')' создает массив, что уже нагружает память, и при больших обьемах данных, может уступать собственным решениям, вот к примеру быстро написанная функция показывает лучший результат при больших обьемах:

var stringCopy = function(str) {
  var strCopy = '';

  for (var i = 0; i !== str.length; i++) {
    strCopy += String.fromCharCode(str.charCodeAt(i));
  }

  return strCopy;
};

console.time('Timer');
for (var i = 0; i !== 100000; i++) {
  'Some text'.split('').join('');
}
console.timeEnd('Timer'); // Timer: 55.083984375ms


console.time('Timer');
for (var i = 0; i !== 100000; i++) {
  stringCopy('Some text');
}
console.timeEnd('Timer'); // Timer: 52.052978515625ms


А если увеличить до 1 миллиона, то цифра сильно начнет перевешивать не в сторону встроенных функций:

Timer: 523.544189453125ms
Timer: 480.338623046875ms
0
Интересно. Но всё же нет. Я добавил вашу функцию к своим тестам и вот что вышло:
console.log(Test(test_arr,clearString));
console.log(Test(test_arr,stringCopy));
console.log(Test(test_arr,clearString2));
console.log(Test(test_arr,clearString3));
752.80500005465
1093.955000047572
309.3099999241531
262.0650000171736

Несколько прогонов — тот же результат. И у меня в цикле как раз миллион итераций. То есть ваша функцию всё же медленнее. По памяти предлагаю не считать временный массив (если он вообще создаётся), который GC обязательно удалит — это не тот расход памяти, о котором заявлено в статье в качестве проблемы.

Не знаю точно, почему у вас другие результаты, но хочу обратить ваше внимание на способ тестирования. Например, компилятор оптимизатор вот это в теории вообще может выкинуть:
'Some text'.split('').join('');

Ведь результат нигде не используется.

К тому же у вас искусственная строка, а нам для чистоты эксперимента нужно чистить именно «грязные» строки.

Поэтому в моих тестах я из большой строки нарезаю много маленьких, которые ссылаются по разным адресам на большую. Это помешает оптимизатору мешать тестированию, какой бы он ни был. Далее, в самом цикле результат мы не выкидываем сразу, а обязательно куда-то сохраняем, это тоже мешает потенциальному оптимизатору искажать результаты. Я не эксперт в V8, а просто рассчитываю на разумный подход оптимизатора. Возможно, я что-то не учёл, тогда прошу обратить на это моё внимание.
+1
Попробовал ваш тест несколько раз, и у меня получились другие результаты:

console.log(Test(test_arr,clearString)); // 868.5000000987202
console.log(Test(test_arr,stringCopy)); // 493.80000005476177
console.log(Test(test_arr,clearString2)); // 435.4999999050051
console.log(Test(test_arr,clearString3)); // 282.60000003501773


Что то мне подсказывает что это зависит от браузера (пользуюсь оперой), так как в той же лисе:

console.log(Test(test_arr,clearString)); // 210
console.log(Test(test_arr,stringCopy)); // 2077
console.log(Test(test_arr,clearString2)); // 632
console.log(Test(test_arr,clearString3)); // 185
0
Что то мне подсказывает что лиса откидывает '.split('').join('')', или как то хитро оптимизирует, но могу ошибаться, так как попробовал ту же '[...str].join('')' и она выполняеться уже порядка 700мс.
0
Может посимвольное, а не побайтное? 1 символ в том же юникоде будет занимать больше байта.
+1
Формально вы правы, поэтому статью я начал с «30 байт» на 15 символов, но реально в Chrome строка из 15 млн. символов «z» занимает 15 мегабайт, судя по диспетчеру Chrome. Плюс я считаю хорошим тоном отвечать на языке того, кто задал вопрос, если это не меняет суть ответа.
0
Ну, строка из 15 миллионов символов «z» и должна занимать 15 мегабайт — ASCII же в UTF-8 умещается в один байт (или в JS строка — это UTF-16?)
+1
или в JS строка — это UTF-16
Именно так. Как обычно: изначально думали уложиться в UCS-2, не получилось создали идиотский вариант, который одновременно и медленный и много памати жрёт… возможно V8 «втайне от пользователя» использует UTF-8?
+1
Насколько я помню, так и есть, V8 автоматически переключается между Latin-1 и UTF-16 в качестве внутреннего представления строк.
(Пруфов, увы, не будет — не могу сходу найти.)
0
Я правильно понял, что join эту проблему решает?

А как дела с интерполяцией?
+11

Странно что нет стандартного метода который принудительно отвязывает строку от родителей. Так как по моему проблема всех самописных решений их ненадежность при смене/обновлении платформы.

+4
нет стандартного метода который принудительно отвязывает строку от родителей

Это не часть спеки языка, это делали реализиции в конкретном движке. И в v8 есть свой персональный метод %FlattenString(s), правда для этого нужно включить нативный синтаксис --no-allow-natives-syntax.

+1
Смутила эта строка
var v8 = require('v' + '8')

Не подскажете для чего это?
0
Чтобы в случае, если эта библиотека случайно окажется собрана каким-нибудь webpack — он не пытался искать и загружать несуществующий модуль во время сборки, а выкинул исключение уже при выполнении.
0
Хм, интересный способ, не встречал раньше. Спасибо за объяснение.
0

Насколько я понимаю, %FlattenString (как и использующая его flatstr) не про то и здесь она не поможет — она "уплощает" cons strings (строки, образованные в результате конкатенации), но sliced strings она возвращает без изменений (по сути своей её реализация не слишком сложна).


Собственно, как уже написал ниже flapenguin — применительно к описанной статье проблеме %FlattenString поможет только если сначала к "проблемной" sliced string что-нибудь прибавить (и получится тот самый (' ' + str).slice(1)).

+9
Удивительно, но в серверной Java (ещё в 8 версии) отказались от всех этих оптимизаций и сделали копирование данных при выделении подстрок. Кажется в JS придут туда же, но не сразу.
+1
Вот спорное решение как по-мне. Лучше бы хотя бы оставили возможность программисту влиять на это.
0
Да особо смысла нет. char[] + offset + length хватит для обработки чего угодно, результат уже можно упаковать в String.
+3

А в C++ есть std::string, const std::string &, std::string && и std::string_view.
И сиди думай, который где использовать

+4
судя по описанию бага, такая течь может возникнуть при использовании
  • concatenating with empty string
  • trim()
  • slice()
  • match()
  • search()
  • replace() with no match
  • split()
  • substr()
  • substring()
  • toString()
  • valueOf()


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

PS: кроме того, этот перечень, как и описание бага было составлено в 2013 — и с тех пор, возможно, часть утечек перестала воспроизводиться.
+1
Странно, я постоянно работают с мини-парсерами, которые используют кучу этих функций и проблем с памятью не наблюдал.
Тут надо понимать, что этот способ даже экономит память, если строки переиспользуются.

Проблемы возникают только когда вы берёте кусок большой строки, а потом про большую «забываете» (а GC-то помнит!).
+2
Простейший кейс, когда мы точно знаем, что в строке число, либо нам нужно получить число:
str = str - 0;


А чем такая конструкция хуже? Или нет разницы?
str = +str;
-4

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

-5
Ну тогда parseFloat, если нужны дробные части (почти никогда не нужны).
+1
Я думаю причины скорее в семантике. parseInt намекает, что мы хотим попарсить строку и извлечь намбер, и нам ок, если 10f5 превратится в 10. Number же честно пытается 1 в 1 сделать перевод и вернет NaN в случае ошибки, что больше соответствует нашему желанию и ожиданиям.
0

А я где-то говорил, что хуже? Я как раз против конструкций вида str = +str; и всегда за более явные аналоги, когда скорость не имеет особого значения, в том числе и Number(str).

0

Я к тому, что +strи str - 0 одинаково плохи, если мы наносекунды не считаем. А если считаем, то +str должна быть быстрее по идее.

+1

Полностью поддерживаю. +str действительно быстрее, но, к примеру, я сейчас работаю в разношерстной команде, и из 10 разработчиков 8 полезут в гугл, чтобы понять, что все-таки происходит. К слову, эти побитовые хаки даже опытных разработчиков вводят в ступор на момент.

0
Забавно, что +нечто встречается в самых разных языках. Вот вариант из C++.

P.S. Вопрос какой язык безумнее — тот, где имеет смысл выражение {} - [] или же тот, где +[](){} отличается от [](){} ещё открыт…
0
Формально отличий нет. Первым делом происходит автоматическое приведение к числу, которое и творит всю магию, то есть получаем либо число, либо хотя бы NaN. А дальше уже вычисляется сама операция, которая ничего не делает.

Из той же серии:
str = str * 1;

Экономим символы в коде:
str-=0;
str*=1;
-27
Это вечная проблема, когда сайт делает «чудо-профи»… он же — дилетант
+20
Этот «дилетант» прочёл всю спецификацию JavaScript всех версий от корки до корки. И вообще, у него может быть Firefox, где проблемы нет. Но заказчик всё же позвонил этому «горе-профи».
0
вот тут еще предлагают
var string_copy = original_string.slice(0);
const newStr = Object.assign("", myStr);
var nName = String(name);
0
В Firefox пример с утечкой слегка работает — объем потребляемой памяти возрастает где-то на 200-250мб, а потом приходит GC.
+5
Это не утечка, так и должно быть. Мусор сначала накапливается, а не чистится сразу. И если наращивать память по 15Мб в секунду, то где-то несколько сотен мегабайт и должно быть в пике. С применением clearString() будет та же картина и в Chrome.
-3
Автор написал, что ссылки на родительские строки — особенность V8 и, если продолжать мысль, то в FF это проявляться вообще не должно. Но оно проявляется, просто GC приходит секунд через 20 после запуска примера MemoryLeak().

В любом случае, и в FF иногда стоит использовать clearString(), чтобы не съедать лишнюю память в ожидании сборщика мусора.
+2

Исходные строки по 15 мегабайт создаются хоть в FF, хоть в Хроме. Разница в том, что в FF они как и любые созданные объекты, освобождаются, когда приходит сборщик мусора (но не раньше, поэтому успевают накопиться 200-250мб), а в Хроме нет, потому что на них остаются формально живые ссылки.


Если в будете использовать в FF clearString(), это никак не поможет, потому что исходные строки по 15 мегабайт все еще будут создаваться.

0
Да, действительно, в FF никак не повлияло. А вот в Chrome при использовании (' ' + str).slice(1); память вообще перестала сколько-нибудь заметно увеличиваться.
+4

В Firefox есть точно та же проблема, только максимальная "короткая" строка не 12 байт, как в Chrome, а 23 / 11 символов (для Latin1 / двухбайтных — см. раздел inline strings по ссылке). На 24 символах проблема воспроизводится. Кстати, следует учитывать, что выбор режима между Latin1 и двухбайтным выполняется исключительно по тому, в каком режиме была изначальная строка — то есть если там был Юникод за пределами Latin1, то и все подстроки будут оставаться ссылками по лимиту в 11 символов, а не 23.


Что ещё веселее, если внимательно посмотреть на результаты теста в JSPerf на Firefox, то окажется, что "решение" через (' ' + str).slice(1) работает за время, не зависящее от длины подстроки. Дело в том, что в Firefox эта операция выполняется через строку типа Rope и не приводит к "отсоединению" от базовой строки, то есть не выполняет свою задачу. Лишний раз видим, что самые быстрые решения зачастую оказываются менее надёжными.

0
А вот это интересно, спасибо. Проблема воспроизводится, если вырезать 25 символов. И это всё меняет. Непонятно только, почему при перезагрузке вкладки или при переходе на другой сайт память не очищается. Только если закрыть вкладку — убивается соответствующий процесс, а вместе с ним и память.
+3
Еще интересный момент, как именно крашится chrome, он не анализирует есть ли утечка памяти, а тупо при превышении аллоцированной памяти в два гига на странице пишет что ВОЗМОЖНО есть утечка памяти и крашит страницу принудительно, не давая возможности что то предпринять. Соответственно держа память под контролем и просто работая с большими объемами, легко схлопотать этот краш ни за что. Edge, firefox, opera, vivaldi при этом справляются.
0
Потому что chrome в консоли пишет maybe. Т.е. сам показывает что он не уверен, но на всякий закрашит страницу.
-1
а как он может быть уверен? если программа много отожрала памяти, это ещё не значит, что это неконтролируемый объем памяти.
0
Вот об этом и разговор, я хочу работать с 4 гигабайтами данных на клиенте, к примеру, а хром решает что это утечка, и крашит страницу, не спросив даже у пользователя.
+2

Хех. Буквально сегодня на одном рабочем сайте внезапно обнаружил колоссальный расход памяти, при случае надо будет глянуть, оно или не оно :)

+1
Спасибо, что разложили по полочкам. Видел ваш вопрос на Тостере и принимал участье в комментариях. Оказывается иногда полезно углубиться в то как работает V8 внутри. Постоянно надо быть начеку =)
+1
Попробовал в Node.js 10.15.3 такой вариант:
function clearString4(str) {
  //Используем Buffer в Node.js
  //По-умолчанию используется 'utf-8'
  return Buffer.from(str).toString();
}

Результат:
763.9701189994812
567.9718199996278
218.58974299952388
704.1628979993984 // Buffer.from


0

А попробуйте еще для верности UTF-16. Это внутреннее представление строк в JS (обязательное условие, прописанное в спеке) и возможно будет сильно быстрее.

-1

ascii самая быстрая кодировка для буферов, на нескольких проектах убеждался. А всё потому, что там простое конвертирование 1 в 1 без всякой ерудны юникодной. А вообще это дикость конечно, из строки, потом в буфер, потом обратно...

0

ascii не может быть кодировкой для произвольной строки, только для специфических строк. Диапазон ascii 128 значений, диапазон внутреннего представления строк в JS — 65 тысяч.

0

Точно. Упустил этот момент. В любом случае это глупость гонять строку через буфер. Создание типизированого массива достаточно дорогое удовольствие. А тем более конвертирование его в строку.

0
А вообще это дикость конечно, из строки, потом в буфер, потом обратно...

Никто и не спорит. Но так же является дикостью то, что V8 не чистит «большую» строку, когда на неё нет прямых ссылок и не копирует необходимые чанки в подстроки. Если не нужно парсить мегабайты строк в секунду — то схема «строка -> буфер -> строка» является, возможно, единственным гарантированным способом в Node.js именно скопировать строку без всяких приватных функций V8 и прочих хаков.
+2

Проверил на node v8.10.0 и результат отличается в зависимости от содержимого строки.


Если строка состоит из ASCII символов, то кодирование в utf8 и utf16 занимает примерно одно время:


> str = "w".repeat(15).repeat(1024);

> start = new Date(); for(var i = 0; i < 10000; i++) { Buffer.from(str, 'utf8').toString('utf8') }; (new Date() - start)
319

> start = new Date(); for(var i = 0; i < 10000; i++) { Buffer.from(str, 'utf16le').toString('utf16le') }; (new Date() - start)
330

Но если диапазон значений шире, то utf8 замедляется в 8 раз, а utf16 ускоряется в три. В результате utf16 быстрее в 26 раз.


> str = "ш".repeat(15).repeat(1024);

> start = new Date(); for(var i = 0; i < 10000; i++) { Buffer.from(str, 'utf8').toString('utf8') }; (new Date() - start)
2615

> start = new Date(); for(var i = 0; i < 10000; i++) { Buffer.from(str, 'utf16le').toString('utf16le') }; (new Date() - start)
101

Очевидно, что в V8 внутреннее представление строки меняется в зависимости от его содержимого. В общем случае utf16 предпочтительнее.

+1
Попробовал с UTF-16
function clearString5(str) {
  return Buffer.from(str, 'utf16le').toString('utf16le');
}

Получилось чуть быстрее:
711.1723950000014 // utf-8
635.6162950000726 // utf-16

Ещё попробовал библиотеку flatstr. Она показывает существенный выигрыш в скорости:
53.19643200002611 // flatstr
-8
function MemoryLeak() {
  let huge = "x".repeat(15).repeat(1024).repeat(1024); // 15МБ строка
  let small = huge.substr(0,15); //Маленький кусочек
  return small;
}
Если известно, что huge кэшируется, почему не сделать явную очистку?
function MemoryMaybeNotLeak() {
  let huge = "x".repeat(15).repeat(1024).repeat(1024); // 15МБ строка
  let small = huge.substr(0,15); //Маленький кусочек
  huge = undefined; // Удаляем ненужную более огромную строку
  return small; // Возвращаем маленький кусочек
}
+3
Почему не сделать явную очистку?
huge = undefined;
Потому что это не поможет. Ссылка на большую строку останется в small.
-8
А где именно? В привязанном объекте типа [[Scope]] для функции?
+6
Нет, ссылка находится в самой строке small. Об этом вся статья.
-5
Хотите сказать, что я могу написать что-то вроде small.parent; и получить все 15 метров huge в консоль?
+4
Этого нет в спецификации JavaScript, так что странно надеяться на такое. Даже если сможете, это будет возможностью конкретной среды исполнения. Для Google Chrome я не знаю способов. Но факт остаётся фактом — в V8 у строк есть скрытая ссылка на «родителя» или нескольких «родителей».
+4

Нет, так написать вы не можете. То, что ссылка остается, не означает, что она доступна где-то из пользовательского кода. Точно так же недоступны ссылки на дефолтные аргументы и локальные константы функций, на переменные замыканий, на много чего еще, но они все равно есть.

-7
О чём и был мой вопрос выше. «Дефолтные аргументы и локальные константы функций» и «переменные замыканий» — это скрытый объект [[Scope]], который есть у функции.
Но меня заверяют, что такого скоупа у строки нет, а ссылка вдруг где-то есть. Мол, "в самой строке". Хотя строка — это иммутабельный инстанс типа String в виде массива байтов со списочным доступом к каждому. В нём технически не может быть никакой ссылки.
+2
Но меня заверяют, что такого скоупа у строки нет

Помилуйте, где вас в чем-то подобном заверяют?


а ссылка вдруг где-то есть. Мол, "в самой строке"

Но так и есть.


Хотя строка — это иммутабельный инстанс типа String в виде массива байтов со списочным доступом к каждому. В нём технически не может быть никакой ссылки.

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

-1
Помилуйте, где вас в чем-то подобном заверяют?
Здесь:
dollar сегодня в 15:58
Нет

Но так и есть.
Вынужден повторить вопрос
А где именно?

Ну если вы лучше знаете, как что устроено
Знал бы — не спрашивал бы.
Но не знаю и спрашиваю.
Знаю только, как устроена строка по спеке.
+2
Здесь:

Вы очень своеобразно интерпретируете свой вопрос и этот ответ. Вас заверяют, что «ссылка не хранится в привязанном объекте типа [[Scope]] для функции», но вы почему-то прочитали это как «такого скоупа у строки нет». Одно с другим логически никак не связано.


А где именно?

А что вы рассчитываете получить в ответ? Описание структуры на Си? Ну посмотрите в исходниках движка. Какая разница как, сути это не меняет: строка small содержит внутри себя ссылку на строку huge.


Знаю только, как устроена строка по спеке.

Вы путаете описание интерфейса (спека) с тем, как устроено внутри (реализация).

-6
Вы очень своеобразно интерпретируете свой вопрос и этот ответ.
Интересно, а как Вы узнали, как я интерпретирую свой вопрос? На Хабре завелись телепаты? Видимо, один из телепатов и подумал, что под «объектом типа [[Scope]]» я разумею что-то специфицированное да и влепил мне минус за незнание спеки…
А я всего лишь хотел узнать, привязан ли к строке хоть какой-нибудь объект с внутренними ссылками, пусть даже где-то в недрах V8. Помимо String.prototype.
Кстати, надо заметить, что хотя в статье речь о строках (и это вроде как подтверждается скриншотами консоли), во всех примерах эти строки создаются в функциях, что даёт основание утверждать, что в [[Scope]] этих функций huge также может сохраняться.
+4
Минусов вам влепили за обсолютное незнание предмета. Ну всё равно как если бы учитель русского языка средней школы начал обсуждать JavaScript и заводить баги на тему того, что 'ё' < 'ж' — это false.

Точно так же как учитель русского языка не может себе представить, что буквы могут быть отсортированы как-то иначе, чем в русском алфивите — вы не можете представить себе, что в природе могут существовать сущности, не отражённые в JavaScript.

Обсуждать что-либо в обоих случаях бесполезно. И механизм голосования на Хабре, собственно, и предназначен для того, чтобы от подобным горе-спорщиков избавиться.
-3
2 khim
Не знаю, у кого тут "обсолютное" незнание, я уже 10 лет на JS пишу.
И прекрасно знаю, что в JS бывают сущности, не отображённые в спеке, хотя бы document и window в браузере или тип fxobj в PDF.
────────────────────
2 vassabi
По спецификации String всё-таки тип, несахарных классов в JS нет.
А в реализации String это функция-конструктор (как и любая другая функция, объявляемая с сахарным ключевым словом class).
И это не объекты String ведут себя как строки, а строки — представители простого типа — ведут себя как объекты-обёртки, например, при вызове методов.
массив байтов — это массив байтов (и его размер может быть больше, чем размер строки в нем).
Зависит от того, в каких единицах считать строку. Если её считать в байтах, размер будет одинаковый. Если в символах Уникода, то, понятно, число уменьшится. А уж если кто-то решит считать строку в тех эмодзи, что отображаются на странице в браузере, а массив байтов: в тех байтах, что заключают в себе всю строку в имплементации (30 MiB согласно статье)…
────────────────────
2 all
Я с самого начала спрашивал про реализацию (где и как), а не про спецификацию.
Но минусовать-то, конечно, проще, чем объяснять. И да, я знаю про Великое Правило «помянул минус — получи минус». Но мне на него как-то с Пизанской башни.
Всем читающим и пишущим желаю писать на хорошем и правильном русском языке и помнить, что ружьё бывает разряжённое, а воздух разрежённым, а не наоборот.
+4

Как раз window и document в спеке есть, только смотреть надо не спеку на javascript, а спеку на HTML.


Я с самого начала спрашивал про реализацию (где и как), а не про спецификацию.

Так вам с самого начала и объяснили. А дальше вы встали в позицию "не верю, так не бывает"...

-1
Я ответил, что "так не бывает" именно по спецификации, в которой строка (которая не объект) есть «ordered sequence of zero or more 16-bit unsigned integer values (“elements”) up to a maximum length of 253-1 elements». В это определение ссылки на родителей "не помещаются".
Почему и спросил, где они и как реализованы.
+2

Еще раз, вы путаете спецификацию (интерфейс) и реализацию. Спецификация описывает, как должен себя вести тот или иной объект, а не как он должен быть устроен. Указанное требование «ordered sequence of zero or more …» никак не нарушается данной реализацией.

+1
Я ответил, что "так не бывает"

Вы живете в каком-то выдуманном мире. Вам объясняют, как это устроено, вы отвечаете «так не бывает». Ваше дело, конечно.

-3
А «так не бывает» это и не мои слова, я сам их зацитировал.
И просил я именно объяснить, «как устроено» и каким образом помещаются в строку ссылки минуя «ordered sequence of elements».
Как вся фигня, к которой нет доступа в функциях, помещается в функцию, понятно: скрытый объект.
Как вся фигня, к которой нет доступа в строках, помещается в строку:? Скрытое что?
+1
Как вся фигня, к которой нет доступа в строках, помещается в строку?

Ну подождите, а как в строку помещается вся фигня, к которой есть доступ в строках? Вы это понимаете? Там не скрытое что?


Как вся фигня, к которой нет доступа в функциях, помещается в функцию, понятно: скрытый объект.

Удивительно, что вам это понятно. Там же объект! А какой у него класс? Какие методы? Время жизни? Ваше объяснение, которе вас удовлетворяет для функций, на самом деле не объясняет вообще ничего, о том, как это устроено. Но почему-то точно такое же объяснение для строк (там скрытая ссылка на родительский объект) вас вдруг ставит в тупик.

-3
Ну подождите, а как в строку помещается вся фигня, к которой есть доступ в строках? Вы это понимаете? Там не скрытое что?
Не скрытое то, что по спецификации для конечного пользователя должно выглядеть упорядоченной последовательностью целых беззнаковых 16-битных чисел и методом доступа []. На уровне языка реализации и интерпретации имеем класс для выделения, чтения, записи и освобождения памяти под эти последовательности. Достаточно и необходимо.

точно такое же объяснение для строк (там скрытая ссылка на родительский объект)
О, [[Scope]] уже стал родительским объектом? Или в чём объяснение "точно такое же"? Я же это первым делом спросил: есть там что-то типа [[Scope]] или нет?
0
На уровне языка реализации и интерпретации имеем класс для выделения, чтения, записи и освобождения памяти под эти последовательности. Достаточно и необходимо.
Достаточно, но не необходимо. Более того: в V8 строки, поддерживая иллюзию того, что внутри них просто массив (иначе это всё не соответствовало бы спецификацию) имеют сложную структуру, особенности которой иногда прорываются наружу.

Я же это первым делом спросил: есть там что-то типа [[Scope]] или нет?
Осталось только понять что вы подразумеваете под «чем-то типа [[Scope]]». Ничего явно видимого в Developer Tools нету ибо, в отличие от [[Scope]], строка может менять своё строение «на лету» в то время как вы на неё смотрите, так что смотреть на её внутреннее устройство не остановив JavaScript-мир проблематично. Но, разумеется, внутри V8-строки есть богатый внутренний мир… со своими особенностями.

Собственно вся статья — об этих особенностях.
+7
Вы рассуждаете с точки зрения логики языка JavaScript. Но эта особенность — не часть языка, а часть конкретной системы управления памятью. Эта система вам ничего не «должна». В частности, у вас нет гарантий того, какой объем памяти будет ассоциирован со строкой, и как там вообще всё будет оптимизировано.
+2
строка — это строка,
массив байтов — это массив байтов (и его размер может быть больше, чем размер строки в нем).
а String — это класс, объекты которого ведут себя, как будто они строки. Как они устроены внутри на самом деле, какие внутри них ссылки, байты и массивы — это глубокие детали реализации.
-6
Жду я значит уже минут 10 в мозилле, вклада увеличила аппетит с 375 Мб до ~600.
Нагрузка на ОЗУ конечно выросла, но далеко до краша.
+2
Ещё один чукча-писатель? Статью читать не пробовали?

Конкретно вот это:
Это особенность движка V8, которая позволяет ускорить работу со строками в ущерб, естественно, памяти. То есть это касается Google Chrome и прочих хромиум-браузеров, а также Node.js. Этого уже достаточно, чтобы отнестись серьёзно к этому явлению.


Нафига медитировать на мозиллой, где этой проблемы нет (там свои, другие, проблемы есть)?
+5

Золотые времена IE6Chrome
Писать workaround'ы для кривого runtime, тут хотя бы исходники есть.

+1
Притом полностью забили на браузеры на альтернативных движках. Во времена IE6 хотя бы иногда заботились о других браузерах ((
0
FGJ, все вышеперечисленные хаки, предназначенные для компенсации проблемы в V8, навроде str.split('').join(''), никак не ломают поведение в других браузерах/движках.
0
На знал о такой проблеме, но догадывался. Потому, что в хроме богато ресурсов сайты жрут. У себя на проектах, честно сказать не замечал. Возможно, дело в том, что используется VUE. И там это как-то решается в рективном двигле. Но нужно потратить время и перепроверить это.

Вопрос который у меня возник — я не видел за последнее время ни одного сайта, который сожрал бы память до краша. Я также не видел сайта, который бы жестко тормозил. Да, раньше дело было. Сам с таким сталкивался. Сейчас все получшело. Но… я сталкиваюсь с тормозами когда открыт devtool.

Вопрос №1 — а не может ли такое поведение быть специфичным для режима отладки? Когда по каким-то причинам двигло пытается сохранить побольше инфы для, ну например, профайлера. Буду проверять. Интересно…

Вопрос №2 — не является ли это осмысленным перерасходом памяти для оптимизации, но при этом память начнет высвобождаться в свободные процессорные тики? Т.е. сначала работаем на производительность, затем на оптимизацию. Нужно тоже проверять…
+2
Devtool кэширует тела ответов ajax, из-за чего память процесса (вкладки) в Chrome, естественно, растёт, породой до огромных размеров. Но чтобы анализировать проблемы памяти именно в JS, нужно смотреть на память JS, она называется «JS Heap» или «Память JavaScript».

В Devtool во вкладке памяти предлагается анализировать как раз память JS, так что проблемы нет.

В диспетчере задач Chrome нужно отдельно включить столбик с памятью JS, для этого кликните правой кнопкой по заголовкам и выберите соответствующий пункт:
Скриншот

+2

dollar с учетом того что строка итерируется, то нет нужды ее разбивать через split, можно просто сделать [...string].join('')

+3

Если посмотреть в сорцы то все еще хуже:


В Factory::NewProperSubString где создается подстрока (SlicedString) исходную строку плющат в последовательную строку.


А это значит, что память течет еще сильнее при использовании ConsString'ов:


x = new (function TrackMe() {})()
x.a = 'a'.repeat(100);
x.b = 'b'.repeat(100);
x.concat = x.a.repeat(1000) + x.b.repeat(1000);
x.substr = (x.a.repeat(1000) + x.b.repeat(1000)).substring(80, 120);

На скриншоте ниже видно, что a и b — маленькие няшные ConsString (они же известны как string rope'ы). Строка из 1000 повторений каждой из них — тоже.
Но для подстроки из 40 символов такую же строку из 1000 повторений сплющило, и теперь в памяти лежит 200 кубов ради 40 символов.


0

Еще интересно, что самый быстрый способ (' ' + str).slice(1) — самый быстрый ровно по той же причине почему происходит эта утечка. NewProperSubString насильно плющит строку. Итоговый перерасход памяти — 1 символ.
Остальные варианты создают куда большие промежуточные объекты.

+1
Интересно, что в Java уже сталкивались с такими утечками (особенно в мире Java EE, когда выгрузка приложения из контейнера неожиданно не освобождала часть памяти), и, начиная с версии 1.7.0_06, провели деоптимизацию: String.substring(int, int) не шарит нижележащий массив символов с новой строкой, а создает новый массив для новой подстроки. Но если тебе все-таки очень надо не создавать копию подстроки (например, исходная строка очень длинная, а подстрока включает большую ее часть и нужна лишь на короткое время), и ты понимаешь и принимаешь все возможные риски подобной оптимизации, то предусмотрели обходной маневр: CharBuffer.wrap(str).subSequence(int, int).

По-моему, в JavaScript тоже могли бы пойти по подобному пути, причем, это не требует особых приседаний со стороны движка.
+1
Альтернативой может стать усложнение GC по отношению к строкам — тогда можно сохранить внутреннее представление в виде ссылки на родительский элемент, но при этом ссылка на родителя не должна препятствовать его сборке GC — и при сборке демон должен будет прозрачно заменить ссылку+оффсеты на функционально и логически эквивалентный «настоящий» массив, полученный в результате слайса.

В Java такое сделать проблематично из-за строгости реализации — при таких операциях GC должен иметь эксклюзивный доступ к объекту, чтобы другие потоки не могли увидеть её в неконсистентном состоянии (а они могут, т.к. массив и оффсеты это три операции записи, причём в несинхронизированые поля, которые ещё и синхронизировать нельзя из соображений производительности). Это подразумевает чуть ли не подмену значения по всем ссылкам, которая должна быть полностью незаметна для всего пользовательского кода, и прочие весёлости, вроде следующей из такого требования двойной индирекции в самих указателях. В общем, проще деоптимизировать и вернуться к проблеме если она станет совсем уж невыносимой.

Рискну предположить, что в JS подобные трюки проворачивать всё же проще, так что вдруг у V8 получится.
0

Решили, наверно, не заморачиваться, пока не найдется PoC эксплоита, кладущий целиком ноду.жс, как это было с Java EE контейнерами.

0

Кстати, в java serial gc и parallel gc для old generation умеют выполнять дефрагментацию хипа, останавливая весь мир, так что встроить туда компактификацию строк с заменой нескольких указателей — не очень космическая проблема.

+1
Уметь-то умеют, но инфраструктура в целом сейчас движется к уменьшению пауз GC — так что надеяться на очистку внутри STW решение не очень хорошее, потому что STW в идеале наступать не должен никогда.

Я вроде бы где-то читал, что работа над строками по-прежнему идёт. Вот в 9 версии, к примеру, сделали компактификацию в смысле перекодирования в LATIN-1 если в строке находятся только совместимые символы — это уже должно уменьшить потребление по меньшей мере процентов на 20, иногда на 40. Следующим этапом, вроде бы, должна стать замена эквивалентных char[] в разных строках.
Может быть, когда-нибудь и доживём до триумфального возвращения шаренных массивов. Но однозначно не в виде STW-обработки, и не в ближайшие два-три года. Пусть сначала Coin и Loom закончат.
0

Собственно, если заглянуть в исходный код V8, то в описании класса SlicedString можно в секции "currently missing features" увидеть "truncating sliced string to enable otherwise unneeded parent to be GC'ed".


Подозреваю, что ноги проистекают из всё того же бага #2869 — там это решение (доработка GC) было предложено первым же комментарием (не считая самого описания).
Более того, во втором комментарии упоминается, что разработчики V8 осознавали проблему и пытались это реализовать ещё "два года назад" — т. е. ещё в 2011, но столкнулись с некоторыми, кхм, сложностями ("it could cause an explosion on GC")


Увы, восемь лет прошло, а воз и ныне там.
На мой взгляд, самое неприятное в этой истории даже не сам факт таких утечек памяти, а отсутствие официальных средств для решения проблемы в ручном режиме.
Все эти str.length < 13 ? str : (' ' + str).slice(1) это очень грустно.

-1
Технически это не memory leak, а space leak. Отличается тем, что память всё же можно освободить, удалив строку.
-3
Ну вот, две сотни с немножечкой комментариев в общем-то развили и закрепили тезис, высказанный в начале. JS искусственно раздут, дабы утилизировать молодые умы путем изучения бесконечного обилия тайн и закладок загадок языка, устраняя тем самы потенциальных конкурентов, которые смогли бы придумать что-то более похожее на язык программирования для браузеров.
Only those users with full accounts are able to leave comments. , please.