Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Возврат функции из функции - как работает замыкание? [дубликат]



    На данный вопрос уже ответили:
    
        
            Как работают замыкания в JavaScript
                
                    7 ответов
                
        
    
    
Учу js и, к сожалению, не понял одну вещь

function makeCounter() {
  var currentCount = 1;

  return function() { // (**)
    return currentCount++;
  };
}

var counter = makeCounter(); // (*)

// каждый вызов увеличивает счётчик и возвращает результат
alert( counter() ); // 1
alert( counter() ); // 2
alert( counter() ); // 3

// создать другой счётчик, он будет независим от первого
var counter2 = makeCounter();
alert( counter2() ); // 1


Вопрос 1: почему при первом вызове функции переменная currentCount = 1, ведь возвращаемое функцией currentCount++, следовательно должно быть 2. Так почему 

return function()...


пропускается?

Вопрос 2: почему при вызовах функции currentCount не присваивается 1, ведь

var currentCount = 1;

    


Ответы

Ответ 1



Когда в строке var counter = makeCounter(); выполняется функция makeCounter, он присваивает значение 1 своей локальной переменной currentCount и возвращает, но не выполняе анонимную функцию. Так как анонимная функция использует переменную из внешней области видимости, создается "замыкание" (closure), которое соединяет экземпляр переменной currentCount с возвращаемым экземпляром анонимной функции.

Ответ 2



Попробую объяснить на пальцах. Данная строка кода var counter = makeCounter(); // (*) Делает примерно следующее var currentCount = 1; var counter = function() { // (**) return currentCount++; }; Только переменная currentCount не глобальная, а находится в "замыкании", то ест видна исключительно внутри функции. Собственно makeCounter() больше уже не выполняется никогда при вызове counter(), тут сама переменная counter является функцией, которая имеет исключительный доступ к переменной currentCount. Вся идея создания функции counter() не напрямую через присваивание, а через другу функцию makeCounter() заключается как раз в том, чтобы привязать к ней переменную, котору видит исключительно counter() и которая сохраняет свое значение между вызовами. В других языках программирования существуют так называемые статические переменные - их смысл как раз в том, чтобы сохранять значение между вызовами функции, но при этом быть доступными только внутри функции. Механим создания замыкания примерно такой. Когда вызывается функция для всех ее локальны переменных выделяется область памяти. Когда функция заканчивает свою работу, то эт область памяти очищается - в этом смысл локальных переменных, они живут, пока исполняетс функция. Однако если внутри функции (у вас это makeCounter()) объявить другую функци (которая тоже будет как бы локальной переменной-функцией), а потом вернуть объявленну функцию (она у вас анонимная, но назовем ее counter(), по имени переменной в которо она сохранится) вызывающему коду, то область памяти с локальными переменными будет "жить" до тех пор, пока возвращенная функция "живет" в переменной во внешнем коде. При этом к этой области памяти доступ будет иметь только, возвращенная функция. Возвращенная функция и связанная с ней область памяти называется замыканием. Если вызвать makeCounter() повторно, то выделиться новая область памяти, объявится и вернется новая функция, образуется новое замыкание - поэтому counter'ы независимы друг от друга, так как "смотрят" в разные области памяти и "видят" там разные переменные currentCount'ы Что касается постфикного (currentCount++) и префиксного (++currentCount) инкремента, то @PeterOslon хорошо объяснил.

Ответ 3



Есть разница между префиксный инкремент ++x и постфиксный инкремент x++. x++ - это возвращает значение x, потом добавляет 1 к x. ++x - добавляет 1 к x, потом возвращает значение x. Так что return function() { return currentCount++; }; вырабатывается примерно так: return function() { var temp = currentCount; currentCount = currentCount + 1; return temp; };

Бизнес-логика, что это такое?



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


Источник

Не совсем ясно, что означает этот термин 
    


Ответы

Ответ 1



Бизнес-логика - это логика доменной модели - все, что в вашем приложении происходит в терминах предметной области. Например, на SO - это все действия с пользователями, вопросами, ответами, плюсы минусы и т.д. Пример: Если пользователь не набрал ZZZ репутации - отправить его правку на проверку другими участниками - это бизнес-логика, ей место в модели. Перенаправить пользователя на страницу вопроса после его создания - не-бизнес логика, которой место в контроллере. Скрыть кнопку "Оставить комментарий" если текущий пользователь не имеет право оставлять комментарии - особенности представление данных (флага из модели) - во view. MVC позволяет выделить "не-бизнес" логику, связанную с пользовательским интерфейсом: вызовы методов модели по определенным действиям пользователя отображение/скрытие контролов подготовку данных к отправке на клиента. ... и поместить логику представления в отдельный кусок приложения - Controller. тем самым оставив в модели "чистую" бизнес-логику, не привязанную к интерфейсу пользователя. Стоит отметить, что ссылка в вопросе ведет на статью, иллюстрированную диаграммо Classic MVC. Реально в Web используется более современный вариант паттерна - MVC Model2 - и его производные. Его отличие - View не взаимодействует с моделью напрямую. Взаимодействие в современном MVC выглядит вот так:

Ответ 2



