Category Archives: dev

JS

Null, операторы сравнения и все, все, все

При всей той напускной строгости и минимализме, которыми блещет Javascript, он не дотягивает до уровня строгости и логичности Python, при примерно таком же уровне минимализма. Зачем, например, такое количество разных значений-аналогов хрестоматийного false? Интерпретация false в вычисляемых выражениях это сплошная головная боль — проблемы приведения «пустых» значений есть в самом языке, это раз. Во-вторых львиная доля времени проводится во взаимодействием со встроенными в язык или браузер объектами, и что возвращает каждый из них можно только запомнить. В-третьих, есть еще написанные неведомо кем библиотеки.

Что такое «пустые значения»? Это собирательное название для всех тех вариантов возвращаемого значения, когда функция не нашла ничего, вычисление невозможно, произошла ошибка и т.п. Несмотря на то, что такого термина нет, данная идиома повсеместно используется всеми программистами на всех языках. И в этом коварство Javascript, потому что он предоставляет несколько вариантов для таких значений. Можно искренне порадоваться за вас, если вы всегда знаете, когда возвращать false, null, а когда undefined. Давайте теперь вместе порадуемся и за тех, кто не знает, но не унывает и пишет библиотеки, которыми нам с вами, возможно, придется пользоваться.

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

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

  false      // старый-добрый булевский false
  undefined  // значение переменной, которая не была инициализирована
  null       // а это специальное значение для переменной инициализированной null
  NaN        // выдается при ошибках арифметических операций типа 1 / 0
  {}         // совсем не NULL, а пустой объект, но часто используется авторами как "пустое значение" по недосмотру
  []         // пустой массив, типичное дело для функций возвращающих список
  Object     // объект да и объект, оставьте его в покое

Выше было сказано, о проблемах библиотек, но далеко ходить не надо. Что из этого по вашему будет выдавать стандартный объект Date, если на вход ему передать неправильную дату? Ошибку? null? Нет — Object.

    var d = new Date("2012--12-01"); // объект будет создан без предупреждений

    d.toJSON();       // null
    d.getTime();      // NaN
    d.toString();     //'Invalid Date'
    d.toISOString()   // наконец-то бросит ошибку!

Поскольку выше было сказано, что на Javascript пишет много кто, то нередка ситуация, когда одну и ту же функцию С-шник напишет так:

  function isEven(arg) {
    if (arg % 2 == 0)
      return 1;
    else
      return 0;
  }

Любитель Perl выдаст

  function isEven(arg) {
    if (arg % 2 == 0)
      return 1;
    else
      return undefined;
  }

Программист на PHP может выдать и:

  function isEven(arg) {
    if (arg % 2 == 0)
      return 1;
    else
      return null;
  }

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

if (isEven(3) == false) {
    alert("ба, нечетное число!");
}

И проблема не сводится к банальному заучиванию таблиц истинности Javascript. В настоящий момент любой сколько-то серьезный сайт содержит в себе гребаную кучу разномастных библиотек, начиная от гигантов типа ExtJS и заканчивая воооон тем маленьким скриптиком, который вы нашли на гитхабе человека, который, судя по остальным его тамошним проектам и дате последнего обновления скрипта, на Javascript пишет редко, давно, и, скорее всего, первый и последний раз.

Но вернемся к обработке пустых значений. Допустим мы, отдавая дань моде, пишем некий сайт на nodejs. И решили мы сделать на нем регистрацию. Поскольку мы заботимся о безопасности, то хотим сделать длину пароля не менее 8 символов, и разрешим использовать в нем буквы в разном регистре и цифры. C Javascript это просто, ведь в язык встроены регулярные выражения:

  /^[a-zA-Z0-9]{8,}$/.test('passworD1'); // пароль подходит

