Pull to refresh

Comments 100

А можно еще вопрос?
Как правильнее обрабатывать большие объемы данных в Javascript избегая зависания интерфейса и сообщений от браузера о «долгом скрипте» (длинный список данных для отчета, отдающийся в JSON)? Есть ли какие-либо библиотеки для этого?
UFO just landed and posted this here
Разбить на короткие блоки, вызывая их по setTimeout() или упомянутые выше WW.
Задача вполне реальная, и, как мне казалось, кем нибудь уже сотни раз решалась.
WW работают не везде, и проблема с производительностью чаще касается браузеров, в которых WW не работают.
SetTimeout хоть и является очевидным решением, но очень странно видоизменяет код и скорее выглядит как костыль (либо приходится использовать его везде, либо дописывать когда количество пользовательских данных достигает объемов которые тормозят работу браузера).
UFO just landed and posted this here
глупость. eval — не нужен. ниже — обсудили, почему.
есть ещё третий вариант — через замыкания.
И четвертый, присвоить колбеку свойство с нужным значением как объекту. Где то ниже TheShock писал про .bind — 5й получается :) Хотя eval/new Function можно вообще за варианты не считать.

Ну писать, что в функцию передаются ссылки значений — некорректно.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Добавлю от себя пару моментов.

Eval — зло ещё и потому, что не даёт браузерам оптимизировать код:

конструктор функции — тоже не стоит использовать. лично мне НИКОГДА не приходилось использовать eval в своём коде.

Есть ли в JavaScript аналоги декларациям/аннотациям (термины из Python/Java соответственно)?

Если я правильно понимаю задачу, то сделать достаточно легко благодаря функциям высшего порядка. Допустим, возмём декоратор "@delay", который заставляет выполнятся функцию через время. В Питоне, он выглядел бы как-то так:

@delay(1000)
def foo(object):
  // code here


На JS:

var foo = function () {
  // code here
}.delay(1000);

// или так:

var foo = delay(1000, function () {
  // code here
});


Код мог бы быть каким-то таким:
Function.prototype.delay = function ( time ) {
  var origFn = this;
  return function () {
    setTimeout( origFn, time );
  }
}

Декораторами применение аннотаций не исчерпывается.

Как вообще придать коду мета-информацию? Один из вариантов — это Google Closure Compiler, есть ли что-нибудь еще? Как бы вы делали AOP?
давайте реальный пример — подумаем что-то.
Да, первый вариант очень близок к декораторам, спасибо.
Ой, я забыл добавить для сравнения вариант БЕЗ eval:




В варианте с eval в «Scope Variables» видно, что переменные не удалены из контекста
Вот насчет eval
У меня есть некий пример, приходилось с подобным сталкиваться просто, не знаю, возможно вы знаете как это решить без евала, хотелось бы знать как.

Допустим есть некая функция asyncFunc, она принимает один аргумент, в нашем случае это будет число. Так же у нас есть некая переменная count, которая является целом числом больше единицы.
Нужно вызвать функцию asyncFunc, передавая ей в качестве аргумента число от 0 до count (вызвать count раз), при этом промежуток между вызовами должен составлять 10 секунд.

Вот допустим такой код:

var asyncFunc = function (n)
{
    console.log(n);
};

var count = 5;

for (var i = 0; i < count; i++)
{
    window.setTimeout(function()
    {
        asyncFunc(i);
    }, 10000 * i);
}

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

for (var i = 0; i < count; i++)
{
    window.setTimeout('asyncFunc(' + i + ')', 10000 * i);
}

Тут без евала конечно, но в setTimeout приходится использовать строку вместо функции, так что, думаю, одно и то же
Ваша проблема решается без эвала, это классическая проблема, решаемая с помощью замыканий. Например так:
for (var i = 0; i < count; i++)
{
  (function(i) {
    return window.setTimeout(function()
    {
      asyncFunc(i);
    }, 10000 * i);
  })(i);
}
Спасибо огромное, не знал
Насколько я понимаю он работает благодаря отделению области видимости где вызывается setTimeout от области видимости где инкрементируется i?
Да, это работает потому, что для каждой итерации цикла создается самовызываемая функция, которая имеет свою область видимости, в которой i — имеет нужное нам значение (мы получаем его внутри setTimeout). В вашем же коде, для всех i область видимости одинаковая и на момент вызова функции в setTimeout, i в этой области видимости будет уже равно 5 (цикл уже отработал). Подробнее почитать например тут: javascript.ru/basic/closure
Сам помню, когда разбирался в тонкостях JS, думал, как красиво это сделать)
С помощью замыканий, и правда, получается просто и красиво.
Есть также хак с with:
for (var i = 0; i < count; i++) with ({ i: i }) {
    window.setTimeout(function() {
      asyncFunc(i);
    }, 10000 * i);
}

Но он уже deprecated, в будущем вместо него будем использовать let.
Кстати, ещё более красивое решение этой проблемы — карринг:

for (var i = 0; i < count; i++) {
    window.setTimeout(asyncFunc.bind(null, i), 10000 * i);
}
UFO just landed and posted this here
А что такое декларации/аннотации в Python/Java?
azproduction:
Пользуясь случаем, задам ВОПРОС о корректном освобождении памяти в JS.

Я хочу удалять отработавшие своё функции.
Об этом я, естественно, задумываюсь в конце выполения функции, которую хочу удалить.
Если я напишу наивно:
a = function(){
   //...
  delete a;
}
то могу попасть на утечку памяти: когда функцию удаляю, она исполняется, поэтому физически удалена быть не может. Когда будет выполнена — она останется в памяти, но все указатели уже удалены. Происходит ли такое на практике, читали ли Вы об этом?
Решение может быть таким:
a = function(){
   //...
  var f = arguments.callee;
  setTimeout(function(){delete f;},1);
}
но, во-первых, это почему-то не работает и не даёт ошибку, во-вторых, функция не выполняется, но используется её SCOPE. Поэтому, возможно, она тоже не удалится. Работает так:
c = function(){
   //...
  setTimeout(function(){delete c;},1);
}
c();


delete удаляет только переменную, ссылку. в нормальных браузерах мусор очистится, когда не будет ссылок. в остальных — как повезет.

и да, «delete varName» удаляет «varName» из глобального контекста, не локального. delete корректно работает только для полей объектов: «delete obj.field»
А точнее сказать локальным переменным присваивается флаг DontDelete.
> в нормальных браузерах мусор очистится, когда не будет ссылок
ну, это похоже на ответ «я верю в то, что...». Например, функции, упомянутые в скрипте на некоторой странице, следовательно, никогда не удалятся? А если кто-то будет держать скопы через ссылки (создаст объект, запоминающий каждую временную функцию, создаваемую в скрипте) — тоже?


Если я напишу так: (f = глобальная), функция b тоже не удалится. Значит, дело не в локальности f.
b = function(){
   //...
  f = arguments.callee;
  setTimeout(function(){delete f;},1);
}
В JavaScript, как и во многих других есть объекты и есть ссылки на объекты. Как только на объект перестают ссылаться он удаляется и освобождается память. delete удаляет ссылку тех объектов, у которых нет флага [[DontDelete]].
Например, функции, упомянутые в скрипте на некоторой странице, следовательно, никогда не удалятся? А если кто-то будет держать скопы через ссылки (создаст объект, запоминающий каждую временную функцию, создаваемую в скрипте) — тоже?
Не удаляется.

В вашем примере вы создаете глобальную переменную f и удаляете её после выполнения тела функции b. Логично, что в глобалах будет только b. Что происходит:
Создается глобальная переменная b, которая ссылается на безымянный объект-функцию. При исполнении тела функции b создается глобальная переменная f, которая ссылается на ту же объект-функцию. (Сейчас у нас 2 ссылки на первую безымянную функцию) Потом вызывается setTimeout с порождением другой безымянной функции. Тело которой удаляет ссылку f. Вторая функция удаляется GC т.к. на неё никто не ссылается. В конце у нас 1 ссылка на первую безымянную функцию — b.

Вот ещё пример:
var b = 100;
var a = function (bb) {
    var c = 100;
    return c * bb;
}

a = a(b);