Бизнес-логика - то же самое что и логика предметной/доменной/прикладной области Допустим, вы программируете софт для приюта животных и для детского приюта. По бизнес-логике приюта для животных, предположим, котика, которого за неделю н забрали новые хозяева, надо усыпить. А до этого его надо кормить, поить и спать укладывать. По бизнес-логике детского приюта - ребенка надо кормить, поить и спать укладывать. В него нельзя втыкать шприц со смертельной дозой морфия. При этом все структуры данных, алгоритмы и т.д. - в двух программах практически одинаковы. Кроме вот этой маленькой детали. "ЭТОТ один ИФЧИК решил СУДЬБУ КОТЕЙКИ", или, например "начинающий программист УБИЛ младенца ВЕКТОРОМ" Если вы перепутаете бизнес-логику приюта для животных и детского приюта, и усыпит ребенка, а котенку подарите куклу, вы, надеюсь, попадете в тюрячку, там вам все за ООП расскажут. Не важно, бизнес это, расчет конфигурации молекул, приют или управление кораблем Бизнес-логика - это та самая часть, которая в итоге должна работать правильно и надежно, та, результатов которой ждет заказчик (котенок, ребенок) Если не отделять, допустим интерфейс от бизнес-логики, то вместо нажатия кнопки "отдат ребенка новым родителям" или "усыпить котенка", на двух аккуратных - почти похожих - пультах управления (интерфейсах) вы будете бегать туда-сюда, пытаясь понять, кого утопить, кого усыпить, кого отдать новым родителям и почему ничего не работает. Вы не отделили интерфейс (панель управления для запуска котят на луну) от бизнес-логики и все запуталось. Ну, я предупреждал. Используете вы синглтоны, очереди, базы данных, флэт-файлы, микросервисы - не важно - важно, чтобы бизнес-логика работала правильно. Под правильно подразумевается корректность результатов в приемлемое время. Все остальное ваших заказчиков не интересует. До тех пор, пока они не являются вашими владельцами. Именно поэтому вы можете продавать очень плохой - с точки зрения программиста - соф клиентам, но с трудом сможете построить на нем надежную систему. Требования бизнес-логики может быть и выполняются, но поддерживать этот код невозможно P.S. Маленький исторический экскурс. Бизнес-логикой это называется потому, что в Нормальном Мире, во Внешней Империи программирование в коммерции и корпорациях развивалось еще с 50х-60х годов: банки, страховые агентства, туроператоры, медицина. Т.е. тебе платили за то, чтобы ты внедрил требования конкретного бизнеса Хорошо, что это бизнес-логика, а не партийная логика, как в Северной Корее.

Ответ 3



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

Ответ 4



Читайте бизнес-логика, как просто логика. Всё. А отделение её от UI - то и означает: что в представлении не должно всяких обращени к БД, выборок из неё, вспомогательных функций на N-строк, например хитроумная сортировка, фильтрация, структурирование данных; шифрование данных; проверка на правильность логина/пароля и прочих каких-либо сверх манипуляций с данными. В представлении должно быть отображение конечного результата, который придет в отве на запрос к управляющему классу, а всё манипулирование (как описано выше) данными (логика) - должно происходить в другом месте. The end.

Ответ 5



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

Практическое применение Enum


Меня всегда волновал вопрос класса Enum: какие у них есть преимущества перед обычными static final примитивами?

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


Ответы

Ответ 1



