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

Добро пожаловать на Пыху!

Логин:
Пароль:
 

Нет прописки? Зарегистрируйся!

Новости

Мы в твиттере!
Мы вконтакте!
Мы на яндексе!

Краснодарское время: 23 Май, 2012, 04:45:48

Страниц: [1] 2
Печать
Автор Тема: Фишки и мелочи JS  (Прочитано 6081 раз)
0 Пользователей и 1 Гость смотрят эту тему.
vasa_c    ↓ 
16 Сентябрь, 2007, 04:44:47
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

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

Завершение операций (";" и без нее)
 
Добрые дяди-разработчики JS, стремились сделать синтаксис языка как можно более простым для обычных вебмастеров, а не проффесиональных программистов.
Поэтому они решили, что бедный вебмастер может умереть от усталости или у него взорвется мозг от напряжения, если каждую инструкцию ему придется завершать точкой с запятой. Поэтому они сделали её необязательной. Вернее, ввели правило — если лексемы в строке без точки с запятой составляют полный оператор, то точка с запятой будет вставлена автоматически.
 
После этого вебмастера живут вольготной жизнью, временами, правда, напарываясь на трудноуловимые ошибки.
 
Классический пример:
Javascript

return
x;
 

Будет возвращено значение x? Фигушки. JS прочтет это как:
 
Javascript

return; // return ничего
x; // Тоже, кстати, верная операция, ничего не выполняющая и возвращающая значение переменной в никуда.
 

Как и во всех классических примерах, здесь, конечно, виноват сам программист. Ну зачем он потащил этот бедный x на другую строчку? Однако, встречаются выражения, которые грех не разбить на несколько строчек. И тут нужно всегда быть осторожным и, там где не помешает, не скупиться на скобки:
 
Javascript

return (
    function ()
    {
        // body
    }
);
 
« Последнее редактирование: 16 Сентябрь, 2007, 04:47:11 от vasa_c » Записан

vasa_c    ↓ 
16 Сентябрь, 2007, 04:45:03 , спустя 16 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Логические операции "&&", "||" и т.п.
 
Неполное вычисление
 
Логические выражения вычисляются только до момента, когда станет ясен окончательный результат.
 
Javascript

var x = f1() && f2();
 

Можно подумать, что здесь будет выполнены две функции, f1 и f2, их результаты будут приведены к логическому типу (true/false), а потом над ними будет проведена операция логического "И".
Нет. Сначала будет выполнена, f1(). Если она вернет false (или приводимое к нему значение), то результат операции станет известен сразу же - это false, вне зависимости от результата f2(). f2() вызываться в этом случае не будет.
 
Аналогично:
 
Javascript

var x = true || f2(); // f2() не выполниться, так как это не может повлиять на конечный результат.
 

 
Возвращаемое значение
 
Многие думают, что логические операции возвращают значение логического типа (true или false). Самое печальное, так думают даже авторы многих книжек.
На самом деле возвращается последнее вычисленное выражение:
 
var x = (2 + 3) || 3;
 
Первым делом будет вычисленно выражение слева от оператора - (2 + 3 = 5). 5 будет приведено к логическому типу — true. В связи с вышесказанным о неполном вычислении, вычисление результата операции "||" будет тут же закончено (логическое "ИЛИ" с одним из операторов true, всегда true). Переменная "x" же получит в качестве значение не "true", а "5".
 
Можете проверить:
 
Javascript

var x = 5 || 3;
if (x === true) {
    alert("Как и надо");
} else {
    alert("Ой, моё мировозрение поколебленно");
}
 

 
Всё вышеперечисленное, кроме проблем для непосвященных, так же несет очень много полезного для тех, кто в этом разбирается.
 
Простой пример: есть массив A, нам нужно получить A[1][2][3][4][5] (ну или если такого индекса нет - undefined). При этом мы не знает структуры переменной. Например, если A[1][2] не существует, а мы напрямую попробуем получить наше значение, вылезет ошибка - попытка доступа к несуществующей переменной, как к массиву. По идее надо делать так:
Javascript

var x = undefined;
if (A[1]) {
    if (A[1][2]) {
        if (A[1][2][3]) {
            if (A[1][2][3][4]) {
                if (A[1][2][3][4][5]) {
                    x = A[1][2][3][4][5];
                }
            }
        }
    }
}
 

А можно так:
Javascript

var x = A[1] && A[1][2] && A[1][2][3] && A[1][2][3][4] && A[1][2][3][4][5];
 

Или так :) :
Javascript

var y = A[1];
var x = (y) && (y = y[2]) && (y = y[3]) && (y = y[4]) && (y = y[5]);
 