У нас есть глобальная переменная b которая ссылается на 100. Потом мы создаем безымянную функцию на которую ссылается a. Мы вызываем безымянную функцию с параметром b. Создается контекст функции в который входят 2 переменные bb и с. bb при инициализации принимает значение 100 и затем инициализируется с, которая тоже принимает значение 100. Дальше мы перемножаем c * bb и возвращаем значение из функции. Т.к. ссылок на переменных контекста нет, то контекст безымянной функции удаляется, с ним удаляются ссылки bb и c. Дальше мы переписываем ссылку а значением 10000. Теперь на безымянную функцию никто не ссылается и она удаляется. В конце остается 2 ссылки а и b.
Вопрос по eval. Продолжение первого вопроса.
Хорошо, нет проблем, я согласен, что eval — это зло. Но бывают случаи, когда после загрузки страницы нужно выполнить текст. Казалось бы, без eval() или Function() тут не обойтись. Но есть ещё подгрузка скриптов. Она часто используется в штатных случаях, когда надо изолировать DOMContentLoaded от сторонних скриптов, чтобы страница прорисовалась быстро и надёжно. Ничто не мешает подгрузить в свою страницу свой скрипт (читай: текст). По определению, скрипт выполняется в window. Возможно, это тот же частный случай Function().
1) Что говорят в мире об этом?
2) Как быстро или насколько медленно подгрузка скрипта выполняется?
3) Имеем ли мы какие-то бонусы от такой подгрузки вместо Function()?

Пример такой подгрузки я недавно делал в spmbt.kodingen.com/habrahabr/habracut06.user.js — скрипте для показа Google "+1" иконок под статьями на Хабре. ( habrahabr.ru/blogs/google/124057/ ) Грузился сначала гугловский скрипт, затем — мой, который контролировал то, загрузились ли необходимые методы. (По-другому нельзя из-за особенностей исполнения юзер-скриптов, Function могла и не пройти.)
>Продолжение первого вопроса. — первого в смысле по счёту в статье.
Есть очень много решений, который контролируют порядок загрузки скриптов без каких-либо eval'ов, используя обычные script-теги: labjs, $script.js, yepnope,…

Отвечая на второй вопрос, я говорю, что eval в некоторых местах может быть невероятно выгодным, по сравнению с обычным подходом: шаблон Резига и парсинг JSON. В остальных случаях eval портит общепринятую концепцию в обработке данных, когда код обрабатывает данные и возвращает данные. Т.е. он не порождает другой код из данных во время обработки. Случаи когда без этого сложно обойтись (когда код должен порождать код): шаблон Резига и парсинг JSON. XSS это уже вторичная проблема.
Шаблон Резига можно заменить таким решением.

делается скрытый элемент-образец.

например:


при создании нового элемента, делается клон элемента-образца.
потом рекурсивно проходим по этому элементу.
и заменяем prop_var на элементы из объекта.

var obj = {
id: 1,
id2: 2,
value: 'test',
}

примерно такой псевдокод.

var clone = $('#prop_id').clone();
вызов рекусивной функции