enum имеет ряд преимуществ при использовании в сравнении с static final int. И использовать его стоит в том случае если вам нужны эти преимущества. Главным отличием является то что используя enum вы можете проверить тип данных. Например: public class SomeClass { public static int RED = 1; public static int BLUE = 2; public static int YELLOW = 3; public static int GREEN = 3; // Совпадающие значения private int color; public void setColor(int color) { this.color = color; } } При этом вы можете передать в класс любое значение int. new SomeClass().setColor(999); Следовательно, основные проблемы использования static final int это: Необходимость проверки вводимых данных. Нет никакой защиты от создания static final int переменной с дублирующимся значением. Еще одним преимуществом enum является возможность перегружать методы. public enum UnitConverter{ METERS{ @Override public double toMiles(final double meters){ return meters * 0.00062137D; } @Override public double toMeters(final double meters){ return meters; } }, MILES{ @Override public double toMiles(final double miles){ return miles; } @Override public double toMeters(final double miles){ return miles / 0.00062137D; } }; public abstract double toMiles(double unit); public abstract double toMeters(double unit); } Недостатки использования enum К ним не применимы операторы >, <, >=, <=. Следовательно enum не стоит применять в списке последовательных данных. enum также требует больше памяти для хранения чем обычная константа. IntDef и StringDef в Android Google не рекомендует использовать enum в Android приложениях из-за того что эт требует больше памяти. Вместо этого для Android можно использовать IntDef и StringDe которые позволяют ограничивать пространство значений для обычных констант. Следовательно, вы получаете главное преимущество enum не теряя в использовании памяти. Это практика повсеместно используется в стандартных классах Android: /** @hide */ @IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) public @interface Visibility {} /** * This view is visible. * Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int VISIBLE = 0x00000000; /** * This view is invisible, but it still takes up space for layout purposes. * Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int INVISIBLE = 0x00000004; /** * This view is invisible, and it doesn't take any space for layout * purposes. Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int GONE = 0x00000008; В принимающем методе используется специальная аннотация: public void setVisibility(@Visibility int visibility) { setFlags(visibility, VISIBILITY_MASK); if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false); } В таком случае вы не сможете использовать другую константу для этого метода: view.setVisibility(456);//Error: Must be one of: View.VISIBLE, View.INVISIBLE, View.GONE

Ответ 2



Перечисления (Enum) удобно использовать для представления множества взаимоисключающих состояний. Пример Перечисления кодов сообщения: enum MessageCode { // Установка однозначного соответствия // между элементом перечисления и целочисленным значением. UNKNOWN(0), TOAST(1), MESSAGE(2); private final int id; MessageCode(int code) { id = code; } // Метод получения целочисленного значения, // соответствующего выбранному элементу Перечисления (Enum) public int getId() { return id; } // Метод получения элемента Перечисления (Enum), // соответствующего переданному целочисленному значению public static MessageCode fromId(int code) { MessageCode[] list = MessageCode.values(); if (code >= 0 && code < list.length) return list[code]; else return MessageCode.UNKNOWN; } } Пример анализа сообщений: private Messenger chatUIThreadMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msg) { String str; switch (MessageCode.fromId(msg.what)) { // Доступ к выводу Toast'ов для Thread'ов прослушивающих Socket'ы case TOAST: str = (String) msg.obj; Toast.makeText(getBaseContext(), str, Toast.LENGTH_LONG).show(); break; // Вывод сообщений в лог чата case MESSAGE: str = (String) msg.obj; messages.add(str); messagesAdapter.notifyDataSetChanged(); final ListView listView = (ListView) findViewById(R.id.listViewChat); listView.smoothScrollToPosition(messagesAdapter.getCount() - 1); break; // Исключительная ситуация case UNKNOWN: Toast.makeText(getBaseContext(), "Messenger: неопределённый тип сообщения!", Toast.LENGTH_LONG).show(); break; default: super.handleMessage(msg); } } });

Ответ 3



Использование enum вместо констант полезно, например, когда вы хотите ограничит возможное множество значений аргумента к-л метода. Если использовать к-л константу (in или String, например), и метод, который должен её принимать будет иметь соответствующий тип аргумента, то ничто вам не помешает передать туда что-то помимо констант. Если же вы используете enum то передать что-то кроме enum у вас не получится.

Ответ 4



Enum можно использовать как реализацию паттерна Singleton. Преимущества: Сериализация из коробки Потокобезопасность из коробки Возможность использования EnumSet, EnumMap и т.д Поддержка switch Недостатки Не ленивая инициализация По мнению Joshua Bloch’а это лучший способ реализации шаблона public enum Singleton { INSTANCE; }

Как можно взаимодействовать с сайтом через консольное приложение?


Допустим, имеется сайт на котором есть какое-то поле.

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

За этими мне нужно обращаться к классу WebBrowser?

Если можно, то покажите пример(например, можно взять целевой сайт google, но бе
использования его API.)
    


Ответы

Ответ 1



Гуглим в консоле Покажу на примере программы, которая позволяет гуглить прям в консоле (извлекае первые заглавные ссылки поиска на google.com): Предварительные работы Используйте CefSharp — библиотеку-оболочку, основанную на Chromium. Очень подробно её описал в этом ответе. Устанавливается просто через Nuget пакет. Install-Package CefSharp.OffScreen -Version 57.0.0 Скопировав из приведенного мною ответа два класса (CefSharpWrapper и ConvertHelper) у вас уже готов скелет программы, с помощью которой вы можете исполнять любой JavaScript прямо из консольного приложения. Также установите x64 или x86 в качестве платформы. Платформа Any CPU поддерживается, но требует дополнительного кода. Дополнительные свойства и методы Также для данной задачи добавьте в CefSharpWrapper свойство Address: public string Address => _browser.Address; и метод WaitTillAddressChanges: public void WaitTillAddressChanges() { // wait till address changes AutoResetEvent waitHandle = new AutoResetEvent(false); EventHandler onAddressChanged = null; onAddressChanged = (sender, e) => { _browser.AddressChanged -= onAddressChanged; waitHandle.Set(); }; _browser.AddressChanged += onAddressChanged; waitHandle.WaitOne(); } Пример самой программы Вот пример самой программы (класс Program, метод Main): public class Program { private static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { CefSharpWrapper wrapper = new CefSharpWrapper(); wrapper.InitializeBrowser(); Console.Write("Введите поисковой запрос: "); string searchText = Console.ReadLine(); string[] urls = await wrapper.GetResultAfterPageLoad("https://google.com", async () => { await wrapper.EvaluateJavascript( // заполняем тесковое поле $@"document.getElementById('lst-ib').value = '{searchText}'; // выполняем submit поисковой формы document.getElementById('tsf').submit()"); // Ждём когда перейдёт на результаты поиска wrapper.WaitTillAddressChanges(); // Когда страница результатов поиска полностью подгрузится, излекае результаты return await wrapper.GetResultAfterPageLoad(wrapper.Address, async () => await wrapper.EvaluateJavascript( // получаем результаты "Array.prototype.map.call(document.querySelectorAll('h3.r > a'), (a) => a.href);")); }); Console.WriteLine("Первые ссылки поиска:"); foreach (string url in urls) { Console.WriteLine(url); } wrapper.ShutdownBrowser(); } } Результаты программы К примеру, если я введу "parse html C#": AJAX Также довольно удобно работать с AJAX c помощью с этой библиотеки.

Ответ 2



Если Вам надо закодить лишь какие-то определенные, известные части какого-то сайт - можно просто вытянуть нужные запросы, которые посылаются браузером (вычленить их можно в консоли разработчика браузера, обычно на вкладке Network) и посылать этот запрос в программе, например через HttpClient. Если же изначально самих запросов Вы знать не можете (получать надо динамически в самой программе), то может 2 варианта событий: Если сайт может работать без использования ajax - парсить страничку на наличие тэг
и отталкиваться от него. В нем есть атрибут action в котором содержится пут и атрибут encoded, который указывает формат кодировки (если нет - по умолчанию "application/x-www-form-urlencoded", вроде бы, хотя точно лучше посмотреть через консоль разработчика). Все это собираете, в том числе и параметры внутри тэга , отправляете через HttpClient и получаете ответ Если сайт работает только через ajax - тут уже сложнее и другого выхода кроме как вручную смотреть через Network какие запросы формируются и жетско их кодить я не вижу Но в большинстве случаев хватает и обычной отправки заранее подготовленных (вычлененных) запросов со своими параметрами. Так же можно привязать к HttpClient'у HttpClientHandler для хранения кукисов и других плюшек. Например Вам надо добавить пост на стену в вк определенное время - алгоритм будет такой: Создать HttpClient с привязанным к нему HttpClientHandler'ом Отправить через него POST запрос на нужный адрес (посмотреть можно на странице в в исходниках или вычленяем через консоль разработчика) со своими логином и паролем в качестве значений. Этот запрос вернет ответ, но в данном случае он не нужен, просто ждем пока придет HttpResponseMessage, чтоб нам пришли куки Отправить еще один POST запрос, который добавит сообщение на стену

Ответ 3



Нашел интересную либу Selenium с помощью которой, через драйвера можно взаимодействовать с реальным браузером. Довольная простая в управлении: using OpenQA.Selenium; using OpenQA.Selenium.Firefox; // Requires reference to WebDriver.Support.dll using OpenQA.Selenium.Support.UI; class GoogleSuggest { static void Main(string[] args) { // Create a new instance of the Firefox driver. // Note that it is wrapped in a using clause so that the browser is closed // and the webdriver is disposed (even in the face of exceptions). // Also note that the remainder of the code relies on the interface, // not the implementation. // Further note that other drivers (InternetExplorerDriver, // ChromeDriver, etc.) will require further configuration // before this example will work. See the wiki pages for the // individual drivers at http://code.google.com/p/selenium/wiki // for further information. using (IWebDriver driver = new FirefoxDriver()) { //Notice navigation is slightly different than the Java version //This is because 'get' is a keyword in C# driver.Navigate().GoToUrl("http://www.google.com/"); // Find the text input element by its name IWebElement query = driver.FindElement(By.Name("q")); // Enter something to search for query.SendKeys("Cheese"); // Now submit the form. WebDriver will find the form for us from the element query.Submit(); // Google's search is rendered dynamically with JavaScript. // Wait for the page to load, timeout after 10 seconds var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); wait.Until(d => d.Title.StartsWith("cheese", StringComparison.OrdinalIgnoreCase)); // Should see: "Cheese - Google Search" (for an English locale) Console.WriteLine("Page title is: " + driver.Title); } } } Однако, все равно хотелось узнать, как можно сделать это стандартными средствами .NET.

Ответ 4



Уточните каким образом происходит обработка введенного текста в поле на сайте? Сайт отправляет его GET/POST или обрабатывает иначе? Приведу ход своих мыслей на примере googl'a и GET.. Зачем нам эмулировать действия пользователя на сайте если мы напрямую можем работать с отправлением запроса, получением результата и дальнейшим парсингом string query_user = Console.ReadLine(); string url = "https://www.google.ru/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#newwindow=1&q=" + query_user; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); И дальше извлекаем из переменной response все что необходимо

Ответ 5



Все зависит от твоих потребностей и от сайта: Используется ли на сайте AJAX Есть ли там сложно-обходимые капчи вроде Re-Capcha На сколько сложная последовательность действий нужна На сколько важно быстродействие На сколько будет загруженной страница(это может быть просто страница, а может быть бесконечная прокрутка) Нужна ли кросплатформенность решения Используются ли какие-то хитрые технологии на сайтах вроде CORS В принципе если обобщить всю информацию поданную НИЖЕ и привести к максимально универсальному ответу: а. если нужно самое быстрое "одноразовое" решение и скорость работы не важна -- значит Selenium б. Если это задача по тестированию - Selenium в. Если нужна универсальность решения и максимально простая поддержка на долгих периодах времени и не важно сколько займет кодинг: CefSharp для прохождения капчи/рекапчи вытянуть из CefSharp куки, всунуть их в дотнетовские куки а дальше взаимодействовать с сайтом через HttpWebRequest запросы Детальнее же про каждый из способов.... WebBrowser Минуса Очень бедное управление. Сложный в работе. графический элемент, а значит будет жрать оперативу и работать медленно не работает с бесконечными страницами гипер кривая работа с JS. Некоторые сайты просто не отображаются. Устаревший инструмент. Плюса +? в теории -кросплатформенный, но не уверен. Прямая работа через HttpWebRequest Post/Get запросы с последующим парсингом. Нужну последовательность можно узнать в консоли разработчика браузера, на вкладке Network. Желательно использовать НЕ низкоуровневые запросы а какие-то библиотеки для REST запросов. Будет быстрее значительно код писатся. Минуса если на сайте AJAX - работать так может быть сложно. никак не пройти Re-Capcha. Может быть затруднительным прохождение капч в принципе. Плюса если сайт часто изменяется, реже будет ламатся код (фрондтенд менее стабилен че бекенд, так что идеальное решение для взаимодействия с бекендом!) Самое высокое быстродействие. Вы можете хоть тысячи страниц в паралели проганять. Может работать с "бесконечными" страницами любого размера. [Хотя для этого нужн будет попотеть немного] прекрасная кросплатформенность кода Selenium - в общем и целом он предназначен именно для автоматизированного тестировани веб-сайтов. В том числе и с аджакс-технологией. То есть он может делать практическ все действия, которые могут делать в браузере люди: находиль элементы интерфейса сайта, посылать в них нажатия кнопок, скролить, делать скриншоты, проверять на видимость/доступность и т.д. Т.к. указан C# тег, а так же было сказано про отсутствие лишних окон, нужно использоват связку: Selenium Web-Driver и PhantomJS. Фантом - это безюайный браузер на движке как у хрома. Он может все то же самое что и простой браузер, только не показывается визуально и не тратит ресурсы на отрисовку. Отдельно хочу сказать что сам по себе селениум довольно неудобен и далек от ООП Проблема в том, что выбора-то особенно и нету... Минуса Он не ООПшный и довольно кривой в использовании очень хреново работает с большими страницами очень медленный нужно использовать сторонние браузеры и в проэкт качать доп.библиотеки для поддержки нужного браузера Не умеет работать с внешними окнами. Например с системным окном аутентификации н сервер (с версии 3.4 умеет). Или с окнами Open/Save file (для этого есть костыли а так же я написал полууниверсальное решение: https://github.com/ukushu/DialogCapabilities ) Плюса На нем вполне можно проходить ReCapcha. При помощи юзера, правда :) Можно пройти и без юзера, если ипользовать аудиокапчу + распознавание текста с ауди через googleAPI. Например, вот здесь есть реализация прохождения рекапчи ботом, но на питоне: https://github.com/eastee/rebreakcaptcha/blob/master/rebreakcaptcha.py Вполне может работать с AJAX. А если допилять костыли, то, даже, вполне неплохо работать. Работает с посделовательностями действий любой сложности. В том числе всякие драг енд дропы. Может запускатся на многих компьютерах в паралели Не уверен ? понятия не имею по поводу кросплатформенности Оболочки вокруг готового движка вроде Хромиума. За пример - CefSharp. Минуса хреново работает с большими/бесконечными страницами относительно медленный нужно качать несколько библиотек многий функционал доступный с коробки в селениуме нужно будет реализовывать самому через JS код. Плюса На нем вполне можно проходить ReCapcha. При помощи юзера, правда :) работает с AJAX Работает с посделовательностями действий любой сложности Умеет работать с внешними окнами. Например с системным окном аутентификации на сервер. Не уверен: ? понятия не имею по поводу кросплатформенности. Скорее всего есть

Ответ 6



В заголовке вопроса - консольное приложение. Но, могу предположить по тексту В это поле нужно послать какое-то значение, нажать на кнопку, затем получить новую страницу, где перейти по первой ссылке. возможно вам подойдёт эмуляция действий пользователя в браузере. Про эмуляцию в браузере: Очень точная эмуляция действий пользователя делается с помощью написания собственног расширения для браузера (Firefox/GoogleChrome например) - от настоящего пользователя не отличить. Плагин может полностью перехватывать управление - например открыть вкладку с определённым сайтом, и "захватить" её. Внутри плагина разрабатывается некая сущность, которая может эмулировать движени и клики мышки от объекта к объекту: сами объекты с чужого сайта выбираются, например, простым jQuery - так-же как и в коде любого сайта. Также сущности нужно добавить возможность эмулировать ввод текста в любое поле. Эмуляция может происходить посылкой trusted DOM событий (это код внути плагинов умеет) или можно напрямую использовать WinApi и его PostMessageW (по крайней мере аддоны фаерфокса умеют подключать любую dll и юзать её затем из JS) - железобетонный способ. В общем технически такой эмулятор от пользователя не отличить. Под подобную деятельность заточен известный аддон Greasemonkey . Но если масштаб эмуляции планируются большими (политика добавления определённых скриптов к определённым страницам недостаточна) - не рекомендовал бы, ибо его логики не хватит. Если нужно к эмуляции добавить ещё и управление браузером из консоли, используйт selenium. Это не прокатит, если вы хотите чтобы это всё работало со стандартного сервер - то есть без GUI. Но по своей практике скажу - использовали эмуляторы часто, для них у нас были отдельные сервера с виндой, с GUI. Без GUI, с голой консолью, есть мнение что качественно эмулировать не выйдет.. Классическое использование таких эмуляторов - боты-парсеры, боты-спамеры, боты-игровые, авто-тестирование собственного сайта.

Ответ 7



На мой взгляд, лучшее решение - это WatiN. WatiN Features Automate all major HTML elements with ease Find elements by multiple attributes Native support for Page and Control model Supports AJAX website testing Supports creating screenshots of webpages Supports frames (cross domain) and iframes Handles popup dialogs like alert, confirm, login etc.. Supports HTML dialogs (modal and modeless) Easy to integrate with your favorit (unit) test tool Works with Internet Explorer 6, 7, 8, 9 and FireFox 2 and 3 Can be used with any .Net Language Licensed under the Apache License 2.0 Downloaded more than 120.000 times And since its open source you can add and contribute new features yourself Пример: [Test] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click(); Assert.IsTrue(browser.ContainsText("WatiN")); } } Сам пользуюсь для граббинга.

Код-гольф: Обход двумерного массива змейкой


Задача соревнования:

Имеется двумерный массив N x N. Нужно написать функцию обхода двумерного массив
змейкой от правого ребра. Пример на картинке.





Пример двумерного массива:

На входе имеется следующий массив:

input = [[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]]


На выходе одномерный массив после обхода:

output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]