Записан

vasa_c    ↓ 
16 Сентябрь, 2007, 04:45:14 , спустя 11 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Восьмеричные числа
 
JS поддерживает большой набор литералов для задания чисел. Примеры: 2, 2.5, .5, -2E5, 4.E+3.
 
Кроме этого числа можно задавать в 16-ричной и 8-ричной кодировках: 0xFF, 0123 (начинающеяся с "0" число - восьмеричное).
 
Вобще-то в стандарте ECMAScript про 8-ричные числа ничего нет, но добрые дяди-разработчики практически всех браузерах позаботились о бедных юзерах, добавив данную возможность. И опять, на мой взгляд, дали им больше геморроя, чем пользы.
 
Большинство программистов, обычно либо не знает об этой слабоиспользуемой фишке, либо просто забывет. А потом думают, что за фигня:
 
Javascript

var x = 025;
alert(x); // 21
 

Впрочем, большинство, конечно, в коде так не пишет. Основные проблемы происходят, когда обрабатываются введенные пользователем данные форм. Полученные из формы числовые данные (в виде строки), обычно пропускаются через parseInt(), а он так же понимает восьмиричную запись. Поэтому, не забываем об этой особенности и обрезаем начальные нули (не забывая о том, что строка может просто содержать "0"):
 
Javascript

var x = (str == "0") ? 0 : parseInt(str.replace(/^0+/, ""));
 
« Последнее редактирование: 19 Сентябрь, 2007, 11:56:19 от vasa_c » Записан

vasa_c    ↓ 
16 Сентябрь, 2007, 04:45:20 , спустя 6 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Приведение типов и оператор "+"
 
Как известно в JS есть такое понятие, как "приведение (преобразование) типов" (как, впрочем, и в PHP и других слаботипизированных языках). Значение нелогических типов в логических операторах приводятся к логическим, при сложении с числом, false приводится к 0 и т.п. Полный список правил можно найти в документации.
 
В JS для двух различных операций (конкатентации строк и сложения чисел) используется один и тот же оператор - "+" (в PHP сложение - "+", конкатентация - ".").
 
Javascript

alert("Раз " + "Два"); // Строки - конкатентация: "Раз Два"
alert(1 + 2); // Числа - сложение: 3
 

Однако, что будет, если один из операндов строка, а другой — число? А будет конкатентация.
 
Javascript

var x = 1 + "2"; // Один из операндов строка. Второй приводится к строке. Итог - "12"
var x = "1" + 2; // Тоже "12"
 

Забыв ненадолго об этой особенности, можно получить на свою голову много ненужных неприятностей.
 
Что же делать, чтобы указать на тип операции?
 
Если нужно сложение, то придется все переменные насильно приводить к числам:
 
Javascript

var one = "1";
var two = "2";
var three = 3;
var four = "4";
alert(parseInt(one) + parseInt(two) + parseInt(three) + parseInt(four));
 

Если же нужно составить из чисел строку, то следует либо один из операндов привести к строке, либо добавить фиктивный операнд:
 
Javascript

var one = 1;
var two = 2;
var three = 3;
var four = 4;
alert(one.toString() + two + three + four);
alert("" + one + two + three + four);
 
« Последнее редактирование: 16 Сентябрь, 2007, 04:52:23 от vasa_c » Записан

vasa_c    ↓ 
16 Сентябрь, 2007, 05:38:29 , спустя 53 минуты 9 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Внедрение сценариев в документ. <SCRIPT>
 
Формат
 
Как правильно включать сценарий в документ с помощью тега <SCRIPT>?
Правильно так:
HTML

<script type="text/javascript">
// Содержимое внедренного тега
</script>
<script type="text/javascript" src="Внешний источник"></script>
 

Правильно использовать атрибут type. Без него html-код будет невалидным.
Атрибут language использовать неправильно. С ним верстка будет невалидна. Единственным его преимуществом, указываемым многими является возможность переключаться между версиями javascript или между javascript/jscript, например, language="Javascript1.2". Однако, здесь два момента:
1. Если вы указываете атрибут type (а его надо указывать), language будет просто проигнорирован.
2. Давно прошли те времена, когда переключение версий имело смысл. На данный момент всё можно делать совершенно другими и более удобными методами. Во всяком случае это верно для 98% обычных web-приложений.
 
В соответствии с высокими стандартами и спецификациями, даже type="text/javascript" неправильно. Правильно: type="application/javascript" или type="application/ecmascript", именно они должны постепенно прийти на смену text/javascript. Однако, пока не пришли и не факт что придут. Так что сейчас верно type="text/javascript".
 