в рекурсивной функции проверяем.
if (element.id.match(/^prop_(.*)$/) {
element.id = obj[RegExp.$1];
}

if (element.value.match(/^prop_(.*)$/) {
element.value = obj[RegExp.$1];
}

Можно и innerHTML менять.

— Минус подхода в том, что нужно либо задавать набор свойств элементов, которые обходить (id, value, innerHTML).
Либо обходить сразу все свойства, что медленно.
Но обычно нужно обходить только id, value и innerHTML

Надо замерить этот способ шаблонизатор Резига

*
делается скрытый элемент-образец.

например:
<div id=«prop_id»>
<input type=«text» value=«prop_id2» value=«prop_value»>
</div>
> получился очень кривой инпут
Почитайте о Contenteditable
Для звука есть ещё старый-престарый тег BGSOUND.
По вопросу 7, уточнение: приведённый код годится только для последнего потомка, appendChild кладёт объект в конец.
Пишем исправленную функцию:
<script>onload = function(){
function resetEvents(element){
	var newEl = element.parentNode.insertBefore(element.cloneNode(true), element);
	element.parentNode.removeChild(element);
	return newEl;

}
document.getElementById('bb').addEventListener('click', function(){alert(2)},!1);
resetEvents(document.getElementById('bb'))
}</script>
<div><b id="bb" onclick="alert(1)">пример</b> <i>шум</i></div>
К ответу 13: на самом деле, лучше, наверное (для скорости), не к DOM-элементам приписывать атрибуты с хешами, а наоборот, к конструкциям JS приписывать DOM-элементы, создавая доступ к ним. Сравнивали ли где-то в публикациях разницу в скорости доступа?

К ответу 15: или предложить пользователю установить юзерскрипт/расширение, которое для того сервера будет грузить картинку, а затем отдавать её в бинарном виде своему серверу или перекидывать в свой фрейм методом window.name. Правда, с бинарными данными сложно работать, надо такое расширение сделать один раз… Не все произвольные пользователи согласятся установить юзерскрипт.
1. Я не понял первый вопрос, пожалуйста, переформулируйте.
2. Предполагается, что пользователь не установит расширение — ничего не будет работать. Да и ставить расширение для того, чтобы работал сайт — это как-то не по вебски.
1. Это не вопрос, а рассуждение об эффективности (скорости) доступа. Если определим переменную JS у DOM-объекта — .., это работать будет, но время доступа в такой конструкции, скорее всего, будет дольше, чем у
(Оборвалось.)
1. Это не вопрос, а рассуждение об эффективности (скорости) доступа. Если определим переменную JS у DOM-объекта — .., это работать будет, но время доступа в такой конструкции, скорее всего, будет дольше, чем у
(А, понял, это иезутиские теги сайта.)
1. Это не вопрос, а рассуждение об эффективности (скорости) доступа. Если определим переменную JS у DOM-объекта — .., это работать будет, но время доступа в такой конструкции, скорее всего, будет дольше, чем у <object>.{ <property> :{method: ...}, element: <DOM>}. Поэтому, хотя такое возможно, лучше всегда избегать использования свойств у элементов. По крайней мере, там, где понадобится скорость. И это замечание по скорости лучше приписать к ответу. (Но у меня нет данных и простеньких тестов, чтобы это показать.)

2. Поставить, чтобы работали расширенные функции сайта — это вполне по веб. Например, «Что надо, чтобы проигрывались файлы *.mov? Установить в системе кодек от QuickTime иили аналогичный. А браузер чем хуже? Для него бывают плагины-движки для особых типов данных (моделирование 3D, игры). А тут всё гораздо проще, без особых технологий.
Как скопировать выделенный текст в буфер обмена кнопочкой (ссылкой)?
Спасибо.
Универсальное решение есть только на флеше: Копируем в буфер обмена
Из JavaScript копирование поддерживает только IE и Safari
IE: window.clipboardData.setData("Text", "PewPewPew");
Safari: через выделение текста и execCommand(«Copy»)

Вообще нормальной практикой считается выделять текст и просить пользователя нажать Ctrl+C — см. goo.gl
Спасибо за ответ.
Но это же позор ходячий © Umputun! А в Chrome?
Может быть у вас есть идеи, как избавиться от проблемы расширения файлов, например, как здесь: jszip.stuartk.co.uk/, не используя флеш.
Долго думал над проблемой — универсального ответа кроме флеша не нашел. Может быть когда-нибудь нам поможет Fire API: Writer.
Ох, спасибо, ребята, спасли мне около полутора часов жизни :)
добавить бы якорных ссылок в оглавление…
Хотел.
<h4 id="faq-1"> — парсер удаляет id
<a name="faq-1"> — удаляет name
Ещё варианты? :)
4. странно приводить в пример код, где большую часть времени занивают сложения, умножения, сравнения, пополнение динамического массива… конечно разницы между локальной и глобальной переменной не будет. по моим тестам присвоение значения локальной переменной в фф занимает 0.029 микросекунды, а глобальной — 0.176
Вопрос: в чём смысл capturing? То есть addEventListener(a, b, true);?
Я понимаю его суть, но зачем он может использоваться? На примерах?
Как оно взаимодействует с ещё одним эвентом, на этом же элементе, который использует баблинг?
Как говорится в придании, в начале было 2 браузера — один поддерживал capturing, а другой bubling. В итоге победил ИЕ. Ие старых версий поддерживал только баблинг, поэтому у разработчиков и не было особого выбора.
Вообще всплытие события — это наиболее логичный вариант. Например мы кликаем на элемент, создавая эпицентр события. Эхо этого события может донестись до внешних элементов. С капчем мы как-будто наводимся на элемент и в конце выстреливаем последний эвент.