Может использоваться любой язык. 

Основное условие: функция должна работать корректно для любого равностороннего двумерного массива. 

Указывать название языка в заголовке ответа и количество символов минифицированной версии функции через запятую.

Победителем станет тот, кто напишет ее за меньшее количество символов. За это о
получает 300 репутации. Победитель определится через 2 недели (12 января). 

Ответ автора не учитывается при выборе победителя. Желаю удачи :)

Таблица лидеров:



execute("ru.stackoverflow.com", 926927);







Победители:

1 место: Haskell - 39 (@АндрейNOP)

2 место: JavaScript - 40 (@Groxan)

3 место: Groovy - 42 (@Nick)

Всем огромное спасибо за участие и интересные решения ;)
    


Ответы

Ответ 1



Haskell, 39 40 42 r=reverse;s[]=[];s(h:t)=r h++s(map r t) https://ideone.com/JYttmr

Ответ 2



Octave, 47 @(l,b=l(1:2:end,:)=fliplr(l(1:2:end,:)))l'(:)'; Try it online! Если разрешить возвращать не вектор-строку, а вектор-столбец, сокращается на 1 символ: Octave, 46 @(l,b=l(1:2:end,:)=fliplr(l(1:2:end,:)))l'(:); Try it online! Прошлый вариант без анонимной функции: Octave, 40 l(1:2:end,:)=fliplr(l(1:2:end,:));l'(:)' Try it online!

