580 likes | 789 Views
О себе. Александр Кучеренко В DataArt 1,5 месяца Занимаюсь программированием 7+ лет За это время писал на : PHP, JavaScript, ActionScript3, C#, Java(Android), Objective-C. Поговорим о JavaScript. О бъекты в JavaScript , лямбды, замыкания . Deferred & Promises . Библиотека Q .
E N D
О себе Александр Кучеренко • В DataArt1,5 месяца • Занимаюсь программированием 7+ лет • За это время писал на:PHP, JavaScript, ActionScript3, C#, Java(Android), Objective-C
Поговорим о JavaScript Объектыв JavaScript, лямбды, замыкания. Deferred &Promises. Библиотека Q.
С чего начиналось С чего все начиналось: • Ajax • Web 2.0 • jQuery
Что сейчас • Полноценные веб приложения(GoogleDoc, eyeOs, FirefoxOS, ChromeOS) • Мобильные приложения(PhoneGap, jQueryMobile, Sencha Touch) • Серверный JavaScript (Node JS) • Инструменты(MongoDB, JSON, NPM, Bower, Grunt) • Тестирование: Buster.js, Karma, TestSwarm, JsTestDriver, YUI Yeti, Jasmine, QUnit, Sinon • 3D графикаWebGL
Базовые типы Элементарные • Number • Boolean • String • Null • undefined Объекты String Array Date Function …
Number • Float64 - 8 байт • console.log(0.1+0.2) > 0.3 ?> 0.30000000000000004 • BCMath for JavaScript • 0.1234.toFixed(2) = 0.12
Number • Приоперацияхс Number - никогданепроисходятошибки. Затомогутбытьвозвращеныспециальныезначения • 1/0 = Number.POSITIVE_INFINITY (плюсбесконечность) • -1/0 = Number.NEGATIVE_INFINITY (минусбесконечность) • ПоложительнаябесконечностьNumber.POSITIVE_INFINITYбольшелюбого Number, идажебольшесамойсебя.
Number • NaN - особыйрезультат • ЛюбаяматематическаяоперациясNaNдаетNaN: • NaN + 1 = NaN • NaNнеравенсамсебе: • NaN == NaN // false • МожнопроверитьспомощьюфункцииisNaN: • isNaN(NaN) // true
String • Строкивjavascript - полностьююникодные • Кавычкидвойныеиодинарныеработаютодинаково • Можноуказыватьюникодныесимволычерез \uXXXX: • Встроенырегулярныевыражения, методы replace/match: "преведмедвед".replace(/(.*?)\s(.*)/, "$2, $1!") // => медвед,превед!
Boolean • False • false • null • undefined • “” • 0 • Number.NaN • True – всеостальное • “0” (В PHPFALSE) • “false”
undefined • Припопыткедоступакглобальнойпеременной undefined (еслионанеизменена). • Неявныйвозвратизфункцииприотсутствиивнейоператора return. • Изоператоров return, которыеничегоневозвращают. • Врезультатепоисканесуществующегосвойствауобъекта (идоступакнему). • Параметры, которыенебылипереданывфункциюявно. • Придоступековсему, чьимзначениемявляется undefined.
undefined • undefined — этотипсединственнымвозможнымзначением: undefined • Неявляяськонстантой, онанеявляетсяиключевымсловом. Можнослёгкостьюпереопределить // Happy debugging suckers ... undefined =true; (function(something, foo, undefined){ // влокальнойобластивидимости `undefined` // сновассылаетсянаправильноезначене. })('Hello World',42);
NULL • Используетсявовнутреннихмеханизмах JavaScript (напримердляопределенияконцацепочкипрототиповзасчётприсваиванияFoo.prototype= null)
Объекты • ВJavaScript всёведетсебя, какобъект, лишьзадвумяисключениями—NULL иUNDEFINED. • Почему у примитивов можно вызвать методы?
Объектыи примитивы 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
Function • Функциив JavaScript тожеявляютсяобъектами Пример №1: foo();// сработает, т.к. функциябудетсозданадовыполнениякода function foo(){} Пример №2: foo;// 'undefined' foo();// вызоветTypeError var foo =function(){};
Область видимости Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он не поддерживает блочную область видимости; всё что остаётся на этот случай в языке — область видимости функций. Пример №1: functiontest(){// областьвидимости for(vari=0;i<10;i++){// необластьвидимости // считаем } console.log(i);// 10 }
Какработаетthis Различаютровнопятьвариантовтого, кчемупривязывается this вязыке. 1. Глобальнаяобластьвидимости Когдамыиспользуем this вглобальнойобласти, онабудетпростоссылатьсянаглобальныйобъект. 2. Вызовфункции foo(); // Тут this такжессылаетсянаглобальныйобъект 3. Вызовметода test.foo(); // Тут this такжессылаетсянаглобальныйобъект. 4. Вызовконструктора new foo(); Еслипередвызовомфункцииприсутствуетключевоеслово new тоданнаяфункциябудетдействоватькакконструктор. Внутритакойфункции this будетуказыватьнановосозданный Object.
Какработает this 5. Переопределение this Переопределить this можно с помощью методов apply,call и bind Пример №1: functionfoo(a, b, c){} varbar ={}; foo.apply(bar,[1,2,3]);// массивразвернётсяв a = 1, b = 2, c = 3 foo.call(bar,1,2,3);// аналогично
Какработает this Пример самой распространённой ошибки: Foo.method=function(){ functiontest(){ // this ??? } test(); }
Какработает this Пример самой распространённой ошибки: Foo.method=function(){ functiontest(){ // this ссылаетсянаглобальныйобъект } test(); }
Какработает this Достойно выходим из ситуации: Foo.method=function(){ varthat =this; functiontest(){ // Здесьиспользуем that вместо this } test(); }
Какработает this А что если… vartest=someObject.methodTest; test();
Какработает this А что если… vartest=someObject.methodTest; test(); Следуяпервомуправилу test вызываетсякакобычнаяфункция; следовательно this внутринегобольшенессылаетсянаsomeObject Хотяпозднеесвязывание this напервыйвзглядможетпоказатьсяплохойидеей, нонасамомделеименноблагодаряэтомуработаетнаследование прототипов.
Замыкания Замыканиев JavaScript: functionouterFn(myArg){ varmyVar = 42; functioninnerFn(){ // имеетдоступкmyVarиmyArg } }
Замыкания Замыкание в PHP: functiongetNumber(){ $id = 42; returnfunction()use ($id){ return $id*2; }; }
Замыкания Классика: for(vari=0;i<10;i++){ setTimeout(function(){ console.log(i); } ,1000);}
Замыкания • Каждоевыполнениефункциихранитвсепеременныевспециальномобъектескодовымименем [[scope]], которыйнельзяполучитьвявномвиде, ноонесть • Каждыйвызов var... - всеголишьсоздаетновоесвойствоэтогообъекта, алюбоеупоминаниепеременной - первымделомищетсявсвойствахэтогообъекта
Замыкания Обычное выполнение функции без замыкания: functionsum(x,y){ // неявносоздалсяобъект [[scope]] ... // в [[scope]] записалосьсвойство z var z; // нашлипеременнуюв [[scope]], [[scope]].z = x+y z =x+y; // нашлипеременнуюв [[scope]], return [[scope]].z return z; // функциязакончилась, // [[scope]] никомубольшененужениумираетвместес z }
Замыкания Замыкание- этокогдаобъектлокальныхпеременных [[scope]] внешнейфункцииостаетсяжитьпослееезавершения. Внутренняяфункцияможетобратитьсякнемувлюбоймоментиполучитьпеременнуювнешнейфункции.
Замыкания Выполнение функции с замыканием: functionaddHideHandler(sourceId,targetId){ // созданобъект [[scope]] сосвойствамиsourceId, targetId // записатьв [[scope]] свойствоsourceNode varsourceNode=document.getElementById(sourceId); // записатьв [[scope]] свойство handler varhandler =function(){ vartargetNode=document.getElementById(targetId);targetNode.style.display='none'; }; sourceNode.onclick=handler; // функциязакончилавыполнение // (***) итут - самоеинтересное! }
Замыкания Призапускефункциивсепроисходитстандартно: • создается [[scope]] • тудазаписываютсялокальныепеременные • внутренняяфункцияполучаетссылкуна [[scope]] Новсамомконце - внутренняяфункцияприсваиваетсяsourceNode.onclick. Внешняяфункциязакончиласвоюработу, новнутренняя - можетзапуститьсякогда-нибудьпотом. Интерпретаторjavascriptнепроводитанализ - понадобятсяливнутреннейфункциипеременныеизвнешней, икакиепеременныемогутбытьнужны. Вместоэтогоонпростооставляетвесь [[scope]] внешнейфункциивживых. Чтобыкогдавнутренняяфункциязапустится, еслионавдругненайдеткакую-либопеременнуювсвоем [[scope]] - онамоглаобратитьсяк [[scope]] внешнейфункцииинашлабыеетам.
Замыкания Пример: functionmakeShout(){ varphrase ="Превед!”; varshout =function(){ alert(phrase); } phrase ="Готово!"; return shout; } shout =makeShout(); shout();// чтовыдаст?
Замыкания • ВнутриmakeShout() Пример: functionmakeShout(){ varphrase ="Превед!”; varshout =function(){ alert(phrase); } phrase ="Готово!"; return shout; } shout =makeShout(); shout(); • создается[[scope]] • В [[scope]] пишется: phrase="Превед!” • В [[scope]] пишется: shout=..функция.. • Присозданиифункция shout получаетссылкуна [[scope]] внешнейфункции • [[scope]].phrase меняетсянановоезначение "Готово!”
Замыкания Призапуске shout() Пример: functionmakeShout(){ varphrase ="Превед!”; varshout =function(){ alert(phrase); } phrase ="Готово!"; return shout; } shout =makeShout(); shout(); • Создаетсясвойсобственныйобъект [[scope2]] • Ищется phrase в [[scope2]] - ненайден • Ищется phrase в [[scope]] внешнейфункции - найденозначение "Готово!” • alert("Готово!") Тоесть, внутренняяфункцияполучаетпоследнеезначениевнешнихпеременных.
Замыкания Счетчик: functionmakeCounter(){ varnumberOfCalls=0; returnfunction(){ return++numberOfCalls; } } varcounter1 =makeCounter(); console.log(counter1());// -> 1 console.log(counter1());// -> 2 console.log(counter1());// -> 3 varcounter2 =makeCounter(); console.log(counter2());// -> 1 console.log(counter2());// -> 2
lambda-функции VS анонимные функции • Согласно wiki синонимы • Есть подозрение что замешан python lambdaх,у: х+у
Callback hell in action getUser(params["id"], function(err,user){ if(!user)returnres.send(404); getProfile(user, function(err,profile){ if(err)returnres.send(500); if(profile){ update(user,profile,params,function(err){ if(err)returnres.send(500); res.send(profile); }) }else{ create(user,params,function(err,profile){ if(err)returnres.send(500); res.send(profile); }) } }) });
Это можно поправить getUser(params["id"],gotUser); functiongotUser(err,user){ if(!user)returnres.send(404); // don't inline closure, use bindgetProfile(user,saveProfile.bind(this,user)); } functionsaveProfile(user,err,profile){ if(err)returnres.send(500); if(profile){ update(user,profile,params,saved); }else{ create(user,params,saved); } }
Выполнение параллельных задач varupdatedA=false; varupdatedB=false; updateA(function(){ if(updatedB) done() updatedA=true; }) updateB(function(){ if(updatedA) done() updatedB=true; })
Когда задач много var files =[a,b,c,d]; varfiltered =[]; vardone =0; files.forEach(function(file){ something(file, function(passed){ if(!passed)return; filtered.push(f); done +=1; if(done ==files.length)cb(); }) });
Deferred and Promises Deferred — этоотложенныйрезультат, которыйстанетизвестенчерезнекотороевремя.
Deferred and Promises readFile("file.txt",function(err, result) {// continue here…}); // becomesvarpromiseForResult=readFile("file.txt");
Deferred and Promises Инструкция functionreadFile(fileName){ vardeferred =Q.defer(); readFileAsync(fileName,function(error, result){ if(error){ deferred.reject(error); }else{ deferred.resolve(result); } }); returndeferred.promise; } • СоздатьDeferred объект • Призавершенииасинхронногометодаперевести Deferred объектвнужноесостояние • ПередатьDeferred.promiseобъектизтекущейфункциикуда-то, гдеегосостояниебудутотслеживать. Почемупривозвратеизфункциипередаетсянесам Deferred объект, аименно Promise?
Deferred and Promises • Deferred объект — этовсеголишьхранилищесостоянияасинхроннойфункции. Такихсостоянийобычнонесколько: • pending — ожиданиезавершенияпроцесса • rejected — процессзаконченпадением • resolved — процессзаконченуспешно • Крометогоу Deferred объектаестьрядметодов, которыемогутменятьегосостояние. Например, метод .resolve(). • Посостоянию Deferred объектамыможемсудить, законченлипроцесс, состояниекоторогомыотслеживаем.
Deferred and Promises • Обработчикивыполняются последовательно • Еслиобработчик «выполнения»/«ошибки» добавляетсякуже «выполненному»/«отменённому» объекту, тоонбудетвызваннемедленно. • Повторныйвызов resolve/reject неприводткизменениюсостоянияиповторномувызовуобработчиков, апростоигнорируется (иневажно, чтобыловызванодоэтого resolve() или reject()). • Хотяpromises появились в jQuery 1.5 лучше их не юзать
Библиотека Q • Установка: npm, bower, NuGet • Модули для: Node.js, CommonJS, AMD, microjs • Понимает промисы: jQuery, Dojo, When.js, WinJS • Множество сторонних модулей • mongoose-q, mongo-q
Библиотека Q • .then, .done, .catch(fail), .finally(fin) readFile("file.txt") .then(function(contentOfFile){ // Do something with content }) .catch(function(error){ // Handle error }) .finally(function(){ // Call always }) .done();
Библиотека Q • .then – выполняем последовательно readFile("file.txt") .then(function(contentOfFile){ // Do something with contentOfFile returnreadFile("file2.txt") }) .then(unction(contentOfFile2){ // Do something with contentOfFile2 returnreadFile("file3.txt") }) .catch(function(error){ // Error handle }) .done()