capturing и bubling делят одно событие т.е. (это тоже событие, но с разных сторон) один может прервать другой. Как бы мы не забиндили события сперва всегда вызвается capturing (логично) начиная с окна и заканчивая целью и только потом идет фаза bubling в обратную сторону.
Сходу мне сложно придумать юзкейс для capturing. Вот что пришло в голову:
1. Временное терменирование всех баблинг событий, используя capturing и stopPropagation()
2. Пикер элементов окна по координате.

Вот тест и пример 1 в одном месте. Надо раскоментить строку event.stopPropagation(), чтобы сработал пример 1. jsfiddle.net/azproduction/bbQ42/
Спасибо. механизм теперь понятен. Но таки интересны реальные юзкейсы, можт кто придумают. У меня есть впечатление, что я видел где-то в плагинах ДжиКвери связанных с инпутом. Но я так и не понял тогда тайного смысла использовать именно capturing.
Опера по-другому обрабатывает нажатие клавиш.
Может здесь копать надо.
По поводу колеса мыши:
На сколько я помню, у opera это работает через одно место. Т.е. наоборот: скролл вверх возвращает отрицательное значение. Когда-то очень сильно этому удивился и долго матюкался
Это был баг примерно версий 9.2 и меньше. О них уже можно забыть.
Когда я с этим столкнулся, это была уже 10+
Возможно вы с этим столкнулись используя старый плагин, который сам «чинил» поведение в Опере.
Да вроде нет.
Без плагинов делал, просто отслеживал изменение значения по событию.
Пришлось как раз наоборот вручную фиксить…
Ну да ладно, сейчас некогда тетсить, так что поверю вам ))
Хочу с помощью JS, сохранить кусок страницы(определенный мной) как картинку (JPG например) и чтобы в картинке было все именно так как выглядет в браузере пользователя.
Бывает такое? или не родились еще такие стандарты?
левый софт не предлагать типа Gyazo или JetScreenshot или плагины к браузеру.
Я так понял что в КАнвасе можно хоть черта делать и затем этот канвас — выгрузить в виде битмапа, и это то что вы пишите уже костыли для того чтобы можно было обычный HTML пихать в канвас, да?
Выгружать в JPG можно толкьо из CANVAS я так понял?
Подскажите, после использования переменных в сильно AJAX приложении нужно ли обнулять переменные var в функциях? Что вообще с ними происходит после выхода из функций?
Не нужно. Как только удаляются все ссылки на объект/функцию или контексты память освобождается автоматически.
Вот тут небольшой пример что происходит habrahabr.ru/blogs/javascript/124327/#comment_4086833
Существует ли способ в JS получить имя переменной в виде string для последующего использования?
Не понял, переформулируйте, пожалуйста, вопрос.
var MyVarName;

Существует ли способ получить 'MyVarName' из JS?
Если переменная объявлена в локальном контексте, то её имя получить нельзя(можно было в ФФ 3.6-, используя __parent__).
Если переменная объявлена глобально, то её имя можно получить, используя:
for (var name in window) {
    console.log(name);
}

Можно получить все имена свойств объекта, можно получить имя функции (NFE, FD), можно так или иначе получить имена аргументов функции
var test = function a(b,c,d) {};

// Function name
var name = test.name || // Modern browsers
           String(test).match(/function(?:\s*)?([a-zA-z$_][a-zA-z$_]*)?/)[1]; // IE
// Arguments
var args = String(test).split(')')[0].split('(')[1].split(/,\s?/); 
Можно ли добраться до CSS псевдоэлементов :before и :after через JS?
Для того, чтобы накидать/изменить их стили и узнать значение свойства content.
Через style это сделать нельзя, но можно пропатчить таблицы стилей.
Например, у нас есть элемент #elem. Мы хотим поменять css-свойство :before те фактически добавить блок:
#elem:before {content: "pewpew"; color: red;}

Добавляем:
document.styleSheets[0].insertRule('#elem:before {content: "pewpew"; color: red;}', 0);
document.styleSheets[0].cssRules[0].style.сolor= '#555'; // Меняем

Ну и для ИЕ:
document.styleSheets[0].addRule('#elem:before', 'content: "pewpew"; color: red;', 0);
document.styleSheets[0].rules[0].style.сolor= '#555;