Теперь давайте представим, что пароль приходит к нам откуда-то из запроса, или из какого-то input, если речь идет о клиент-сайде. И вот так получилось, что в результате опечатки или что-то напутав вы засунули в переменную с паролем что-то не то:

  var request = {
    "user"     : "alex",
    "password" : "sdjk23h78dg2"
  };

  // опа, опечатка
  if ( /^[a-zA-Z0-9]{8,}$/.test(request["pasword"]) ) {
    save_user_to_database(request);
  } 

И что характерно, пользователь сохранится. С пустым паролем, или только с солью, если таковая предусмотрена. Все это благодаря особенности Javascript:

  /^[a-z]{1,10}$/.test(null);      // true 
  /^[a-z]{1,10}$/.test(undefined); // true 
Строки и числа

На эту тему сломано много копий, кому-то милее, чтобы за попытку сложения строк и чисел интерпретатор больно бил по рукам(и, наверное, это самое правильное поведение), а кто-то хочет, чтобы интерпретатор интерпретировал строки как числа когда это возможно. Что тоже, согласитесь, часто удобно, когда мы знаем, что на более высоком уровне переменная уже прошла через валидаторы, и в ней все-таки число, пусть и в строковом представлении.

Давайте посмотрим как происходит интерпретация таких чисел в строках в разных языках:

PHP 34 32
Python ошибка ошибка
Perl 34 32
Ruby ошибка ошибка
Javascript ‘331’ 32

Так что parseInt и parseFloat — ваши лучшие друзья. Хотя, лучше Number…

  // вы так, конечно, никогда не напишите, но зрелищно ведь, да?
  parseInt(null, 24) === 23 // true
Множественные аргументы

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

  function lastNegative() {
    var negative = null;

    while ( arguments.length ) {
      negative = arguments.pop(); // ошибка
      if (negative < 0)
        break;
    }

    return negative;
  } 

Несмотря на то, что локальная переменная arguments — массив неименованных аргументов функции, она не является полноценным массивом Javascript. И чтобы работать с ним привычным образом следует каким-то образом преобразовать ее в массив нормальный, класса Array. Самый частый сниппет используемый для этого имеет такой вид.

  var args = Array.prototype.slice.call(arguments);
Массивы

Всегда тщательнейшим образом следует проверять тип и структуру возвращаемого массива, иначе после нескольких часов дебага вы можете с удивлением обнаружить, что:

[]     == true // false - пустой массив, вроде бы интуитивно понятно
[1]    == true // true  - единица, все логично - и массив непустой, и единица приводится к true
[2]    == true // false - массив непустой, значит дело в единице, которая приводится к true... 
[true] == true // false - либо мы имеем хитрое правило приведения, либо true != true

// может все дело в магической единице ?
[1 , 1]  == true // false нет...

// а если вложенный массив?
[ [1], [1] ]    == true // false
[ [ [ [1] ] ] ] == true // true

// но !
new Array()    == true // false 
new Array(1)   == true // false 
new Array(1,2) == true // false 

Наверняка у профессионалов Javascript есть объяснение для всего этого, но проблема от этого не исчезнет. Утешает только то, что далеко не каждый гуру языка без гугла сможет объяснить почему:

  new Array([],null,undefined,null) == ",,,"; // true

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

Форматирование

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

  function foo() {
    return "Феанор велел остановиться, раны его были смертельны и он знал, что час его близок";
  }
 
function bar() {
  return 
    "Феанор велел остановиться, раны его были смертельны и он знал, что час его близок";
}

foo(); // возвращает текст
bar(); // возвращает undefined 
Оператор foreach

Плох тот интерпретируемый язык программирования, который по долгу службы меньше всего связан с математикой, но при этом имеет массивы и не имеет оператора foreach для прохода по ним. Т.е. оператор вроде бы как есть, но в отличие от всех остальных языков, этот оператор поддерживающих, итерация происходит не по значениям массива, а по ключам. Что на самом деле очень удобно в определенном классе алгоритмов, с которыми мне в буднях веб-разработки сталкиваться почти не приходится.