Ответ 3



Python, 56 f=lambda l:sum([x[::i%2*2-1]for i,x in enumerate(l)],[]) Спасибо @АндрейNOP за подсказку! Python, 60 f=lambda l:sum([[x[::-1],x][i&1]for i,x in enumerate(l)],[]) https://ideone.com/JPyzUQ (эта версия с лямбда функцией была любезно предоставлена @Let's say Pie) Попытка 2: инвалидирована по причине неправильного оформления - решение должно быть реализовано как функция Python, 49 sum([[x[::-1],x][i&1]for i,x in enumerate(l)],[]) https://ideone.com/WdHq4n PS @КириллМалышев подсказал как можно сократить код еще на 6 символов - спасибо!! Попытка 1: Python, 55 sum([x if i&1 else x[::-1] for i,x in enumerate(l)],[]) https://ideone.com/2hoF3P

Ответ 4



JavaScript, 38 Данный код работает в Firefox, но не работает на V8 (из-за специфичного алгоритма сортировки). Если это неприемлемо, скажите =) f=a=>a.flatMap((x,i)=>x.sort(_=>~i&1)) console.log(f([[4,3,2,1],[5,6,7,8],[12,11,10,9],[13,14,15,16]])); JavaScript, 40 f=a=>a.flatMap((x,i)=>i%2?x:x.reverse()) console.log(f([[4,3,2,1],[5,6,7,8],[12,11,10,9],[13,14,15,16]]));

