Управлять вложенными классами и переменными с помощью UglifyJS

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

(function() {
  var root = { api:{}, core:{}, names:{} };

  /* util.js file */
  root.names.SOME_LONG_NAMED_CONST='Angel';

  /* Person.js file */
  root.core.Person = function(name) { this.name = name };

  /* API.js with the functions we want to expose */
  root.api.perform = function(param_for_api) { /* do something */ }

  window.lib_name.perform = root.api.perform;

})();

который уменьшен до не очень минимальной версии

(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();

Я понимаю, что uglify, вероятно, думает, что корневая переменная — это структура данных, которую нужно оставить как есть и нельзя изменить. Есть ли способ позволить UglifyJS изменять вложенные имена в корневом пространстве имен?


person whadar    schedule 02.05.2013    source источник


Ответы (5)


Когда вы сворачиваете Javascript, вы можете изменить только имена переменных, API, ядро ​​​​и имена не являются переменными, а свойствами объекта. Если бы они были изменены минимизатором, вы могли бы получить неожиданные результаты. Что, если в вашем коде вы вызовете

root["api"].perform = function()...

или даже что-то вроде

function doIt(section, method, argument) {
    root[section][method](argument);
}
doIt('api','perform', 101);

Все совершенно законный JS, но минимизатор никогда не мог понять, что происходит.

person Jan Misker    schedule 02.05.2013
comment
Да, я понимаю ваши аргументы. Есть ли что-нибудь еще, что я могу сделать? - person whadar; 02.05.2013
comment
@Jan Developer может решить это, а не uglifyjs. Я могу настроить uglifyjs на это: «не коверкайте строки» или «не трогайте переменные, которые я вам сказал». Я использовал обфускатор, и он без проблем все искажал. - person Inanc Gumus; 02.06.2014

Я пытался использовать --mangle-props UglifyJS2 и могу сказать вам: «это создает беспорядок».

Как кто-то заметил: «Разработчик должен решать, какие свойства искажать, а не uglifyjs».

Я подхожу к проблеме, используя следующие параметры:

--mangle-props
--mangle-regexp="/_$/"

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

Вы просили изменить вложенные имена в корневом пространстве имен. Итак, ваш код:

(function() {
  var root = { api:{}, core:{}, names:{} };

  root.names.SOME_LONG_NAMED_CONST_='Angel';

  root.core.Person_ = function(name) { this.name = name };

  root.api.perform_ = function(param_for_api) {  }

  window.lib_name.perform = root.api.perform;
})();

Это приведет к следующему:

(function() {
    var n = {
        api: {},
        core: {},
        names: {}
    };
    n.names.a = "Angel";
    n.core.b = function(n) {
        this.name = n;
    };
    n.api.c = function(n) {};
    window.lib_name.perform = n.api.c;
})();

Команда: uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js

Если вы хотите изменить первый уровень корневого пространства имен (api, core, names), просто поставьте на них подчеркивание (api_, core_, names_), все в ваших руках;)

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

person Fernando Fabreti    schedule 15.08.2015
comment
Хороший. Я предпочитаю начальные символы подчеркивания (остаток моих дней C++) и коверкаю с /^_/ - person cmroanirgo; 27.11.2017
comment
Понятно, но я сделал это с символами подчеркивания в конце, потому что их легче печатать (дополнение кода работает лучше). - person Fernando Fabreti; 28.11.2017

Помимо точки зрения @JanMisker (которая полностью верна), переписывание свойств небезопасно, поскольку они могут подвергаться воздействию кода, выходящего за рамки минимизации.

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

(function() {
  var root = { api:{}, core:{}, names:{} };
  root.names.SOME_LONG_NAMED_CONST='Angel';
  alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something
})();

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

(function() {
  var a = { b:{}, c:{}, d:{} };
  a.d.e='Angel';
  alert(a.d.e);
})();

Но даже если вы находитесь внутри своей частной области, вы можете получить доступ и, что более важно, назначить переменные из внешней области! Представьте себе это:

(function() {
  var root = { api:{}, core:{}, names:{} };
  root.api.perform = function(param_for_api) { /* do something */ }
  window.lib_name = root.api;
})();

Вы не только выставляете функцию, но и объект с функцией на нем. И функция будет видна из любого места, где видно окно.

Так, например, запись следующего в консоли javascript даст разные результаты с минификацией и без нее:

window.lib_name.perform(asdf);

С минификацией вам придется написать:

window.lib_name.f(asdf);

Или что-то подобное.

Помните, что всегда может быть код вне вашей минификации.

Не так уж важно иметь абсолютный минимальный JS, но если ЭТО так важно по какой-то причине (например, инопланетяне похитили вашу падчерицу, и единственный способ вернуть ее — уменьшить ее до 100 символов или около того), вы может вручную заменить нежелательно длинное имя свойства на более короткое, просто убедитесь, что оно нигде не будет отображаться и не будет доступно через нотацию ассоциативного массива (root['api']).

person vinczemarton    schedule 02.05.2013
comment
Хотя падчерицу не похитили :) необходимость вложенного коверкания критична для минификации. Я думаю, что размер кода можно будет уменьшить более чем на 20%. обратите внимание, что я намеренно использовал window.lib_name.perform = root.api.perform, поэтому минификация все равно будет работать - person whadar; 02.05.2013
comment
Проблема в том, что динамическая типизация и практически все, что касается объектов в javascript, настолько снисходительны, что uglifyjs просто не может быть на 100% уверенным в том, что вы показываете, просто взглянув на ваш скрипт. Также @JanMisker поднял вопрос о строках. Их можно построить динамически, а затем использовать в качестве индексатора для ассоциативного массива. У uglifyjs просто нет способа безопасно реализовать минимизацию для этого. Вам придется сделать это вручную. - person vinczemarton; 02.05.2013
comment
Закрытие может это сделать (ADVANCED_OPTIMIZATIONS). - person miracle2k; 29.08.2013

как объяснил @Jan-Misker в своем ответе, изменение имени свойства НЕ является хорошей идеей, поскольку потенциально может сломать ваш код.

Однако вы можете обойти это, определив имена свойств как локальные переменные и изменив все .properties на [ключи], чтобы уменьшить размер файла:

(function() {
  var API = 'api';
  var CORE = 'core';
  var NAMES = 'names';
  var SLNC = 'SOME_LONG_NAMED_CONST';

  var root = {};
  root[API]={};
  root[CORE]={};
  root[NAMES]={};

  /* util.js file */
  root[NAMES][SLNC] ='Angel';

  /* Person.js file */
  root[CORE].Person = function(name) { this.name = name };

  /* API.js with the functions we want to expose */
  root[API].perform = function(param_for_api) { /* do something */ }

  window.lib_name.perform = root[API].perform;

})();

Поскольку теперь все свойства стали локальной переменной, uglify js исказит/сократит имена переменных и, как следствие, уменьшит общий размер файла:

!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}();

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

person Norman Xu    schedule 12.03.2014

В последней версии uglify (сегодня) есть изменение свойств объекта, см. v2.4.18. Он также поддерживает зарезервированные файлы для исключения как свойств объекта, так и переменных, которые вы не хотите искажать. Проверьте это.

Используйте опцию --mangle-props и --reserved-file filename1.json filename2.json и т.д....

person lostdorje    schedule 29.03.2015
comment
Я думаю, что проще указать uglify на mangle only these вместо mangle except this, потому что в последнем случае каждое новое ключевое слово, которое вы создаете, может быть случайно искажено (забудьте вставить в список исключений) - person Fernando Fabreti; 28.11.2017