for (index in ARRR) {
    var value = obj[index];
    console.log(value); 
}

А Кто-то любит использовать дедовский метод:

for (var index = 0; index < a.length; ++index) {     
    console.log(a[index]); 
}

Для тех кто хочет умилостивить колбэчное божество есть свой вариант:

a.forEach(function(entry) {
    console.log(entry);
});

У меня есть свой сниппет для такого цикла, а у вас? Не находите, что когда в языке есть целых 3 способа(не считая собственные foreach для каждой уважающей себя библиотеки), делать одну и ту же вещь базовую вещь, и каждому способу чего-то не хватает, то здесь что-то не так? Не находите? А это все потому, что вы пишите на Javascript. Но все же задумайтесь почему в CoffeeScript и Dart таки поддерживается итерация по значению списка:

# dart
for (var x in collection) {   
    print(x); 
}

# coffeescript
for x in collection
    print(x)

source

Javascript = how get in event your this

var Controller = Spine.Class.create({
init: function() {
$(‘#destroy’).click(this.destroy);
},
destroy: function() {
// this = event;
}
});

var Controller = Spine.Class.create({
init: function() {
$(‘#destroy’).click(this.proxy(this.destroy));
},
destroy: function() {
// this = init;
}
});

Generate GUID on JS

Math.guid = function() {
return ‘xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx’.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == ‘x’ ? r : (r&0x3|0x8);
return v.toString(16)
}).toUpperCase();
};

То, что вам никто не говорил о z-index


Проблема z-index в том, что многие просто не понимают, как он работает.
Всё, описанное ниже, есть в спецификации W3C. К сожалению, не все её читают.
Continue reading

Делегирование (Delegation) — основной шаблон проектирования, в котором объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту.

Связность (coupling, или зависимость dependency) — это мера зависимости между классами или модулями. При слабой связности модули взаимодействуют через устойчивый интерфейс и не зависят от реализации друг друга.

Агрегирование (также называемое композицией или включением) — методика создания нового класса из уже существующих классов путём включения, называемого также делегированием.

Модульность — принцип, согласно которому программное средство (программа, библиотека, веб-приложение и др.) разделяется на отдельные именованные сущности, называемые модулями.

Инкапсуляция — ограничение доступа извне к внутренним компонентам (методам, свойствам и т.п.).

Шаблонный метод (англ. Template method) — поведенческий шаблон проектирования, определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.

Полиморфизм – свойство, позволяющее единообразно обрабатывать данные разных типов.

Утиная типизация (duck typing; неявная динамическая типизация) — определение типа объекта через поддерживаемый им контракт, а не действительные интерфейсами или базовыми типами, от которых он унаследован.

Паттерн Стратегия на Javascript

От переводчика:
Я собрался изучить новый для меня паттерн Стратегия, но не нашёл толкового русского описания его реализации на javascript. Статья на wiki пугает своей сложностью, а наглядность примера оставляет желать лучшего. По этому и взялся за перевод этой статьи, одновременно разбираясь, что же из себя представляет данный паттерн.
Спойлеры и текст, выделенный серым, являются моими комментариями.

Далее мы разберём примеры того, как я использую СТРАТЕГИЮ в Javascript, и как он используется реальной библиотекой, для разбиения её на небольшие части.

Continue reading

Понимание ООП в JavaScript [Часть 1]

— Прототипное наследование — это прекрасно
JavaScript — это объектно-ориентированный (ОО) язык, уходящий корнями в язык Self, несмотря на то, что внешне он выглядит как Java. Это обстоятельство делает язык действительно мощным благодаря некоторым приятным особенностям.

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

К счастью, в ECMAScript 5 появилось множество вещей, которые позволили поставить язык на правильный путь (некоторые из них раскрыты в этой статье). Также будет рассказано о недостатках дизайна JavaScript и будет произведено небольшое сравнение с классической моделью прототипного ОО (включая его достоинства и недостатки).

Continue reading