Ответ 5



JavaScript, 54 f=a=>a.reduce((c,n,i)=>c.concat(i%2?n:n.reverse()),[]) console.log(f([[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]]));

Ответ 6



Python, 62 61 Пока ничего лучше варианта с рекурсией не придумал :) f=lambda m,x:m and(x&1and m.pop(0)or m.pop(0)[::-1])+f(m,x+1) https://ideone.com/i2d0f6

Ответ 7



Python, 43 f=lambda m,a=-1:m and m.pop(0)[::a]+f(m,-a) https://ideone.com/fQVgtD

Ответ 8



F#, 68 let rec f=function|[]->[]|h::t->List.rev h@(t|>List.map List.rev|>f) https://ideone.com/S6PhEZ

Ответ 9



PowerShell, 51 $f={$args[0]|%{if(++$i%2){[Array]::Reverse($_)}$_}} Try it online!

Ответ 10



C#, 62 dynamic s(int[][]a)=>a.SelectMany((x,i)=>i%2>0?x:x.Reverse()); https://repl.it/repls/EmbarrassedHalfCommand

Ответ 11



PHP, 93 91 function f($a){foreach($a as$v)$r[]=$_++&1?$v:array_reverse($v);return array_merge(...$r);} https://ideone.com/XsiPap

Ответ 12



PHP, 138 105 function f($a){foreach($a as$i=>$y){foreach($y as$x=>$w){$r[]=$i&1?$w:array_reverse($y)[$x];}}return $r;} https://3v4l.org/DgDi8

Ответ 13



C#, 188 184 List S(T[,]a){var l=new List();int m=a.GetLength(1);for(int i=0;i=0;j--)l.Add(a[i,j]);else for(int j=0;j

Ответ 14



C#,171 168 163 153 Так как решения через LINQ уже были, то предлагаю вашему вниманию традиционных подход. Минифицированная версия: T[]F(T[,]a){var d=a.GetUpperBound(1);var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0){r.Add(a[i,j]);j=b?++j:--j;}}return r.ToArray();} Удобночитаемая: static T[] F(T[,] a) { var d = a.GetUpperBound(1); var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) { r.Add(a[i, j]); j = b ? ++j : --j;//Трюк, что бы сэкономить символы на if/else } } return r.ToArray(); } Чуть чуть подправил: T[]F(T[,]a){var d=a.GetUpperBound(1);var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0){r.Add(a[i,b?j++:j--]);}}return r.ToArray();} Удобночитаемая: T[] F(T[,] a) { var d = a.GetUpperBound(1); var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) { r.Add(a[i, b ? j++ : j--]); } } return r.ToArray(); } Опять чуть подправил: T[]F(T[,]a){var d=a.GetLength(1)-1;var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0)r.Add(a[i,b?j++:j--]);}return r.ToArray();} Удобночитаемая: T[] F(T[,] a) { var d = a.GetLength(1) - 1; var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) r.Add(a[i, b ? j++ : j--]); } return r.ToArray(); } И еще чуть-чуть убрал все лишнее: T[]F(T[,]a){int d=a.GetLength(1),g=0;var r=new T[d*d];for(int i=0;i0;int j=b?0:d-1;while(j=0)r[g++]=a[i,b?j++:j--];}return r;} Удобночитаемый: T[] F(T[,] a) { int d = a.GetLength(1), g = 0; var r = new T[d * d]; for (int i = 0; i < d; i++) { var b = i % 2 > 0; int j = b ? 0 : d - 1; while (j < d & j >= 0) r[g++] = a[i, b ? j++ : j--]; } return r; }

Ответ 15



Perl 5, 81 sub f{foreach$row(@_){foreach$val(@$row){push@$s,$i++%2?@$val:reverse@$val;}}$s;} Try it online!

Ответ 16



Groovy, 42 f={i=0;it.each{it.reverse i=!i}.flatten()} https://ideone.com/XirvAC

Ответ 17



