Пользователь
0,0
рейтинг
17 июля 2011 в 17:31

Разработка → JavaScript F.A.Q: Часть 2

image

Около 2-х месяцев назад я и TheShock собирали вопросы по JavaScript в теме FAQ по JavaScript: задавайте вопросы. Первая часть, те вопросы, которые достались мне, появилась буквально через несколько дней JavaScript F.A.Q: Часть 1, а вот вторая часть все не выходит и не выходит. TheShock сейчас переезжает в другую страну и поэтому ему не до ответов. Он попросил меня ответить на его часть. Итак вторая часть ответов — те вопросы, которые достались тоже мне.

В этой части:
  1. Почему eval = evil?
  2. Чем заменить eval?
  3. Как проиграть звук используя JS без flash? Какие форматы поддерживаются?
  4. На сколько использование глобальных переменных замедляет скрипты?
  5. Как запретить выделение текста на JS?
  6. (function() {})(); Для чего используется такая конструкция?
  7. Как сбросить события и восстановить состояние элемента?
  8. Как корректно передавать js-команды с параметрами в ajax приложениях?
  9. Для чего в jQuery передавать window если он и так глобальный и зачем undefined?
  10. Как узнать в какую сторону юзер крутит колесо мыши?
  11. Есть ли событие focus у элемента, если нет, то возможно ли его реализовать?
  12. Как сделать неизменяемый объект (freez)?
  13. Возможно ли сделать кастомную реализацию DomElement так чтобы при разборе для подмножества тегов использовалась она?
  14. Какой клиентский MVC-фреймворк посоветуете?
  15. Обход ограничений по Referer
  16. GOTO в Javascript
  17. Наследование в JavaScript. Как идеологически правильно заэкстэндить Man и Woman от Human
  18. Возможно ли устроиться javascript программистом без копания в верстке и server-side?
  19. Какие есть практики передачи данных с сервера в код, который исполняется из .js?
  20. Можно ли подцепить(эмулировать) поведение DOMInputElement'a с помощью обыных DOMElemen'ов(div)?
  21. Проблема приватных и публичным методов
  22. Как с помощью JS сделать одновременный выбор и загрузку нескольких файлов?
  23. При загрузке файлов с использованием File и FormData как определить не пытается ли пользователь загрузить директорию?
  24. Есть ли в JavaScript аналоги декларациям/аннотациям (термины из Python/Java соответственно)?

1. Почему eval = evil?


1. Eval нарушает привычную логику программы, порождая код.
2. Он выполняет код в текущем контексте и может его изменять — потенциальное место для атак. Контекст можно подменить, выполнив такой трюк:
(1,eval)(code);

Что тут происходит: eval — это та же функция, которая зависит от способа вызова. В этом случае мы меняем контекст на глобальный.

3. Порожденный eval-ом код сложнее отлаживать.
4. Eval очень долго выполняется.

Почитать
Global eval. What are the options?
Куча вопросов на stackoverflow:
stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil
stackoverflow.com/questions/646597/eval-is-evil-so-what-should-i-use-instead
stackoverflow.com/questions/86513/why-is-using-javascript-eval-function-a-bad-idea


2. Чем заменить eval?


Лучшая замена eval — хорошая структура кода, которая не предполагает использование eval. Однако от eval не всегда легко отказаться (Парсинг JSON, Шаблоонизатор от Резига).
Хорошая замена eval — конструктор Function. Но это тот же eval, который выполняется в глобальном контексте.
new Function('return ' + code)();
// Это практически эквивалентно
(1,eval)(code);

3. Как проиграть звук используя JS без flash? Какие форматы поддерживаются?


Самое простое решение:
<audio src="http://developer.mozilla.org/@api/deki/files/2926/=AudioTest_(1).ogg" autoplay>
  Your browser does not support the <code>audio</code> element.
</audio>

Но этим HTML5 audio не ограничивается. Форматы: Ogg Vorbis, WAV PCM, MP3, AAC, Speex (зависит от конкретного браузера) О форматах: Audio format support

Почитать
HTML5 Audio и Game Development: баги браузеров, проблемы и их решения, идеи
WHATWG — The audio element
MDN — HTML audio
Пример: Creating a simple synth
Пример: Displaying a graphic with audio samples
Пример: Visualizing an audio spectrum
Пример: Creating a Web based tone generator

4. На сколько использование глобальных переменных замедляет скрипты?