Комментарии
 
Многие очень любят заключать содержимое <SCRIPT> в комментарии:
 
HTML

<script type="text/javascript">
<!--
alert("Жабаскрипт!");
// -->

</script>
 

Часть из практикующих это не может даже внятно объяснить зачем это делать. Просто все делают. Другая часть гордо поднимает указательный палец к верху и объясняет: "Это надо, чтобы старые браузеры, не знающие тега SCRIPT не выводили содержимое".
Граждане! Те браузеры давно вымерли. Не осталось ни одного вменяемого человека, который бы помнил как их звали и какие у них были порядковые версии. Однако дело их живет и процветает.
 
В ответ на это, опять-таки, любят заявлять: но все равно JS может быть выключено в браузере, а так же существует большое количество браузеров (например, для мобильных устройств), не поддерживающих его. Однако, браузер понимающий, хотя бы HTML версии 3 (а если он её не понимает, он не понимает ничего), знает, что содержимое SCRIPT выводить нельзя, вне зависимости от того, будет он его выполнять или нет.
 
Некоторые идут дальше и заключают в html-комментарии даже содержимое подключаемых JS-файлов. Почему, браузер не знающий о теге SCRIPT не будет выводит содержимое указанного в нем файла на экран, предлагаю подумать самим.
 

<![[CDATA
 
С переходом на XHTML, однако, появляется новая напасть. Текстовое содержимое (а JS-код с точки зрения HTML, это просто текст внутри тега) не должно содержать очень многие символы из тех, что повсеместно используются в JS. Браузеры, конечно, не дураки и подобные вещи пропускают. Но особо ушлые верстальщики, все-таки хотят делать "как надо". Приходится содержимое SCRIPT заключать в секцию CDATA:
 
HTML
<script type="text/javascript">
//<![CDATA[
alert("Содержимое");
//]]>

</script>

Комментарии "//" необходимы, т.к. JS не знает о том, что такое CDATA
 

<head> или <body>
 
Многим интересно, куда нужно вставлять сценарии. Только в <head> или можно и в <body>. Эксперимент показывает, что можно и в BODY. Остается вопрос, верно ли это с точки зрения стандартов? Ответ — с точки зрения стандартов это верно. С точки же зрения практики, это зависит от ситуации.
Общепринято библиотеки (набор функций) подключать в <head>. Код же производящий действия в момент подключения может понадобиться включить после html-кода элементов, к которым он совершает обращение.
 
О некоторых вещах из данной области можно почитать в теме "обработка документа в браузере".
« Последнее редактирование: 27 Июнь, 2008, 11:06:16 от vasa_c » Записан

AlexB    ↓ 
16 Сентябрь, 2007, 09:05:28 , спустя 3 часа 26 минут 59 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 89
Сообщений: 3423
Сила слова: 2.6

И того, что разсказал vasa_c про возвращаемое значение логической операции ||, следует элегантный способ смоделировать "значение по умолчанию" формального параметра функции.
 
Text

function func(param, param_with_default)
{
    param_with_default = param_with_default || "default value";
        ...
}
 
Записан

Dagdamor    ↓ 
16 Сентябрь, 2007, 09:41:38 , спустя 36 минут 10 секунд
НЕ ХУЕТА! ХУЕТА!

Карма: 2
Сообщений: 47
Сила слова: 4.26

vasa_c
Очень интересно, спасибо за статьи :)
Записан

Форум PHP Community - скажи "нет" детскому саду!
vasa_c    ↓ 
17 Сентябрь, 2007, 03:10:38 , спустя 17 часов 29 минут
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Объявление переменных. "Var".
 
Переменные нужно определять с помощью ключевого слова var. Всегда.
 
Javascript

var x = 10;
var y;
var a, b = "var";
 

Некоторые вообще не используют "var". А некоторые используют при определении локальных переменных. Потому что кто-то когда-то сказал, что определение переменной без var используется для определения глобальных переменных. Но это не так (не совсем так).
 
Например:
 
Javascript

function func()
{
    var c;  // Определяем переменную
    c = 10; // Записываем значение в переменную
}
 

Здесь "c = 10" обычная инструкция присвоения значения переменной. От того, что мы уберем "var c", что-то изменится в сути "c = 10"? Конечно, нет. Это так и останется присвоением десятки переменной "c". Единственно, не найдя локальной переменной "c", JS пойдет искать её выше.
 
Javascript

var c = 1;
function func() {c = 2;}
func();
 

Здесь будет найдена глобальная переменная "c" и значение будет присвоено ей.
 

Javascript

var c = 1;
function func1() {
    var c = 2;
    function func2() {
        c = 3;
    }   
    func2();
}
func();
 

А здесь в func2 происходит присвоение тройки локальной переменной из func1, так как она встретится раньше на пути, чем глобальная "c".
 

Javascript

function func() {
    c = 1;
}
func();
 

А здесь переменная "c" не будет найдена нигде. И тогда, в соответствии с запутанной логикой JS, будет создано свойство глобального объекта за именем "c" и ему присвоено нужное значение. Это будет "псевдопеременная". Вести себя она будет в большинстве случаев, как обычная, но здесь есть множество темных моментов, которые могут взять и всплыть в совершенно неожиданном месте.
 
Необязательно понимать, что я здесь написал про поиск переменных, главное понять одно — переменная всегда должна быть объявлена с помощью "var". Даже не из-за вышеописанных "темных моментов", а потому что это придает коду намного больше четкости, сразу видно в каком контексте определена используемая переменная.
 
Если в функции нужно задать значение глобальной переменной, объявите её заранее:
 
Javascript

var global_var;
function func() {global_var = 10;}
 

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

function func() {
    window["global_var"] = 10;
}
 

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

vasa_c    ↓ 
17 Сентябрь, 2007, 03:33:11 , спустя 22 минуты 33 секунды
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Значение аргументов по умолчанию
 
Как известно, в JS поскупились добавить возможность задавать для аргументов функции значения по умолчанию. Т.е. так написать нельзя:
 
Javascript

function func(x, y = 1, z = "зэ") {...}
 

Будет ошибка. К счастью, передавать все описанные для функции аргументы каждый раз не нужно. Ошибки не произойдет, а соответствующая переменная примет значение undefined. Здесь можно воспользоваться способом от AlexаB, предложенным выше:
 
Javascript

function func(x, y, z)
{
    y = y || 1; // Если y не пришел, зададим ему значение по умолчанию
    z = z || "зэ";
    ...
}
func(1);
func(1, 2, 3);
func(1, 2);
 

Если переменная может принимать приводимое к false значение, то придется проверять на undefined более четко:
 
Javascript

if (y === undefined) {
    y = "by default";
}
 

Если же undefined так же является допустимым для переменной значением, то можно воспользоваться массивом arguments, содержащим полученные аргументы:
 
Javascript

if (arguments.length < 2) { // y - второй аргумент функции
    y = "by default";
}
 

С помощью arguments так же возможно реализовать функцию, получающую неопределенное количество параметров:
Javascript

function sum()
{
    var sum = 0;
    for (var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}
 
alert(sum(1, 2));
alert(sum(1, 2, 3, 4, 5, 6, 7));
 
« Последнее редактирование: 17 Сентябрь, 2007, 03:35:20 от vasa_c » Записан

vasa_c    ↓ 
17 Сентябрь, 2007, 03:50:47 , спустя 17 минут 36 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Статические переменные функции
 
Так же пожмотились создатели JS и на статические переменные функции. Если кто не знает, статическая переменная, это переменная, доступная только внутри функции, но в отличии от локальных переменных, сохраняющая свое значение между вызовами функции.
 
Например в PHP:
 
PHP

function func()
{
    static $counter = 0;
    $counter++;
    print 'Вы эту бедную функцию уже в '.$counter.'-й раз вызываете';
    ...
}
 

В JS так не сделать. Для реализации подобного поведения, некоторые используют глобальные переменные. Однако, здесь можно вспомнить о том, что в JS практически всё является объектом. В том числе и функции. А объектам можно задавать свойства.
 
Javascript
function func()
{
    func.counter++;
    alert('Вызов за номером ' + func.counter);
}
func.counter = 0;
 
func();
func();
func();
 

Здесь мы в качестве статической переменной используем свойства объекта, соответствующего функции func(). В плане сокрытия переменной, это аналогично использованию глобальной переменной — кто угодно может прочитать и перезаписать свойство. Однако, основного мы добились — перестали засорять глобальную область видимости и привязали переменную к конкретной функции.
 
PS. Внутри функции, для указания на нее, вместо имени, лучше использовать agruments.callee
Javascript

function func()
{
    arguments.callee.counter++;
    alert('Вызов за номером ' + arguments.callee.counter);
}
 
Записан

vasa_c    ↓ 
17 Сентябрь, 2007, 04:19:57 , спустя 29 минут 10 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Оператор with
 
Во многих языках есть такой полезный оператор with. В JS он тоже есть.
Только вот в JS его использовать не следует. Почему? А потому.
 
Если вам нужно обработать объект с длинным именем, используйте промежуточную переменную.
 
Вместо:
Javascript

one.two.three.four.five.x = one.two.three.four.five.y + one.two.three.four.five.z;
 

Пишите:
Javascript

var tmp = one.two.three.four.five;
tmp.x = tmp.y + tmp.z;
 

Или :) :
Javascript

function(obj) {
    obj.x = obj.y + obj.z;
}(one.two.three.four.five);
 
Записан

vasa_c    ↓ 
21 Сентябрь, 2007, 02:35:44 , спустя 3 дня 22 часа 15 минут 47 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Анонимная функция, несколько фишек
 
Как известно, основными двумя способами создания функции являются:
 
Javascript

/* Статический способ */
function func(arg) {
    // body
}
 
/* Динамический */
var func = function(arg) {/* body */};
 

Во втором случае создается анонимная функция, присваиваемая переменной. Дальше с переменной можно работать, как и с функцией, определенной "классическим" образом. Т.е. запускать.
Однако, мы можем не присваивать функцию никакой переменной, а сразу же взять и выполнить:
 
Javascript

(function() {
    alert(1);
})();
 

Здесь сразу же будет вызван alert(). После чего функция исчезнет, так как она не доступна ни через одну переменную.
Чем это отличается от того, чем просто выполнить нужные нам инструкции, не заключая их в анонимную функцию?
Отличается это в первую очередь тем, что для функции в момент её исполнения создается локальный контекст, исчезающий, после её завершения. Чем это может быть полезно в данном случае?
Например, нам нужно при загрузке страницы, выполнить какие-то более-менее сложные действия. Например, пройтись по всем ссылкам и сделать так, чтобы при клике на них пользователя спрашивали, уверен ли он, что хочет перейти по ссылке:
 
HTML

<html>
    ...
    <body>
    ...
        <script type="text/javascript">
            var anchors = document.getElementsByTagName("a");
            for (var i = 0; i < anchors.length; i++) {
                var a = anchors.item(i);
                a.onclick = (function() {return confirm("Sure?");});
            }
        </script>

    </body>
</html>
 

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

(function () {
    var anchors = document.getElementsByTagName("a");
    for (var i = 0; i < anchors.length; i++) {
        var a = anchors.item(i);
        a.onclick = (function() {return confirm("Sure?");});
    }
})();
 

Создается функция, в её контексте выполняется код, контекст вместе с функцией умирает. Всё, никаких новых глобальных переменных не появилось.
 

Прекращение выполнения кода
 
Часто бывает такая ситуация при подключении внешнего сценария, что при выполнении кода в нем, в зависимости от какого-то условия, выполнение нужно прекратить. Однако, в JS нет никакого подобия оператора exit(), так что многим приходится создавать навороченные циклы. А можно просто, заключить весь код файла в анонимную функцию и в нужном месте выйти из неё.
 
Javascript

(function () {
    // script.js
    if (expression) return;
    // script.js
})();
 

Однако, помните, что если подключаемый файл — библиотека функций, их не нужно заключать в анонимную. Т.к. они будут определены внутри её контекста и не будут видны извне.
« Последнее редактирование: 21 Сентябрь, 2007, 02:37:34 от vasa_c » Записан

adw0rd    ↓ 
19 Декабрь, 2007, 12:43:02 , спустя 88 дней 11 часов 7 минут 18 секунд
НЕ ХУЕТА! ХУЕТА!

эдво
Группа: в ухо

Карма: не нужна
Сообщений: 17615
Сила слова: 1.67

Спасибо за статью)
Записан

Python, Django, Git, Emacs, Nginx, MySQL, SphinxSearch, FreeBSD/Linux
Мой блог * Кинсбург * Либург * Я на GitHub
phpdude    ↓ 
15 Декабрь, 2008, 07:43:11 , спустя 362 дня 19 часов 9 секунд
НЕ ХУЕТА! ХУЕТА!

я - ЭМО
Группа: в ухо

Карма: 344
Сообщений: д-о-х-у-я!
Сила слова: 1.66

хорошая статья. и правда норм.
Записан

забанен. могу забанить других, пишите в личку
BEER. Helping ugly people have sex since 1862.
Шурикен    ↓ 
19 Январь, 2010, 12:50:35 , спустя 399 дней 17 часов 7 минут 24 секунды
НЕ ХУЕТА! ХУЕТА!


Карма: 0
Сообщений: 14
Сила слова: 0

Text

(function() {
    alert(1);
})();
 
^объясните плиз, чет не работает у меня.
Записан
Страниц: [1] 2
Печать
 

Перейти в: