Поговорим о JavaScript, основы и современные тенденции развития языка
1. О себе
Александр Кучеренко
• В DataArt 1,5 месяца
• Занимаюсь программированием 7+ лет
• За это время писал на: PHP, JavaScript,
ActionScript3, C#, Java(Android), Objective-C
2. Поговорим о JavaScript
1. Объекты в JavaScript, лямбды,
замыкания.
2. Deferred & Promises.
3. Библиотека Q.
7. Number
• При операциях с Number - никогда не происходят
ошибки. Зато могут быть возвращены специальные
значения
• 1/0 = Number.POSITIVE_INFINITY (плюс бесконечность)
• -1/0 = Number.NEGATIVE_INFINITY (минус бесконечность)
• Положительная бесконечность
Number.POSITIVE_INFINITY больше любого Number, и
даже больше самой себя.
8. Number
• NaN - особый результат
• Любая математическая операция с NaN дает NaN:
– NaN + 1 = NaN
• NaN не равен сам себе:
– NaN == NaN // false
• Можно проверить с помощью функции isNaN:
– isNaN(NaN) // true
9. String
• Строки в javascript - полностью юникодные
• Кавычки двойные и одинарные работают одинаково
• Можно указывать юникодные символы через
uXXXX:
• Встроены регулярные выражения, методы
replace/match:
"превед медвед".replace(/(.*?)s(.*)/, "$2, $1!") // => медвед, превед!
11. undefined
• При попытке доступа к глобальной переменной undefined (если
она не изменена).
• Неявный возврат из функции при отсутствии в ней оператора
return.
• Из операторов return, которые ничего не возвращают.
• В результате поиска несуществующего свойства у объекта (и
доступа к нему).
• Параметры, которые не были переданы в функцию явно.
• При доступе ко всему, чьим значением является undefined.
12. undefined
• undefined — это тип с единственным возможным
значением: undefined
• Не являясь константой, она не является и ключевым
словом. Можно с лёгкостью переопределить
// Happy debugging suckers ...
undefined = true;
(function(something, foo, undefined) {
// в локальной области видимости `undefined`
// снова ссылается на правильное значене.
})('Hello World', 42);
13. NULL
• Используется во внутренних механизмах
JavaScript (например для определения
конца цепочки прототипов за счёт
присваивания Foo.prototype = null)
14. Объекты
• В JavaScript всё ведет себя, как объект, лишь
за двумя исключениями — NULL и
UNDEFINED.
• Почему у примитивов можно вызвать
методы?
15. Объекты и примитивы
var number = 2;
// Можно вызвать метод
console.log(number.toString()); // -> 2
// Попробуем задать свойство
number.newProperty = 3;
console.log(number.newProperty); // -> undefined
// Если нельзя, но сильно хочется, то - можно
Number.prototype.newProperty = 3;
console.log(number.newProperty); // -> 3
16. Function
• Функции в JavaScript тоже являются
объектами
Пример №1:
foo(); // сработает, т.к. функция будет создана до выполнения кода
function foo() {}
Пример №2:
foo; // 'undefined'
foo(); // вызовет TypeError
var foo = function() {};
17. Область видимости
Пример №1:
function test() { // область видимости
for(var i = 0; i < 10; i++) { // не область видимости
// считаем
}
console.log(i); // 10
}
Хотя JavaScript нормально понимает синтаксис двух фигурных скобок,
окружающих блок, он не поддерживает блочную область видимости; всё что
остаётся на этот случай в языке — область видимости функций.
18. Как работает this
1. Глобальная область видимости
Когда мы используем this в глобальной области, она будет просто ссылаться
на глобальный объект.
Различают ровно пять вариантов того, к чему привязывается this в языке.
4. Вызов конструктора
new foo();
Если перед вызовом функции присутствует ключевое слово new то данная
функция будет действовать как конструктор. Внутри такой функции this будет
указывать на новосозданный Object.
2. Вызов функции
foo(); // Тут this также ссылается на глобальный объект
3. Вызов метода
test.foo(); // Тут this также ссылается на глобальный объект.
19. Как работает this
5. Переопределение this
Переопределить this можно с помощью методов apply, call и bind
Пример №1:
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3
foo.call(bar, 1, 2, 3); // аналогично
20. Как работает this
Пример самой распространённой ошибки:
Foo.method = function() {
function test() {
// this ???
}
test();
}
21. Как работает this
Пример самой распространённой ошибки:
Foo.method = function() {
function test() {
// this ссылается на глобальный объект
}
test();
}
22. Как работает this
Достойно выходим из ситуации:
Foo.method = function() {
var that = this;
function test() {
// Здесь используем that вместо this
}
test();
}
24. Как работает this
А что если…
var test = someObject.methodTest;
test();
Следуя первому правилу test вызывается как обычная функция; следовательно
this внутри него больше не ссылается на someObject
Хотя позднее связывание this на первый взгляд может показаться плохой идеей,
но на самом деле именно благодаря этому работает наследование прототипов.
28. Замыкания
• Каждое выполнение функции хранит все
переменные в специальном объекте с
кодовым именем [[scope]], который нельзя
получить в явном виде, но он есть
• Каждый вызов var... - всего лишь создает
новое свойство этого объекта, а любое
упоминание переменной - первым делом
ищется в свойствах этого объекта
29. Замыкания
Обычное выполнение функции без замыкания:
function sum(x,y) {
// неявно создался объект [[scope]]
...
// в [[scope]] записалось свойство z
var z;
// нашли переменную в [[scope]], [[scope]].z = x+y
z = x+y;
// нашли переменную в [[scope]], return [[scope]].z
return z;
// функция закончилась,
// [[scope]] никому больше не нужен и умирает вместе с z
}
30. Замыкания
Замыкание - это когда объект локальных
переменных [[scope]] внешней функции
остается жить после ее завершения.
Внутренняя функция может обратиться к
нему в любой момент и получить
переменную внешней функции.
31. Замыкания
Выполнение функции с замыканием:
function addHideHandler(sourceId, targetId) {
// создан объект [[scope]] со свойствами sourceId, targetId
// записать в [[scope]] свойство sourceNode
var sourceNode = document.getElementById(sourceId);
// записать в [[scope]] свойство handler
var handler = function() {
var targetNode = document.getElementById(targetId);
targetNode.style.display = 'none';
};
sourceNode.onclick = handler;
// функция закончила выполнение
// (***) и тут - самое интересное!
}
32. Замыкания
При запуске функции все происходит стандартно:
1. создается [[scope]]
2. туда записываются локальные переменные
3. внутренняя функция получает ссылку на [[scope]]
Но в самом конце - внутренняя функция присваивается sourceNode.onclick.
Внешняя функция закончила свою работу, но внутренняя - может запуститься
когда-нибудь потом.
Интерпретатор javascript не проводит анализ - понадобятся ли внутренней
функции переменные из внешней, и какие переменные могут быть нужны.
Вместо этого он просто оставляет весь [[scope]] внешней функции в живых.
Чтобы когда внутренняя функция запустится, если она вдруг не найдет какую-либо
переменную в своем [[scope]] - она могла обратиться к [[scope]] внешней функции
и нашла бы ее там.
33. Замыкания
Пример:
function makeShout() {
var phrase = "Превед!”;
var shout = function() {
alert(phrase);
}
phrase = "Готово!";
return shout;
}
shout = makeShout();
shout(); // что выдаст?
34. Замыкания
Пример:
function makeShout() {
var phrase = "Превед!”;
var shout = function() {
alert(phrase);
}
phrase = "Готово!";
return shout;
}
shout = makeShout();
shout();
1. создается [[scope]]
2. В [[scope]] пишется: phrase="Превед!”
3. В [[scope]] пишется: shout=..функция..
4. При создании функция shout получает
ссылку на [[scope]] внешней функции
5. [[scope]].phrase меняется на новое значение
"Готово!”
Внутри makeShout()
35. Замыкания
Пример:
function makeShout() {
var phrase = "Превед!”;
var shout = function() {
alert(phrase);
}
phrase = "Готово!";
return shout;
}
shout = makeShout();
shout();
1. Создается свой собственный объект
[[scope2]]
2. Ищется phrase в [[scope2]] - не найден
3. Ищется phrase в [[scope]] внешней функции -
найдено значение "Готово!”
4. alert("Готово!")
При запуске shout()
То есть, внутренняя функция получает
последнее значение внешних
переменных.
45. Deferred and Promises
1. Создать Deferred объект
2. При завершении
асинхронного метода
перевести Deferred объект в
нужное состояние
3. Передать Deferred.promise
объект из текущей функции
куда-то, где его состояние
будут отслеживать.
function readFile(fileName){
var deferred = Q.defer();
readFileAsync(fileName, function(error, result){
if(error){
deferred.reject(error);
}else{
deferred.resolve(result);
}
});
return deferred.promise;
}
Инструкция
Почему при возврате из функции
передается не сам Deferred объект, а
именно Promise?
46. Deferred and Promises
• Deferred объект — это всего лишь хранилище состояния
асинхронной функции. Таких состояний обычно несколько:
– pending — ожидание завершения процесса
– rejected — процесс закончен падением
– resolved — процесс закончен успешно
• Кроме того у Deferred объекта есть ряд методов, которые могут
менять его состояние. Например, метод .resolve().
• По состоянию Deferred объекта мы можем судить, закончен ли
процесс, состояние которого мы отслеживаем.
47. Deferred and Promises
• Обработчики выполняются последовательно
• Если обработчик «выполнения»/«ошибки» добавляется
к уже «выполненному»/«отменённому» объекту, то он
будет вызван немедленно.
• Повторный вызов resolve/reject не приводт к изменению
состояния и повторному вызову обработчиков, а просто
игнорируется (и не важно, что было вызвано до этого
resolve() или reject()).
• Хотя promises появились в jQuery 1.5 лучше их не юзать