Зависит от конкретного браузера. В современных браузерах этот момент оптимизирован и разницы нет. В старых доступ до глобальной переменной может быть медленнее в 2 раза.

Вот тест: jsperf.com/global-vs-local-valiable-access
Пожалуйста, учитывайте тот факт, что это тест, каждая операция прогоняется до 1 миллиона раз в секунду. Даже если прирост для теста 30%, то фактический прирост для 1й операции будет мизерный (микросекунды).

5. Как запретить выделение текста на JS?


Есть несколько вариантов.

1. Выполнить preventDefault для событий onselectstart и onmousedown
var element = document.getElementById('content');
element.onselectstart = function () { return false; }
element.onmousedown = function () { return false; }

2. Добавить атрибут unselectable
$(document.body).attr('unselectable', 'on')

3. Добавить стиль user-select: none
.g-unselectable {
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  user-select: none;
}

Есть и безумные решения данной проблемы:
4. По таймеру чистить Range, TextRange, Selection
Не буду объяснять. Как работать с Selection можно посмотреть тут: Range, TextRange и Selection
5. Рендерить текст на канвасе
6. Использовать disabled textarea
<textarea disabled="disabled" style="border:none;color:#000;background:#fff; font-family:Arial;">
    Как запретить выделение текста на JS?
</textarea>

Вот микроплагин для jQuery, использующий методы 1-3
$.fn.disableSelection = function() {
    function preventDefault () {
        return false;
    }
    $(this).attr('unselectable', 'on')
           .css('-moz-user-select', 'none')
           .css('-khtml-user-select', 'none')
           .css('-o-user-select', 'none')
           .css('-msie-user-select', 'none')
           .css('-webkit-user-select', 'none')
           .css('user-select', 'none')
           .mousedown(preventDefault);
           .each(function() { 
               this.onselectstart = preventDefault;
           });
};

Этот плагин не спасет от Ctrl+A Ctrl+C в тех браузерах, которые не поддерживают user-select или unselectable. Помните: запрещать выделение — зло.

6. (function() {})(); Для чего используется такая конструкция?


Это называется Immediately-Invoked Function Expression (IIFE) — Функция-выражение, которая была вызвана сразу же после создания.

Вот несколько способов создать IIFE
(function(){ /* code */ })(); // Обычная IIFE
(function(){ /* code */ }()); // Крокфорд рекомендует

// Поставить функцию в состояние "значение"

true && function(){ /* code */ }();
0,function(){ /* code */ }();

// Можно создать, используя оператор в качестве префикса

!function(){ /* code */ }(); // Facebook style
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

// Используя new

new function(){ /* code */ }
new function(){ /* code */ }() // Если необходимы аргументы, то ставим скобки

Где это используется:
1. Если вам нужно, чтобы какие-то переменные не попали во внешнее окружение (подобие let)
var APPLICATION_ID = function(){
    var keys = {
       'pew.ru': 15486,
       'pew.pew.ru': 15487,
       'pew.pew.pew.ru': 1548
    };
    
    return keys[window.location.host];
}();

2. Создание локального замыкания — основа для модулей, объектов, запоминающих состояние
var Module = (function(){
    function privateMethod () {
    
    };
    
    return {
       publicMethod: function () {
           return privateMethod();
       }
    };
}());

Почитать
Immediately-Invoked Function Expression (IIFE)
ECMA-262-3 in detail. Chapter 5. Functions
Functions and function scope
Named function expressions
JavaScript Module Pattern: In-Depth
Closures explained with JavaScript

7. Как сбросить события и восстановить состояние элемента?

У нас есть какой-нибудь DOM-объект с изначально заданными при помощи HTML и CSS параметрами «А». В процессе работы мы динамически меняем параметры на «В». Существует ли простой способ «сбросить» состояние объекта первоначальное состояние «А»? Аналогично с событиями. Как сделать откат на первоначальное состояние?
Откатить состояние объекта не получится. DOM не хранит состояние потому, что элементы могут быть получены различным образом: непосредственно из кода html и сгенерированные JavaScript.

С событиями все проще. В DOM есть хороший метод cloneNode. Мы просто клонируем элемент со всем его содержимым (он не будет иметь событий) и подменяем старый элемент новым.
// Для теста
$('input').click(function(){alert('')});

function resetEvents(element) {
    var newElement = element.cloneNode(true);
    element.parentNode.insertBefore(newElement, element); // fixed by @spmbt
    element.parentNode.removeChild(element);

    return newElement;
}

resetEvents($('input')[0]);

// Клик на инпут ничего не даст 

8. Как корректно передавать js-команды с параметрами в ajax приложениях?

Как корректно передавать js-команды с параметрами в ajax приложениях без возни с кавычками? Это относится к передаче данных в ajax-системе, описанной выше способом. При передаче сложных команд с несколькими уровнями вложенности параметров (например, «setTimeout('alert(»Бум!!!");', 200);" ) наблюдаются проблемы при использовании кавычек. Как их разрешить или есть ли какие-нибудь общие правила их оформления?
Пересылать код от сервера к клиенту — это плохо. Это аналогично проблеме с eval. Правильно, быстро и безопасно передавать не код, а данные, которые будет обрабатывать браузер.

Можно использовать RPC. Очень прозрачный RPC можно получить, используя NowJS. NowJS — фреймворк, позволяющий выполнять функции на сервере как будето вы выполняете их на клиенте.

Вот пример с сайта:
На сервере: Node.JS server
var nowjs = require("now");

// тут создаем http сервер

var everyone = nowjs.initialize(httpServer);

everyone.now.getServerInfo = function(callback){
  db.doQuery(callback);
}

На клиенте
now.getServerInfo(function(data){
  // Данные запроса
});

Просто, прозрачно и красиво!

Почитать
Getting Started with NowJS

9. Для чего в jQuery передавать window если он и так глобальный и зачем undefined?


jQuery создает замыкание и в него пробрасывает window и undefined.
1. undefined передается для уменьшение объема кода и обхода проблемы с перезаписью undefined. jQuery часто сравнивает с undefined и при сжатии undefined превратиться в однобуквенную переменную.
2. window передается для уменьшение объема кода. jQuery применяет хорошую политику, отделяя методы окна от своих, используя window.setInterval() и прочие. При сжатии window превратиться в однобуквенную переменную, в коде будет c.setInterval()

10. Как узнать в какую сторону юзер крутит колесо мыши?


Есть 2 события, реагирующие на скролл колесом 'DOMMouseScroll' (Firefox) и 'mousewheel' (остальные)
Как навесить событие, думаю всем понятно, посмотрим его обработчик
function wheel(event){
    var delta = 0;
    if (!event) event = window.event; // Событие IE.
    // Установим кроссбраузерную delta
    if (event.wheelDelta) { 
        // IE, Opera, safari, chrome - кратность дельта равна 120
        delta = event.wheelDelta/120;
    } else if (event.detail) { 
        // Mozilla, кратность дельта равна 3
        delta = -event.detail/3;
    }
    if (delta) {
        // Отменим текущее событие - событие поумолчанию (скролинг окна).
        if (event.preventDefault) {
            event.preventDefault();
        }
        event.returnValue = false; // для IE
        
        // если дельта больше 0, то колесо крутят вверх, иначе вниз
        var dir = delta > 0 ? 'Up' : 'Down',
    }
}

Для jQuery Есть Mouse Wheel Plugin jQuery Mouse Wheel Plugin Demos

Не все элементы могут порождать скролл-событие. Зависит от конкретного браузера. Подробнее: scroll and mousewheel

11. Есть ли событие focus у элемента, если нет, то возможно ли его реализовать?


Фокус — это логическое событие, показывающее то, что объект сейчас выделен.

Если почитать стандарт то там написано, что события focus и blur есть только у элементов формы: LABEL, INPUT, SELECT, TEXTAREA, и BUTTON. По стандарту никакие другие элементы не могут генерировать это событие. Для всех остальных элементов есть другие события DOMFocusIn и DOMFocusOut. IE, как всегда имеет свои события: focusin и focusout.
Firefox тоже не особо подчиняется стандарту у него некоторые элементы, кроме формы могут выбрасывать focus и blur.

Посмотрите тест событий focus, blur, focusin, focusout, DOMFocusIn, DOMFocusOut:
www.quirksmode.org/dom/events/tests/blurfocus.html
Если слушать все 3 события у элемента, то мы можем определить получил ли любой элемент фокус или нет.

Почитать
blur and focus

12. Как сделать неизменяемый объект (freeze)?


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

Object.preventExtensions — самое слабое ограничение. Объект не может получать дополнительные параметры
Object.sealpreventExtensions + любые параметры не могут удаляться
Object.freezepreventExtensions + seal + параметры становятся только на чтение
Эти новые методы поддерживаются далеко не во всех браузерах

Почитать
New in JavaScript 1.8.5
Object.preventExtensions/seal/freeze Function (JavaScript)
ECMAScript 5 compatibility table

13. Возможно ли сделать кастомную реализацию DomElement так чтобы при разборе для подмножества тегов использовалась она?


Напрямую нет. DOM отделен от JavaScript и поэтому есть некоторые ограничения.
1. Нет конструкторов конкретных элементов — есть только фабрика элементов — document.createElement('') и некоторые методы строки для создания элементов — String.prototype.anchor и тп. HTMLElement и HTMLDivElement — это не конструкторы.
2. Даже если мы эмулируем конструктор, то DOM «очистит» объект после вставки в дерево.
Пример
// Создаем наш какбы-конструктор
var HTMLZzzElement = function () {
   var self = document.createElement('zzz');
   self.__proto__ = HTMLZzzElement.prototype;
   return self;
};

// Наследуем от HTMLUnknownElement
function F(){}
F.prototype = HTMLUnknownElement.prototype;

HTMLZzzElement.prototype = new F();
HTMLZzzElement.prototype.constructor = HTMLZzzElement;
HTMLZzzElement.prototype.pewpewpew = function () {};

// Создаем
var zzz = new HTMLZzzElement();

// Да, работает
zzz.innerHTML = 'Yohoho!';

// Проверяем
console.log(zzz instanceof HTMLZzzElement); // true
console.log(zzz instanceof HTMLUnknownElement); // true
console.log(zzz instanceof HTMLElement); // true
console.log(typeof zzz.pewpewpew); // function
//Все хорошо - все есть

// Засовываем в DOM
document.body.appendChild(zzz);

// Получаем обратно
zzz = document.querySelector('zzz')

// Объект очистили
console.log(zzz instanceof HTMLZzzElement);  // false
console.log(zzz instanceof HTMLUnknownElement); // true
console.log(zzz instanceof HTMLElement); // true
console.log(typeof zzz.pewpewpew); // undefined

Однако, мы можем дописать к прототипу «класса» HTML элемента методы и свойства и потом использовать в будущем:
HTMLUnknownElement.prototype.pewpewpew = function () {};

console.log(typeof zzz.pewpewpew); // function

Цепочка наследования «классов» такая
Node -> Element -> HTMLElement -> HTMLDivElement/HTMLMetaElement ...
Вы можете дописать метод к прототипу элемента, однако помните, что пропатчивать чужие объекты — это плохо!

Этот способ будет работать только в современных браузерах. В ИЕ8 и ниже html элементы берутся как будето из воздуха, поэтому их прототипы нельзя переписать на низком уровне:
>>document.createElement('div').constructor
//undefined -- даже не null...

>>document.createElement('div') instanceof Object
//false -- забавно, правда? хотя магия с toString говорит обратное
>>Object.prototype.toString.call(document.createElement('div'));
//"[object Object]"

14. Какой клиентский MVC-фреймворк посоветуете?


Фреймворков много:
JavaScriptMVC
Backbone.js
SproutCore
TrimJunction

Посмотрите все и выберете лучше тот, который вам по душе. Я плотно работал только с Backbone.js

Почитать
Написание сложных интерфейсов с Backbone.js
И опять про MVC
MVC в JavaScript

15. Обход ограничений по Referer

На сервере blabla.ru лежит картинка, сервер не отдает её если передал Referer с другого сервера (pewpew.com). Как получить содержимое картинки и показать пользователю?
Напрямую из браузера никак. Можно сделать серверный прокси, который будет менять Referer.

16. GOTO в Javascript

Собственно вопрос как повторить поведение goto в JavaScript. (Про хороший стиль и goto рассказывать не нужно :) речь идёт про генерированный код. и восстановление блоков и циклов процесс весьма не тривиальный)
В JavaScript есть метки, но нет goto (есть только зарезервированное слово). Метка выглядит так же как и во всех других языках, но работают они иначе. Метка выделяет блок. Внутри блока могут быть другие помеченые блоки. На метку можно переходить используя break и continue, которые есть внутри этого блока.