C, 97 int f(int n,int a[n][n],int*b){for(int i=0;i

Ответ 18



Ruby, 63 def f(a,x)a[0]?(x%2==0?a.shift.reverse: a.shift)+f(a,x+1):[]end https://ideone.com/T0MECG

Как устроена работа буферизации в php?


Существует 2 вида буферов в php:


Cистемный буфер
Пользовательский буфер


Так могут возникнуть проблемы с работой с буфером.
Для работы с системным буфером есть одна единственная функция - flush(). Для работ
с пользовательским буфером остальные функции с приставкой ob_(Функция ob_implicit_flush() - не относится к данному списку, она больше подходит к системному буферу).

Код для демонстрации:


  Внимание использована инструкция в файле .htaccess
  php_value output_buffering off


";
for ($i = 1; $i <= 10; $i++) {
    echo $i.":(".mt_rand(0,1000).") ";
    flush();
    sleep(1);
}
echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Этим примером я хотел показать что при использовании только системного буфера м можем выводить кусочками сообщение на экран. Минусом является то что если захотим отправить заголовки или сессию то будет ошибка, т.к после отправки первого сообщения на экран всевозможные заголовки были отправлены и повторная передача заголовков невозможна. Код для демонстрации - 2: Внимание использована инструкция в файле .htaccess php_value output_buffering 4096 "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; flush(); sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> В этом примере мы не увидим кусочный вывод текста мы увидем его только когда исполнитс весь код. Даже flush() здесь не поможет. Здесь между прочим используется не только системный но и пользовательский буфер. Кстати вылезут ошибочки по поводу заголовков...тут конечно я не могу объяснить ситуацию из-за чего. Я в конце выскажу свои предположения и вопросы...stay here. Код для демонстрации - 3: Внимание использована инструкция в файле .htaccess php_value output_buffering 4096 "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; ob_flush(); flush(); sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> В этом примере информация будет выводиться кусками. Этот пример как вы могли заметит отличается только дополнительной функцией ob_flush(). Она таки высвобождает накопленны данные в системный буфер. Следом вызывается flush() который информацию выводит прямо в браузер. Первый пример кода тому явное подтверждение. В конце будут отправлены ошибки о заголовках, оно и понятно данные были уже отправлены. Код для демонстрации - 4: Внимание использована инструкция в файле .htaccess php_value output_buffering off "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Интересно, а для чего я этот код добавил? Никаких флюшей здесь нет, даже функций которые работают с буфером вывода. Все гораздо проще чем может показаться на первы взгляд. Дело в том что если мы запустим этот код он отработает как надо и выведет ошибки об уже отправленных заголовках...what? Выходит системный буфер не такой и буфер если он вызывает ошибку о заголовках, однако данные выведутся только тогда когда закончится скрипт. Вот так вот. Эпилог: Мои суждения: Т.к существует 2 буфера в php то получается один зависит от другого: чтобы вывест информацию со свежевыпеченного echo, мы должны сначала высвободить информацию с "нашего" буфера в "казенный" php буфер, после этого мы должны вызвать операцию для "казенного" php буфера на вывод. Системный буфер нельзя удалить, а пользовательский можно. Информация всегда будет попадать в системный буфер хочешь ты того или нет! Пользовательский буфер - это станция где люди ожидают свой самолет. Системный буфе - это самолет, который вмещает в себя людей. Заголовки - это купленные билеты (Вы ж не можете купить билет уже в самолете (Ну по крайней мере я ни разу такого не видел)) если самолет имеет время ожидания вы можете купить билет прямо на станции. Одно без другого жить не может, нет пользовательского буфера - нет времени для покупки билета, нет системного буфера - нет самолета. Выводом буфера будет являться то, когда самолет прибудет на станцию и высадит пассажиров Вопросы: Мой второй пример, мне не понятно по какой причине мне в конце работы скрипта ph пишет ошибку, по факту flush() не срабатывает ввиду того что мы находимся в пользовательском буфере. (Хотя возможно то что в системном буфере ничего нет и этим вызовом мы уже отправляем заголовки) Если опять же во втором примере мы заменим flush() => ob_flush(), мы получим ошибк об отправленных заголовках...why? Как я писал есть 2 буфера системный и пользовательский по логике в системном буфере лежит пользовательский и как вы могли видеть при наличии пользовательского буфера необходимо вызвать 2 флюша, а если у нас имелся только системный то хватало системного флюша. Если мы в файле .htaccess напишем php_value output_buffering on то размер буфер станет unlimited как сообщает php.ini, отнюдь если мы в скрипте вызовем функцию ob_get_level( то получим 0, если вы запускали код, который я писал выше то могли заметить если у нас буфер был заданного размера то мы получали 1, если мы его удаляли или писали off то он у нас был 0 (хочу заметить я знаю что могут быть вложенные буферы и значение может быть куда выше)...собственно почему у нас 0 если размер буфера on? Переосмысление (ответы на первый, второй и третий вопросы): Если данные попадают в системный буфер то заголовки будут отправлены, не важно вызвали вы flush() или нет, как следствие - варн об от отправленных заголовках* Если вы вызовете flush(), но в системный буфер до этого ничего не попадало, это будет означать что данные нужно вывести на экран немедленно из системного буфера, как следствие - отправк заголовков и варн* Если вы пишите ob_flush() в пользовательском буфере с уровнем < 2 (это равносильно тому, что если вы пишите echo в системном буфере), то вы получите варн* так как вы пробрасываете содержимое в системный буфер, а как я писал выше системный буфер если видит какие-то выводимые данные он начинает отправлять заголовки, в этом случае происходит то же самое. Если мы в файле .htaccess напишем php_value output_buffering on при вызове phpinfo( в php файле мы увидим в колонке Local Value - on. В итоге у нас буфер 0! Как так? Вс просто. Эта директива (output_buffering) не поддерживает такое значение. У этой директив есть несколько состояний: 0 - выключенный буфер, 1 - безлимитный буфер и любое число. Кроме того если мы напишем текст вроде: on, off, true, false, sampletext - то в Local Value мы увидим подобные значения, но они будут интерпетироваться как 0. Если мы будем писать текст через пробел то получим ошибку 500, например php_value output_buffering hello world. * Если вы отправляете заголовки после. ** php_value, php_flag, output_buffering - директивы. P.S Возможно я что-то не правильно понял, не стоит воспринимать это как истину, любом случае я смог понять только так, по-другому объяснить свои вопросы не удалось. В любом случае я жду объяснения от людей, которые соображают


Ответы

Ответ 1



Отвечу только на часть вашего вопроса, по крайней мере на текущий момент. Ваш код: "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Ваш комментарий к нему: Интересно, а для чего я этот код добавил? Никаких флюшей здесь нет, даже функций, которые работают с буфером вывода. Все гораздо проще чем может показаться на первый взгляд. Дело в том что если мы запустим этот код он отработает как надо и выведет ошибки об уже отправленных заголовках...what? Выходит системный буфер не такой и буфер если он вызывает ошибку о заголовках, однако данные выведутся только тогда когда закончится скрипт. Вот так вот. Так вот. Не совсем понятно, почему вы в этом, четвертом примере решили (если я ва верно понял), почему не должно быть предупреждений насчет заголовков. Так или иначе вы в любом случае должны послать их самыми первыми, до посыла всяких там текстовых сообщений через echo - банально потому, что именно такого поведения от веб-сервера ожидают браузеры. И php тут просто уведомляет вас, что вы неверно пытаетесь сообщить браузеру что-либо через заголовки. Для понимания этого стоит обратиться к собственно протоколу HTTP, а именно к ег структуре. Ответ веб-сервера в общем случае состоит из трех частей: Starting line, Headers и Message Body. Первое отвечает за тип сообщения, второе за заголовки и третье собственно за то, что получит браузер в качестве ответа. Соответственно, когда приведенный код выше начинает выполняться, вне зависимост от того, играетесь ли вы с буфером или нет, веб-сервер, который работает с вашим интерпретаторо php, будь он apache'ем или чем угодно еще, при получении от php первого же текстовог сообщения в третьей строке вашего кода (а оно будет первым, данный код же будет выполняться последовательно), сформирует ответ браузеру, который будет состоять из трех перечисленных выше частей. И далее веб-браузер может всего лишь досылать что-либо в Message Body, но заголовки уже будут отправлены. Другими словами, если вы хотите что-то вывести в заголовках, то вам нужно сформироват их до первого любого вывода в браузер, или же выстроить логику своего приложения каким-либо иным образом, где функция header() не будет использоваться после любой функции, реализующей вывод чего бы то ни было браузеру посредством HTTP.

Ответ 2



Чтобы немного прояснить для себя и других, как работает буферизация в PHP, давайте заглянем в исходники. У меня исходники php-7.1.3. Заходим в корневую папку исходников и пытаемся найти в них реализацию функции ob_get_contents: $ fgrep -r 'PHP_FUNCTION(ob_get_contents)' . Находится такое: ./main/output.c:PHP_FUNCTION(ob_get_contents) Вот эта функция: PHP_FUNCTION(ob_get_contents) { if (zend_parse_parameters_none() == FAILURE) { return; } if (php_output_get_contents(return_value) == FAILURE) { RETURN_FALSE; } } (Попутно было замечено, что PHP-еретики в своих кодах используют табулятор вместо 4-х пробелов.) В том же файле находим функцию php_output_get_contents: PHPAPI int php_output_get_contents(zval *p) { if (OG(active)) { ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used); return SUCCESS; } else { ZVAL_NULL(p); return FAILURE; } } Что происходит? Выглядит так, как будто что-то (видимо, возвращаемая строка) формируетс на основе некоторого адреса в памяти и длины данных. То есть пользовательский буфер это просто кусок памяти. Вероятно, там еще есть стек меток для каждого вызова ob_start. Это уже сами ищите реализацию ob_start, кому интересно. Аналогичным образом можно найти реализацию функции flush и увидеть, что она работает через функцию конкретного применяемого SAPI. Я посмотрел SAPI для PHP как модуля Apache: static void php_apache_sapi_flush(void *server_context) { php_struct *ctx; request_rec *r; ctx = server_context; /* If we haven't registered a server_context yet, * then don't bother flushing. */ if (!server_context) { return; } r = ctx->r; sapi_send_headers(); r->status = SG(sapi_headers).http_response_code; SG(headers_sent) = 1; if (ap_rflush(r) < 0 || r->connection->aborted) { php_handle_aborted_connection(); } } Здесь используются структуры C API самого Apache, то есть и вывод предварительн должен делаться таким же образом. Как Apache буферизует выводимые данные - это уже его личное дело и к PHP не имеет никакого отношения. Результат таков, что "системная" и "пользовательская" буферизация в PHP - это абсолютн разные вещи, настолько разные, что их не стоило бы называть в одном списке, чтобы не вносить сумятицу, и, возможно, с той же целью, пользовательскую буферизацию вообще стоило бы назвать как-то по-другому.

Javascript: безопасны ли висячие запятые?


Один мой коллега очень любит при определении константных Javascript-объектов оставлять висячие запятые:

var myObj = {
    prop1: 'smth',
    prop2: 'smthElse', // <--- Висячая запятая!
}


Мотивирует он это тем, что при исправлении такого объекта программист получит более релевантный вывод в диффе (1 строчку вместо двух). 

Мне такой стиль очень не нравится, но по причинам также чисто эстетическим:
1. JSLint на такое ругается
2. Неподдержка в старых IE (8-). Но под них мы все равно не пишем.

Стандарт вообще говоря такое разрешает.

Внимание, вопрос: известны ли вам еще причины по которым висячие запятые стоит/н
стоит использовать? Например положительное/отрицательное влияние на сборщики-оптимизаторы или что-нибудь в этом духе
    


Ответы

Ответ 1



Строго говоря: стандарт не запрещает использование висячих запятых в инициализаторе объекта ObjectLiteral[Yield] : { } { PropertyDefinitionList[?Yield] } { PropertyDefinitionList[?Yield] , } Таким образом ставить или не ставить запятую целиком на совести разработчика. В итоге, все решается договоренностью внутри группы разработчиков, чтобы код бы однообразным. Так как это допустимый вариант инициализации - никаких влияний на оптимизацию(минимизацию) оказываться не должно, но зависит от реализации оптимизатора/минификатора. Что касается проверяльщиков: все зависит от правил, которые довольно гибко могу настраиваться. UPD: июль 2017 Добавлю, что, на июль 2017 года, один из самых популярных стайлгайдов - стайлгайд от airbnb не просто не запрещает висячие запятые, а требует их.