Въведение в Underscore

Генади Самоковаров / @gsamokovarov

Генади Самоковаров

Днес


За какво ще си говорим днес:


  • Често срещани пробеми в JavaScript
  • Underscore и как ги решава
  • jQuery — за домашно

Пробеми в JavaScript

Любимата ми тема

Не че нещо,

но JavaScript има характер.

Въпреки това, е чаровен.

Научете се да го обичате.

Лирично отклонение


typeof

Как да разберем дали нещо е Array?

> typeof []
'object'

A дали е RegExp?

> typeof /./
'object'

Дори null?

> typeof null
'object'

instanceof

Накратко — горе–долу

> ([]) instanceof Array
true
> (/./) instanceof RegExp
true
> null instanceof Object
false

Опа

Чупи се при null,

но не това е по-голямото зло.

Надълго

Нагледно

document.body.appendChild(document.createElement('iframe'));
OtherArray = window.frames[window.frames.length - 1].Array;
var array = new OtherArray(1, 2, 3);

array instanceof Array;      // false
array.constructor === Array; // false

Array === OtherArray;        // false

Домашно

Доста добро четиво по въпроса, може да намерите тук.

Препоръчвам

> {}.toString.call([]) === '[object Array]'
true
> {}.toString.call(/./) === '[object RegExp]'
true
> {}.toString.call(null) === '[object Null]'
true

null

За сравнение на стойност с null

value == null
true
  • По-бързо
  • Хваща undefined

Единствената смислена употреба на ==

void

Унарен оператор, който връща undefined за всичко, което му дадем отдясно.

void 0 === undefined
true
  • По-бързо от тypeof
  • Портабилно

Closure в итерация

Какво ще направи следният код?

var i, link;
for (i = 0; i < 5; i++) {
  link = document.createElement('a');
  link.innerHTML = 'Link: ' + i;
  link.onclick = function() {
    alert(i);
  };
  document.body.appendChild(link);
}

Опа

Как да го оправим?

var i, link;
for (i = 5; i < 10; i++) {
  link = document.createElement('a');
  link.innerHTML = 'Link: ' + i;
  link.onclick = (function(i) {
    return function() {
      alert(i);
    }
  })(i);
  document.body.appendChild(link);
}

Невинен

Тук, JavaScript е невинен!

Това е проблем при повечето динамични езици, като се среща при:

  • ActionScript
  • Python
  • PHP
  • дори и Ruby

Внимавайте, къде и как правите анонимни фунцкии!

arguments

Искам да именувам първият от аргументите си като останалите да са вариадични.

var sum = function(first) {
  var rest = arguments.slice(1);
  var result = first;
  for (var i = 0, len = rest.length; i < len; i++) {
    result += rest[i];
  }
  return result;
};

sum(1, 2, 3);
TypeError: Object #<Object> has no method 'slice'

arguments

Това е интересно, нека видим какво става.

(function() {
  console.log(typeof arguments);           // object
  console.log(arguments instanceof Array); // false
})();

arguments не е Array, въпреки че изглежда като такъв.

arguments

Как да го излъжем?

var sum = function(first) {
  var rest = [].slice.call(arguments, 1);
  var result = first;
  for (var i = 0, len = rest.length; i < len; i++) {
    result += rest[i];
  }
  return result;
};

sum(1, 2, 3);
6

Всъщност, това работи и за други Array подобни обекти.

var fragments = document.querySelectorAll('.fragment');
console.log(fragments instanceof Array);                // false
console.log([].slice.call(fragments) instanceof Array); // true

Oще

Проблеми има колкото искаш.

Underscore

Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.

Категории

Функциите, предоставени от Underscore са групирани в следните категории:

  • Обекти
  • Колекции
  • Масиви
  • Функции
  • Общи
  • Chaining

Обекти

Ще започнем с функциите за работа с обекти.

Предикати

Сещате се за проблемите с typeof и instanceof?

  • _.isArray
  • _.isObject
  • _.isArguments
  • _.isFunction
  • _.isString
  • _.isNumber
  • _.isFinite
  • _.isBoolean
  • _.isDate
  • _.isRegExp
  • _.isNaN
  • _.isNull
  • _.isUndefined

Показно

console.log(_.isArray([]));             // true
console.log(_.isObject([]));            // true
console.log(_.isRegExp(/./));           // true
console.log(_.isBoolean(new Boolean));  // true
console.log(_.isDate(new Date));        // true
console.log(_.isNull(void 0));          // false
console.log(_.isUndefined(void 0));     // true
console.log(_.isNaN(NaN));              // true

Други

  • _.isЕqual
  • _.isEmpty

Показно

_.isEmpty проверява дали обект е празен.

console.log(_.isEmpty({}));             // true
console.log(_.isEmpty({option: true})); // false

_.isEqual сравнява обекти в дълбочина.

console.log(_({a: {b: 1}}).isEqual({a: {b: 1}}));   // true
console.log(_({a: {b: 1}}).isEqual({a: {b: "1"}})); // false

_.extend

_.extend(destination, *sources)

Събира ключовете на два или повече обекта в един.

Последният ключ печели.

_.extend({name : 'moe'}, {age : 50})

Хипер удобно!

_.pick

_.pick(object, *keys)

Избира ключове от обект.

Връша нов обект само с тях.

Показно

Искам само name и age от
{name : 'moe', age: 50, userid : 'moe1'}

_.pick({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');

Ключовете могат да са в масив.

_.pick({name : 'moe', age: 50, userid : 'moe1'}, ['name', 'age']);

Колекции

Под колекции ще разбираме обекти, масиви и подобни

_.each

_.each(list, iterator, [context])

По-добрият начин за итерация

Контекстът може да бъде пропуснат, но е добра идея, ако ви трябва

При масиви и подобни, фунцкията се вика с:
(element, index, list)

При обекти, фунцкията се вика с:
(value, list)

Показно

Спомняте си проблемите с анонимните фунцкии?

_.each(_.range(5), function(i) {
  var link = document.createElement('a');
  link.innerHTML = 'Link: ' + i;
  link.onclick = function() {
    alert(i);
  };
  document.body.appendChild(link);
});

Предпочитайте го пред for!

_.map

_.map(list, iterator, [context])

Взима колекция и съпоставя функция за всеки елемент

Акумулира върнатите стойностти и ги връща в масив

Показно

Сериозно

_.map(_.range(4), function(i) {
  return i * i;
});

_.filter

_.filter(list, iterator, [context])

Взима колекция и съпоставя функция за всеки елемент

Събира елементите, за които функцията е била истина, в масив

Показно

Сериозно

_.filter(_.range(10), function(i) {
  return i % 2 === 0;
});

_.reduce

_.reduce(list, iterator, memo, [context])

iterator(memo, value, key/index, list)

Без крави тука ☹

Събира голяма колекция в един резултат

Показно

_.reduce([1, 2, 3], function(memo, num) { return memo + num; }, 0);

_.toArray

_.toArray(obj)

Спомняте си [].slice?

Това е по-добро и по-четимо

Ако не сте се досетили, хваща какво-да-е и го прави на масив.

Показно

(function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);

Колекции

Има още интересни фунцкии, които няма да можем да покажем сега. Разгледайте документацията.

Масиви

Полезни фунцкии за масиви и подобни

_.flatten

_.flatten(array, [shallow])

Изглажда дълбоки масиви (колкото си искате дълбоки)

Ако shallow е истина, изглажда само едно ниво

Показно

_.flatten([1, [2], [3, [[4]]]]);
_.flatten([1, [2], [3, [[4]]]], true);

_.compact

_.compact(array)

Връща нов масив, без лъжливи стойностти

Лъжливи стойностти:

  • 0
  • false
  • null
  • undefined
  • ""
  • NaN

Показно

_.compact([0, 1, false, 2, '', 3]);

Масиви

Има още интересни фунцкии, които няма да можем да покажем сега. Разгледайте документацията.

Chaining

Позволява ни да извикваме редица от Underscore методи, върху една и съща колекция.

Показно

var lyrics = [
  {line : 1, words : "I'm a lumberjack and I'm okay"},
  {line : 2, words : "I sleep all night and I work all day"},
  {line : 3, words : "He's a lumberjack and he's okay"},
  {line : 4, words : "He sleeps all night and he works all day"}
];

_.chain(lyrics)
  .map(function(line) { return line.words.split(' '); })
  .flatten()
  .reduce(function(counts, word) {
    counts[word] = (counts[word] || 0) + 1;
    return counts;
  }, {})
  .value();

Oще

За още информация последвайте следните връзки:

jQuery

jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript.

Домашно

Задължително минете през интерактивният урок, ако за пръв път чувате какво е jQuery. Прегледайте още:

Въпроси?

Благодаря ☺