Пример:
loop1: for (var a = 0; a < 10; a++) {
   if (a == 4) {
       break loop1; // Только 4 попытки
   }
   alert('a = ' + a);
   loop2: for (var b = 0; b < 10; ++b) {
      if (b == 3) {
         continue loop2; // Пропускаем 3
      }
      if (b == 6) {
         continue loop1; // Продолжаем выполнят loop1 'finished' не будет показано
      }
      alert('b = ' + b);
   }
   alert('finished')
}

block1: {
    alert('hello'); // Отображает 'hello'
    break block1;
    alert('world'); // Никогда не будет отображено
}
goto block1; // Parse error - goto нет

17. Наследование в JavaScript. Как идеологически правильно заэкстэндить Man и Woman от Human


Идеологически правильно использовать прототипное наследование. Процесс наследования хорошо описан в статье "Основы и заблуждения насчет JavaScript"

18. Возможно ли устроиться javascript программистом без копания в верстке и server-side?


Зависит от проекта, размера компании и вашего опыта.
Если вы пойдете разработчиком браузерных-игр в крупную компанию, то шанс, что бы будете заниматься только JavaScript очень высок. Крупная компания может себе позволить отдельного интерфейсного программиста, а мелкая — нет. Вот повезет ли вам без опыта в крупной?!
Если вы только начинаете изучать веб, то вам стоит пойти туда где хотят всего.

19. Какие есть практики передачи данных с сервера в код, который исполняется из .js?


Способов передачи данных очень много: XHR, SSE, WebSockets, JSONP:
Реализация HTTP server push с помощью Server-Sent Events
Новые возможности XMLHttpRequest2
Создание приложений реального времени с помощью Server-Sent Events
Много всего про ajax

Какой транспорт и формат выбрать вам — зависит от требований к приложению. Например, если это чат, то лучше SSE или WebSockets (время доставки сообщения критично). А если этот чат должен работать кроссдоменно и в ИЕ6, то придется добавить магии с iframe или flash.

20. Можно ли подцепить(эмулировать) поведение DOMInputElement'a с помощью обыных DOMElemen'ов(div)?


На низком уровне нельзя. Причину я объяснил в 13-м примере. Можно сделать обертку, которая будет эмулировать Input, в основе которой будет DOMDivElement.
Я набросал простенький эмулятор jsfiddle.net/azproduction/Kz4d9 — получился очень кривой инпут (не умеет сабмититься, не умеет терять фокус и т.п.).
Эту же проблему можно решить более элегантно(спасибо Finom, что напомнил), добавив всего 1 атрибут: contenteditable, который поддерживают только десктопные браузеры — получился тот же инпут, но гораздо проше и лучше jsfiddle.net/azproduction/ZTqRe

21. Проблема приватных и публичным методов

Если возникает желание использовать частные переменные и методы объектов, в JavaScript есть проблема доступа к частным методам из публичных. А привилегированные методы, определенные внутри конструктора, если я правильно понимаю, дублируются для каждого экземпляра, что не очень хорошо с точки зрения производительности. Как насчет такого подхода?
TheShock:
Неплохой подход. По крайней мере лучше, чем объявлять все методы в конструкторе.
// our constructor
function Person(name, age){
    this.name = name;
    this.age  = age;
};

// prototype assignment
Person.prototype = new function(){

    // we have a scope for private stuff
    // created once and not for every instance
    function toString(){
        return this.name + " is " + this.age;
    };

    // create the prototype and return them
    this.constructor = Person;

    this._protectedMethod = function (){
        // act here
    };

    this.publicMethod = function() {
        this._protectedMethod();
    };

    // "magic" toString method
    this.toString = function(){
         // call private toString method
         return toString.call(this);
    }
};

Модульный подход:
var Module = (function (window) {
    function privateFunction() {
        console.log('pass');
    };

    var privateClass = function () {};

    var publicClass = function () {};
    publicClass.prototype.publicMethod = function () {
        privateFunction(); // ...
    };
    
    return { // Экспортируем
        publicClass: publicClass
    };
}(this));

// Используем
(new Module.publicClass()).publicMethod();

22. Как с помощью JS сделать одновременный выбор и загрузку нескольких файлов?


Все уже давно и не по разу написано:
1. HTML5 File API: множественная загрузка файлов на сервер
2. Загрузка файлов с помощью HTML5 и сколько раз мы сказали нехорошие слова
3. Загрузка браузером нескольких файлов
4. ajax загрузка нескольких файлов с php формой
5. Загрузка файлов с помощью html5 File API, с преферансом и танцовщицами

23. При загрузке файлов с использованием File и FormData как определить не пытается ли пользователь загрузить директорию?

При загрузке файлов с использованием File и FormData — как определить не пытается ли пользователь загрузить директорию. Свойство File.type к сожалению не помогает (для некоторых типов, как и для дир — пустая строка), свойство size — тоже (ff и сафари по разному показывают)?
Насчет File.type говорится, что это mime-тип и если он не определен, то он становится пустой строкой. mime-тип, как водится получается из расширения файла/папки. Если мы подсунем настоящий файл с именем «pewpew», то mime будет "", а если директорию с именем «pewpew.html», то mime будет "text/html". Нам это не подходит.
Размер папки всегда 0, но могут быть и файлы размером 0 байт. Для 100% уверенности нам это не подходит.
В стандарте www.w3.org/TR/FileAPI не говорится о директориях отдельно, но там отмечено, что браузеры могут выкидывать SECURITY_ERR если пользователь пытается работать с не корректным файлов (защищенным, не доступным на чтение и т.п.) Будем исходить из этого.

В основу я взял пример Drag and Drop
Будем использовать FileReader для определения типа ресурса. В фаерфоксе если читать папку как файл, то метод reader.readAsDataURL выбрасывает исключение. В хроме это исключение не выбрасывается, но вызывается событие onerror у reader. Скрестим и получим функцию для определения файл это или нет.
function isReadableFile(file, callback) {
    var reader = new FileReader();
    reader.onload = function () {
        callback && callback(true);
        callback = null;
    }
            
    reader.onerror = function () {
        callback && callback(false);
        callback = null; 
    }
        
    try {
        reader.readAsDataURL(file); 
    } catch (e) {
        callback && callback(false);
        callback = null; 
    }
}

В функции я для подстраховки удаляю callback, чтобы он не вызвался повторно (в будущем поведение FileReader может измениться).
Пример использования jsfiddle.net/azproduction/3sV23/8

Можно, было ограничится размером файла. Если 0, то считать что это не файл (ибо странно загружать файл размером 0 байт).

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


Прямых аналогов нет.