Вобщем можно написать обертку, которая будет делать всю эту магию за нас, ну или использовать jQuery#before/after.
Почитать www.w3.org/TR/DOM-Level-2-Style/Overview.html
UFO just landed and posted this here
for(var t=0;t<10;t++){
    console.time('native'+t);

    var res = 0; 
    for(var idx = 0; idx < 1000; idx++){
        res = Math.round( Math.random()*((new Date()).getTime()/idx) )
    };
 
    console.timeEnd('native'+t);
    console.time('eval'+t);

    var res = 0; 
    for(var idx = 0; idx < 1000; idx++){
        eval('res = Math.round( Math.random()*((new Date()).getTime()/idx) )')
    };

    console.timeEnd('eval'+t);
}
native0: 6.568ms VM11296:10
eval0: 28028.307ms VM11296:18
native1: 9.370ms VM11296:10
eval1: 28053.163ms VM11296:18
native2: 6.128ms VM11296:10
eval2: 28058.792ms VM11296:18
native3: 6.266ms VM11296:10
eval3: 28059.410ms VM11296:18
native4: 5.758ms VM11296:10
eval4: 28071.947ms VM11296:18
native5: 5.366ms VM11296:10
eval5: 28079.450ms VM11296:18
native6: 7.372ms VM11296:10
eval6: 28091.091ms VM11296:18
native7: 8.352ms VM11296:10
eval7: 28088.152ms VM11296:18
native8: 6.371ms VM11296:10
eval8: 28073.608ms VM11296:18
native9: 6.120ms VM11296:10
eval9: 28083.928ms VM11296:18
UFO just landed and posted this here
Вызвать код без ебала значительно быстрее, чем с евалом — об этом речь.
UFO just landed and posted this here
Как eval может заменить script?
UFO just landed and posted this here
Что-то числа слишком большие. Вот правильные результаты:
for(var t=0;t<10;t++){
    console.time('native'+t);

    var res = 0; 
    for(var idx = 0; idx < 1000; idx++){
        res = Math.round( Math.random()*((new Date()).getTime()/idx) )
    };
 
    console.timeEnd('native'+t);
    console.time('eval'+t);

    var res = 0; 
    for(var idx = 0; idx < 1000; idx++){
        eval('res = Math.round( Math.random()*((new Date()).getTime()/idx) )')
    };

    console.timeEnd('eval'+t);
    console.time('func'+t);

    var res = 0; 
    for(var idx = 0; idx < 1000; idx++){
        void function( idx ){
            res = Math.round( Math.random()*((new Date()).getTime()/idx) )
        }( idx )
    }

    console.timeEnd('func'+t);
}
native0: 2.552ms VM40121:10
eval0: 75.564ms VM40121:18
func0: 4.598ms VM40121:28
native1: 4.724ms VM40121:10
eval1: 63.414ms VM40121:18
func1: 6.070ms VM40121:28
native2: 3.579ms VM40121:10
eval2: 66.886ms VM40121:18
func2: 6.601ms VM40121:28
native3: 4.170ms VM40121:10
eval3: 61.786ms VM40121:18
func3: 5.309ms VM40121:28
native4: 4.719ms VM40121:10
eval4: 69.186ms VM40121:18
func4: 5.035ms VM40121:28
native5: 4.029ms VM40121:10
eval5: 66.522ms VM40121:18
func5: 5.816ms VM40121:28
native6: 5.628ms VM40121:10
eval6: 72.350ms VM40121:18
func6: 4.726ms VM40121:28
native7: 4.374ms VM40121:10
eval7: 68.818ms VM40121:18
func7: 5.549ms VM40121:28
native8: 5.118ms VM40121:10
eval8: 73.261ms VM40121:18
func8: 6.945ms VM40121:28
native9: 5.579ms VM40121:10
eval9: 73.521ms VM40121:18
func9: 5.297ms VM40121:28

Как видно, eval на порядок дольше исполняется, чем создание замыкания.
UFO just landed and posted this here
Я прекрасно понимаю о чём вы говорите, но спорите вы с выдуманными тезисами. Да, eval — запуск интерпретации. И поэтому он медленный. И поэтому не надо использовать его там, где хватило бы и замыкания.
UFO just landed and posted this here
Sign up to leave a comment.

Articles