PS Если я забыл ответить на какой-то вопрос — задайте его ещё раз. Если у вас есть вопросы, на которых нет ответов ни в первой ни во второй части — задавайте — они будут в третей ;)
Mikhail Davydov @azproduction
карма
449,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (100)

  • 0
    А можно еще вопрос?
    Как правильнее обрабатывать большие объемы данных в Javascript избегая зависания интерфейса и сообщений от браузера о «долгом скрипте» (длинный список данных для отчета, отдающийся в JSON)? Есть ли какие-либо библиотеки для этого?
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Для этой цели и были созданы Воркеры(Workers). Когда-то давно я предлагал решение данной проблемы: WXHR: старый добрый XHR со вкусом Web Workers
    • +3
      Уже было в этом цикле статей, 8 пункт habrahabr.ru/blogs/javascript/120192/
    • 0
      Разбить на короткие блоки, вызывая их по setTimeout() или упомянутые выше WW.
    • 0
      Задача вполне реальная, и, как мне казалось, кем нибудь уже сотни раз решалась.
      WW работают не везде, и проблема с производительностью чаще касается браузеров, в которых WW не работают.
      SetTimeout хоть и является очевидным решением, но очень странно видоизменяет код и скорее выглядит как костыль (либо приходится использовать его везде, либо дописывать когда количество пользовательских данных достигает объемов которые тормозят работу браузера).
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          глупость. eval — не нужен. ниже — обсудили, почему.
        • 0
          есть ещё третий вариант — через замыкания.
        • 0
          И четвертый, присвоить колбеку свойство с нужным значением как объекту. Где то ниже TheShock писал про .bind — 5й получается :) Хотя eval/new Function можно вообще за варианты не считать.

          Ну писать, что в функцию передаются ссылки значений — некорректно.
          • 0
            Вариант с bind, кстати, называется "карринг"
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      По поводу документирования кода, также интересно было бы услышать мнение о подходе, рекомендованном MS — включении xml-документации в тело функций.
      weblogs.asp.net/bleroy/archive/2007/04/23/the-format-for-javascript-doc-comments.aspx
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          И тут на ум приходит Dox.
  • 0
    Спасибо за помощь)
    • +5
      Добавлю от себя пару моментов.

      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 );
        }
      }
      

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

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




        В варианте с eval в «Scope Variables» видно, что переменные не удалены из контекста
  • +1
    Chrome: Global Score в полтора раза быстрее :)
  • 0
    Вот насчет 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 приходится использовать строку вместо функции, так что, думаю, одно и то же
    • +7
      Ваша проблема решается без эвала, это классическая проблема, решаемая с помощью замыканий. Например так:
      for (var i = 0; i < count; i++)
      {
        (function(i) {
          return window.setTimeout(function()
          {
            asyncFunc(i);
          }, 10000 * i);
        })(i);
      }
      
      • 0
        Спасибо огромное, не знал
        Насколько я понимаю он работает благодаря отделению области видимости где вызывается setTimeout от области видимости где инкрементируется i?
        • +1
          Да, это работает потому, что для каждой итерации цикла создается самовызываемая функция, которая имеет свою область видимости, в которой i — имеет нужное нам значение (мы получаем его внутри setTimeout). В вашем же коде, для всех i область видимости одинаковая и на момент вызова функции в setTimeout, i в этой области видимости будет уже равно 5 (цикл уже отработал). Подробнее почитать например тут: javascript.ru/basic/closure
        • +1
          Сам помню, когда разбирался в тонкостях JS, думал, как красиво это сделать)
          С помощью замыканий, и правда, получается просто и красиво.
      • 0
        Есть также хак с with:
        for (var i = 0; i < count; i++) with ({ i: i }) {
            window.setTimeout(function() {
              asyncFunc(i);
            }, 10000 * i);
        }

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

        for (var i = 0; i < count; i++) {
            window.setTimeout(asyncFunc.bind(null, i), 10000 * i);
        }
      • НЛО прилетело и опубликовало эту надпись здесь
  • +4
    А что такое декларации/аннотации в Python/Java?
  • 0
    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();


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

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


        Если я напишу так: (f = глобальная), функция b тоже не удалится. Значит, дело не в локальности f.
        b = function(){
           //...
          f = arguments.callee;
          setTimeout(function(){delete f;},1);
        }
        • +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.
  • 0
    Вопрос по 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 могла и не пройти.)
    • 0
      >Продолжение первого вопроса. — первого в смысле по счёту в статье.
    • 0
      Есть очень много решений, который контролируют порядок загрузки скриптов без каких-либо eval'ов, используя обычные script-теги: labjs, $script.js, yepnope,…

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

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

        например:


        при создании нового элемента, делается клон элемента-образца.
        потом рекурсивно проходим по этому элементу.
        и заменяем 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

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

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

        например:
        <div id=«prop_id»>
        <input type=«text» value=«prop_id2» value=«prop_value»>
        </div>
  • +2
    > получился очень кривой инпут
    Почитайте о Contenteditable
  • 0
    Для звука есть ещё старый-престарый тег BGSOUND.
  • +2
    По вопросу 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>
  • 0
    К ответу 13: на самом деле, лучше, наверное (для скорости), не к DOM-элементам приписывать атрибуты с хешами, а наоборот, к конструкциям JS приписывать DOM-элементы, создавая доступ к ним. Сравнивали ли где-то в публикациях разницу в скорости доступа?

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

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

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

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

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

        Существует ли способ получить 'MyVarName' из JS?
        • +1
          Если переменная объявлена в локальном контексте, то её имя получить нельзя(можно было в ФФ 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?/); 
          • 0
            Спасибо за ответ!
  • 0
    Можно ли добраться до CSS псевдоэлементов :before и :after через JS?
    Для того, чтобы накидать/изменить их стили и узнать значение свойства content.
    • +1
      Через 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
  • 0
    4. Eval очень долго выполняется.

    for(var t=0;t<10;t++){
    	console.time('native'+t);
    
    	var res = 0; 
    	for(var idx = 0; idx < 1000000; idx++){
    		res = Math.round( Math.random()*((new Date()).getTime()/idx) )
    	};
     
    	console.timeEnd('native'+t);
    	console.time('eval'+t);
    	eval([
    
    	'var res = 0;',
    	'for(var idx = 0; idx < 1000000; idx++){',
    		'res = Math.round( Math.random()*((new Date()).getTime()/idx) )',
    	'};'
    
    	].join(''))
    	console.timeEnd('_eval_'+t);
    }
    native0: 1795.110ms
    _eval_0: 1747.425ms
    native1: 1762.246ms
    _eval_1: 1754.927ms
    native2: 1758.079ms
    _eval_2: 1752.534ms
    native3: 1765.570ms
    _eval_3: 1756.089ms
    native4: 1765.483ms
    _eval_4: 1742.980ms
    native5: 1759.714ms
    _eval_5: 1755.417ms
    native6: 1760.582ms
    _eval_6: 1756.282ms
    native7: 1764.699ms
    _eval_7: 1766.217ms
    native8: 1742.862ms
    _eval_8: 1754.718ms
    native9: 1762.537ms
    _eval_9: 1747.459ms 
    

    Хочу напомнить, что JavaScript вообще то весь — интерпретируемый язык
    наберите в консоли браузера просто eval и нажмите ентер, что Вы увидите?
    правильно:
    function eval() { [native code] }
    

    Видите это самое " [native code]"?
    • 0
      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
      
      • 0
        а страницу в цикле обновлять не пробовали, не? так — для сравнения? ))
        Конечно у Вас будут два неравнозначных цикла — в одном некая операция в уже инициализированном интерпретаторе, а во втором каждый раз с инициализацией нового.
        вот именно по этому eval не для всех.
        • 0
          Вызвать код без ебала значительно быстрее, чем с евалом — об этом речь.
          • 0
            выполнить код при помощи eval быстрей и проще, чем при вставки тега script и прочих порождений страхов перед eval — об этом речь
            • 0
              Как eval может заменить script?
              • 0
                при динамической вставке имеется ввиду, когда для выполнения кода в уже отрендеренную страницу добавляют скрипт, подгружаемый уже работающим скриптом
                на самом деле код в теге script выполняется тем же алгоритмом, что и код в eval — это один и тот же интерпретатор
                при загрузки страницы у Вас скрипт — это тот же текст
        • 0
          Что-то числа слишком большие. Вот правильные результаты:
          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 на порядок дольше исполняется, чем создание замыкания.
          • 0
            Вы так и не поняли о чем я написал
            В контексте eval — внутри него, код исполняется точно так же — не быстрей и не медленней, а именно точно так же, как и код, который загружен при помощи тега script, потому, что eval — это не какой то там особенный способ исполнения скрипта — это тот же самый алгоритм, которым вообще весь js выполняется. Выполняя текст в eval, Вы просто выполняете его в отдельном экземпляре интерпретатора, работающего в том же скопе, в котором и вызывающий код.
            Разумеется у Вас в цикле код с eval будет выполняться дольше на время инициализации нового экземпляра интерпретатора, именно по этому его просто глупо вызывать в цикле. В таких случаях сам цикл должен быть тоже помещен внутрь eval.
            У Вас замыкание работает в контексте уже инициализированного интерпретатора — считайте что в родительском eval-е.
            Фактически Ваш код равнозначен следующему коду:
            eval([
            "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
                    "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);",
            "}"
            ].join(''));
            
            • +1
              Я прекрасно понимаю о чём вы говорите, но спорите вы с выдуманными тезисами. Да, eval — запуск интерпретации. И поэтому он медленный. И поэтому не надо использовать его там, где хватило бы и замыкания.
              • 0
                а Вы не обратили внимание, что в моем тесте так:
                запуск интерпретатора > цикл

                а у Вас:
                цикл > запуск интерпретатора
                ?
                Да, eval не стоит тулить где не попадя, но и отказываться от него совсем не стоит

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

                Я часто использую eval для выполнения асинхронно загруженного кода. Кто то использует document.createElement('script')… и т.д. для этого, кто то юзает AMD типа require.js и я не против таких методов, но я уверяю Вас, что eval быстрее и проще любого из них, а что до пресловутой сложности отладки кода в eval, то не думаю что Вам часто приходится заниматься отладкой сторонних библиотек на пример. В любом случае код с удаленной машины Вы получаете в виде просто текста, не зависимо от MIME типа в заголовке запроса — это просто текст и что бы его выполнить, Вам по любому надо пройти через инициализацию интерпретатора, каким бы способом Вы не пользовались, а после его инициализации код будет выполняться с одинаковой скоростью в любом случае. Только в случае, на пример через тег скрипт, у Вас еще и на операцию с DOM помимо прочего будет расход ресурса — это дольше, в случае с AMD у Вас тоже присутствуют затраты помимо запуска интерпретатора.

                Я не знаю ни одного программера из тех, кто въехал в то как это все работает на уровне RUNTIME_FUNCTION(Runtime_CompileString) на пример, что бы он относился к этому невероятно полезному инструменту негативно, потому что у них есть понимание как оно работает и где его можно юзать, а где нет, зато те, кто не удосуживался разобраться в истинной сути, поголовно орут, что eval — это зло, но только вот объяснить когда оно зло, а когда очень даже и добро могут далеко не все.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.