├── .gitignore ├── README.markdown ├── compile.js ├── html ├── img │ ├── all-objects.png │ ├── arch-controller.png │ ├── arch-directives.png │ ├── arch-scope.png │ ├── arch-services.png │ ├── arch-view.png │ ├── arch.png │ ├── assignment-example.png │ ├── assignment.png │ ├── backbone.gif │ ├── both.png │ ├── broken-heart.jpg │ ├── call_stack.png │ ├── call_stack_unfold.png │ ├── callback-hell.png │ ├── complex.jpg │ ├── composite.svg │ ├── consumers_sources.jpg │ ├── crockford.png │ ├── default.svg │ ├── dennis_ritchie.jpg │ ├── event_loop.png │ ├── facade.svg │ ├── fmi.jpg │ ├── great-power.png │ ├── heavy-loaded.jpg │ ├── hidden_classes.png │ ├── http.png │ ├── ie-glue.jpg │ ├── impure_function.jpg │ ├── io_callback.png │ ├── javascript-objects-treasure-map.png │ ├── jungle.jpg │ ├── ko.jpg │ ├── lion.jpg │ ├── magic.jpg │ ├── much_much_later.jpg │ ├── mvc-server-side.png │ ├── mvw.png │ ├── mvw2.png │ ├── node-event-loop.jpg │ ├── node.gif │ ├── not-understand.jpg │ ├── observer.svg │ ├── outofthebox.png │ ├── praise-lambda.png │ ├── promise_states_simple.jpg │ ├── promises.png │ ├── prototype.png │ ├── prototype1.png │ ├── pure_function.jpg │ ├── pure_phylosoraptor.jpg │ ├── rest.png │ ├── runtime.png │ ├── savior.png │ ├── scopes-real.png │ ├── scopes.png │ ├── separation-of-concerns.png │ ├── separationofconcerns.jpg │ ├── socket.jpg │ ├── sofiajs.png │ ├── spring-triangle.png │ ├── strategy.svg │ ├── tailfact.png │ ├── tcp-joke.png │ ├── tooling │ │ ├── android.jpg │ │ ├── chrome-dev-tools-network.png │ │ ├── chrome-timeline.jpg │ │ ├── cpu-profiler.png │ │ ├── facebook-sprite.png │ │ └── memory-profiling.png │ ├── typed_arrays.png │ ├── w3c.png │ ├── waitforit.jpg │ └── worship.jpg └── src │ └── remark.js ├── layout.html.mustache ├── lectures ├── 00-intro.markdown ├── 01-variables-functions.markdown ├── 02-modules.markdown ├── 03-01-moar-functional.markdown ├── 03-functional.markdown ├── 04-01-moar-modules.markdown ├── 04-prototypes.markdown ├── 05-01-es2015.markdown ├── 05-02-promises-and-generators-short.markdown ├── 06-node-js.markdown ├── 07-express │ ├── expressServer │ │ ├── app.js │ │ ├── package.json │ │ ├── router.js │ │ └── static │ │ │ └── index.html │ ├── generator.js │ └── httpServer │ │ └── app.js ├── 08-dom-intro-and-events.markdown ├── 09-websockets.markdown ├── 10-jquery.markdown ├── 10-js-tooling.markdown ├── 11-jquery.markdown └── 12-d3.markdown ├── package.json └── tasks ├── 01 └── README.md ├── 02 ├── README.md ├── more-examples │ └── complement.js └── solutions │ ├── cons-car-cdr.js │ ├── curry.js │ ├── map-filt-red-native.js │ ├── map-filt-red-with-pair.js │ ├── sequence-and-compose.js │ ├── sum.js │ └── sumSquaresOfArgs.js ├── 03 ├── README.md ├── homework │ ├── README.md │ └── bootstrap │ │ ├── index.js │ │ ├── lib │ │ ├── observables │ │ │ ├── Observable.js │ │ │ └── PostsCollection.js │ │ └── observers │ │ │ ├── LogObserver.js │ │ │ ├── MailObserver.js │ │ │ └── Observer.js │ │ ├── package.json │ │ └── public │ │ ├── index.html │ │ └── src │ │ └── main.js └── solutions.js ├── 04 ├── README.md └── solutions │ ├── task1.js │ ├── task2-es6.js │ └── task2.js ├── 05 ├── 01.js ├── README.md └── solutions │ ├── asyncgen.js │ ├── simpleGenerator.js │ └── task1.js ├── 06 └── README.md ├── 07 └── README.md ├── 08 └── wsChat │ ├── app.js │ ├── client.js │ └── package.json └── 09 ├── README.md ├── index.html └── src ├── DOMCompiler.js ├── Provider.js ├── Scope.js ├── Utils.js └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | *.publishproj 131 | 132 | # NuGet Packages Directory 133 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 134 | #packages/ 135 | 136 | # Windows Azure Build Output 137 | csx 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.[Pp]ublish.xml 152 | *.pfx 153 | *.publishsettings 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[cod] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | 218 | node_modules 219 | compiled 220 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # JavaScript за напреднали @ ФМИ 2015/16 2 | 3 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/FMIjs/gitter?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 4 | 5 | ```sh 6 | npm install 7 | ./compile.js 8 | firefox compiled/00-intro.html 9 | ``` 10 | -------------------------------------------------------------------------------- /compile.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var hogan = require('hogan.js'), 4 | fs = require('fs'), 5 | path = require('path'), 6 | Q = require('q'), 7 | cpr = require('cpr').cpr, 8 | lecturesDir = './lectures', 9 | compiledDir = './compiled', 10 | layoutFilename = './layout.html.mustache', 11 | requiredForComiple = process.argv.slice(2); 12 | 13 | 14 | function discoverLectures() { 15 | var deferred = Q.defer(); 16 | fs.readdir(lecturesDir, function (error, lectures) { 17 | if (error) { 18 | deferred.reject(new Error(error)); 19 | } else { 20 | deferred.resolve(lectures); 21 | } 22 | }); 23 | 24 | return deferred.promise; 25 | } 26 | 27 | function loadLayout() { 28 | var deferred = Q.defer(); 29 | fs.readFile(layoutFilename, function (error, data) { 30 | if (error) { 31 | console.error('Too bad: ' + error); 32 | deferred.reject(new Error(error)); 33 | } else { 34 | deferred.resolve(hogan.compile(data.toString())); 35 | } 36 | }); 37 | 38 | return deferred.promise; 39 | } 40 | 41 | function compile(layout, template_name) { 42 | var deferred = Q.defer(); 43 | fs.readFile(path.join(lecturesDir, template_name), function (error, data) { 44 | if (error) { 45 | deferred.reject(new Error(error)); 46 | } else { 47 | deferred.resolve(layout.render({slides: data.toString()})); 48 | } 49 | }); 50 | 51 | return deferred.promise; 52 | } 53 | 54 | loadLayout().then(function (layout) { 55 | discoverLectures().then(function (lectures) { 56 | var templatePattern = process.argv[2], 57 | templates = lectures.filter(function (lecture) { 58 | return RegExp(templatePattern).test(lecture); 59 | }); 60 | 61 | console.log(templates); 62 | 63 | templates.forEach(function (template) { 64 | compile(layout, template).then(function (output) { 65 | var basename = template.split('.')[0], 66 | compiled = path.join(compiledDir, basename); 67 | fs.writeFile(compiled + '.html', output, function (error) { 68 | if (error) { 69 | console.error('Writing file failed: ' ); 70 | console.error(error); 71 | } 72 | }); 73 | }, function (error) { 74 | console.error('Could not read lecture file ' + template); 75 | console.error(error); 76 | }); 77 | }); 78 | 79 | cpr('html', 'compiled', { 80 | overwrite: true, 81 | confirm: true 82 | }, function (error) { 83 | if (error) { 84 | console.error('Error copying src and img folders: '); 85 | console.error(error); 86 | } 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /html/img/all-objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/all-objects.png -------------------------------------------------------------------------------- /html/img/arch-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch-controller.png -------------------------------------------------------------------------------- /html/img/arch-directives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch-directives.png -------------------------------------------------------------------------------- /html/img/arch-scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch-scope.png -------------------------------------------------------------------------------- /html/img/arch-services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch-services.png -------------------------------------------------------------------------------- /html/img/arch-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch-view.png -------------------------------------------------------------------------------- /html/img/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/arch.png -------------------------------------------------------------------------------- /html/img/assignment-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/assignment-example.png -------------------------------------------------------------------------------- /html/img/assignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/assignment.png -------------------------------------------------------------------------------- /html/img/backbone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/backbone.gif -------------------------------------------------------------------------------- /html/img/both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/both.png -------------------------------------------------------------------------------- /html/img/broken-heart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/broken-heart.jpg -------------------------------------------------------------------------------- /html/img/call_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/call_stack.png -------------------------------------------------------------------------------- /html/img/call_stack_unfold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/call_stack_unfold.png -------------------------------------------------------------------------------- /html/img/callback-hell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/callback-hell.png -------------------------------------------------------------------------------- /html/img/complex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/complex.jpg -------------------------------------------------------------------------------- /html/img/composite.svg: -------------------------------------------------------------------------------- 1 |

Component




+ operation()

[Not supported by viewer]

Leaf




+ operation()

[Not supported by viewer]

Composite


+ components


+ operation()

[Not supported by viewer]
for component in componentscomponent.operation()
-------------------------------------------------------------------------------- /html/img/consumers_sources.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/consumers_sources.jpg -------------------------------------------------------------------------------- /html/img/crockford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/crockford.png -------------------------------------------------------------------------------- /html/img/dennis_ritchie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/dennis_ritchie.jpg -------------------------------------------------------------------------------- /html/img/event_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/event_loop.png -------------------------------------------------------------------------------- /html/img/facade.svg: -------------------------------------------------------------------------------- 1 |

Client



[Not supported by viewer]

Facade




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]
-------------------------------------------------------------------------------- /html/img/fmi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/fmi.jpg -------------------------------------------------------------------------------- /html/img/great-power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/great-power.png -------------------------------------------------------------------------------- /html/img/heavy-loaded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/heavy-loaded.jpg -------------------------------------------------------------------------------- /html/img/hidden_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/hidden_classes.png -------------------------------------------------------------------------------- /html/img/http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/http.png -------------------------------------------------------------------------------- /html/img/ie-glue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/ie-glue.jpg -------------------------------------------------------------------------------- /html/img/impure_function.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/impure_function.jpg -------------------------------------------------------------------------------- /html/img/io_callback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/io_callback.png -------------------------------------------------------------------------------- /html/img/javascript-objects-treasure-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/javascript-objects-treasure-map.png -------------------------------------------------------------------------------- /html/img/jungle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/jungle.jpg -------------------------------------------------------------------------------- /html/img/ko.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/ko.jpg -------------------------------------------------------------------------------- /html/img/lion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/lion.jpg -------------------------------------------------------------------------------- /html/img/magic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/magic.jpg -------------------------------------------------------------------------------- /html/img/much_much_later.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/much_much_later.jpg -------------------------------------------------------------------------------- /html/img/mvc-server-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/mvc-server-side.png -------------------------------------------------------------------------------- /html/img/mvw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/mvw.png -------------------------------------------------------------------------------- /html/img/mvw2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/mvw2.png -------------------------------------------------------------------------------- /html/img/node-event-loop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/node-event-loop.jpg -------------------------------------------------------------------------------- /html/img/node.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/node.gif -------------------------------------------------------------------------------- /html/img/not-understand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/not-understand.jpg -------------------------------------------------------------------------------- /html/img/observer.svg: -------------------------------------------------------------------------------- 1 |

Observer




+ notify()

[Not supported by viewer]

ConcreteObserverB




+ notify()

[Not supported by viewer]

ConcreteObserverA




+ notify()

[Not supported by viewer]

Subject


- observers


+ attachObserver(o:Observer)

+ detachObserver(o:Observer)

+ notifyObservers()

[Not supported by viewer]
for o in observerso.notify()
-------------------------------------------------------------------------------- /html/img/outofthebox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/outofthebox.png -------------------------------------------------------------------------------- /html/img/praise-lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/praise-lambda.png -------------------------------------------------------------------------------- /html/img/promise_states_simple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/promise_states_simple.jpg -------------------------------------------------------------------------------- /html/img/promises.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/promises.png -------------------------------------------------------------------------------- /html/img/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/prototype.png -------------------------------------------------------------------------------- /html/img/prototype1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/prototype1.png -------------------------------------------------------------------------------- /html/img/pure_function.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/pure_function.jpg -------------------------------------------------------------------------------- /html/img/pure_phylosoraptor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/pure_phylosoraptor.jpg -------------------------------------------------------------------------------- /html/img/rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/rest.png -------------------------------------------------------------------------------- /html/img/runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/runtime.png -------------------------------------------------------------------------------- /html/img/savior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/savior.png -------------------------------------------------------------------------------- /html/img/scopes-real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/scopes-real.png -------------------------------------------------------------------------------- /html/img/scopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/scopes.png -------------------------------------------------------------------------------- /html/img/separation-of-concerns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/separation-of-concerns.png -------------------------------------------------------------------------------- /html/img/separationofconcerns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/separationofconcerns.jpg -------------------------------------------------------------------------------- /html/img/socket.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/socket.jpg -------------------------------------------------------------------------------- /html/img/sofiajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/sofiajs.png -------------------------------------------------------------------------------- /html/img/spring-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/spring-triangle.png -------------------------------------------------------------------------------- /html/img/strategy.svg: -------------------------------------------------------------------------------- 1 |

<<Interface>>
IStrategy




+ execute()

[Not supported by viewer]

ConcreteStrategy1




+ execute()

[Not supported by viewer]

ConcreteStrategy2




+ execute()

[Not supported by viewer]

Context





[Not supported by viewer]
-------------------------------------------------------------------------------- /html/img/tailfact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tailfact.png -------------------------------------------------------------------------------- /html/img/tcp-joke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tcp-joke.png -------------------------------------------------------------------------------- /html/img/tooling/android.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/android.jpg -------------------------------------------------------------------------------- /html/img/tooling/chrome-dev-tools-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/chrome-dev-tools-network.png -------------------------------------------------------------------------------- /html/img/tooling/chrome-timeline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/chrome-timeline.jpg -------------------------------------------------------------------------------- /html/img/tooling/cpu-profiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/cpu-profiler.png -------------------------------------------------------------------------------- /html/img/tooling/facebook-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/facebook-sprite.png -------------------------------------------------------------------------------- /html/img/tooling/memory-profiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/tooling/memory-profiling.png -------------------------------------------------------------------------------- /html/img/typed_arrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/typed_arrays.png -------------------------------------------------------------------------------- /html/img/w3c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/w3c.png -------------------------------------------------------------------------------- /html/img/waitforit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/waitforit.jpg -------------------------------------------------------------------------------- /html/img/worship.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/html/img/worship.jpg -------------------------------------------------------------------------------- /layout.html.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | js @ ФМИ 5 | My Awesome Presentation 6 | 7 | 33 | 34 | 35 | 45 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /lectures/00-intro.markdown: -------------------------------------------------------------------------------- 1 | # JavaScript @ ФМИ 2 | 3 | ### 2015/2016 4 | 5 | --- 6 | 7 | # JavaScript като цяло 8 | 9 | * Client side (демек в браузъра) 10 | * Server side (демек не в браузъра) 11 | * На някои странни места (tessel.io) 12 | 13 | --- 14 | # Ще си говорим за 15 | 16 | * Програмиране с JavaScript генерално 17 | * Какво е асинхронен код и защо го ползваме 18 | * Какво (не) харесваме в асинхронния код 19 | * Как да се справим с проблемите 20 | * Каква е разликата между server и client side JavaScript 21 | * ФУНКЦИОНАЛНО ПРОГРАМИРАНЕ!!! 22 | 23 | --- 24 | 25 | # Бегъл план 26 | 27 | * Основните неща в езика 28 | * Променливи 29 | * Функции 30 | * Callbacks 31 | * Събития 32 | * По-интересни js специфични теми 33 | * ООП (без класове) 34 | * Прототипи 35 | * Наследяване 36 | 37 | --- 38 | 39 | class: center 40 | # За да е по-лесно за всички 41 | 42 | ## Node first 43 | 44 | ## Browser later 45 | 46 | --- 47 | 48 | # Защото 49 | 50 | * Консистентност 51 | * Тестването с node е значително по-просто от тестването в браузър. 52 | * Не искаме да рискуваме изказвания като „ама то може и в браузъра“ 53 | 54 | --- 55 | 56 | # Лекции 57 | 58 | * Веднъж в седмицата 59 | * Три часа 60 | * Присъствието очевидно не е задължително 61 | * Но със сигурност помага много 62 | 63 | ??? 64 | 65 | * Кои дни 66 | * От колко часа 67 | * Кои зали 68 | 69 | --- 70 | 71 | # ~Упражнения 72 | 73 | ** < see previous slide > ** 74 | 75 | ??? 76 | 77 | * Кои дни 78 | * От колко часа 79 | * Кои зали 80 | 81 | --- 82 | 83 | # Домашни 84 | 85 | * Планираме да са ~ веднъж на две седмици (но сме реалисти) 86 | * Обикновено срокът ще е една седмица 87 | * Ще се тестват автоматично 88 | * „Ама то sum е почти същото като summ“ 89 | * „Е добре де, връщам String, а не Number, ама е правилно“ 90 | * … 91 | * Очевидно ще формират част от оценката 92 | 93 | ??? 94 | 95 | * Колко често(ни се иска) 96 | * Как ще се проверяват 97 | * Каква тежест 98 | 99 | --- 100 | 101 | # Проекти 102 | 103 | * Не може да завършвате курса без проект 104 | * Няма проблем да е web, с back-end на друг език(ruby/go/perl/…) 105 | * Много харесваме идеята за нещо, което е тотално несвързано с web 106 | * Никога не е твърде рано да започнете 107 | * Трябва да одобрим идеята 108 | 109 | По-лесно е да научите технология, ако я ползвате за реализирането на нещо, което ви е интересно 110 | 111 | ??? 112 | 113 | * Добре е да се почнат възможно най-рано 114 | * Добрата идея помага да научиш нужната технология 115 | * Каква тежест ще имат? 116 | 117 | --- 118 | 119 | # Тестове 120 | 121 | * Два на брой 122 | * В средата на семестъра 123 | * В края на семестъра/през сесията 124 | * В moodle 125 | * Може да изпуснете един 126 | * Може да поправите някой през сесията 127 | * НЕ РАЗЧИТАЙТЕ ТВЪРДЕ МНОГО НА ПОСЛЕДНИТЕ ДВЕ 128 | 129 | --- 130 | 131 | # Дните 132 | 133 | Ще се постараем да не сме толкова досадни. Да заспивате след курса и да се събуждате, за да дойдете на курса определено не е най-прекрасното преживяване. За нас също. 134 | 135 | --- 136 | 137 | # And now for something completely different … 138 | 139 | --- 140 | class: center 141 | # UNIX 142 | ![Dennis Ritchie](img/dennis_ritchie.jpg) 143 | 144 | ??? 145 | 146 | Горещо препоръчваме ползването на UNIX-like среди. 147 | 148 | --- 149 | 150 | # Но все пак 151 | 152 | В [download секцията](http://nodejs.org/download) на сайта на nodejs има msi инсталатори. 153 | 154 | --- 155 | 156 | # nvm/n 157 | 158 | По ред причини глобалното инсталиране на node за dev цели не е твърде удобен подход. 159 | 160 | Двата варианта, които предлагаме са [nvm](github.com/creationix/nvm) и [n](https://github.com/visionmedia/n). 161 | 162 | --- 163 | 164 | # REPL demo 165 | 166 | --- 167 | 168 | # Скриптове 169 | 170 | Пишем програмата си във файл. След това подаваме името на файла като аргумент на `node` командата и по този начин го изпълняваме. 171 | 172 | Изненадани сте, нали? 173 | 174 | ```sh 175 | $ cat hi.js 176 | function sayHi(name) { 177 | console.log('Hello, ' + name); 178 | } 179 | sayHi('Pencho'); 180 | 181 | $ node hi.js 182 | Hello, Pencho 183 | ``` 184 | -------------------------------------------------------------------------------- /lectures/01-variables-functions.markdown: -------------------------------------------------------------------------------- 1 | # Variables, functions, linting 2 | 3 | --- 4 | ## Книжки 5 | 6 | * JavaScript - The Good Parts /David Crockford/ 7 | 8 | --- 9 | 10 | # Променливи 11 | 12 | ```javascript 13 | > a = 5 // BAD 14 | 5 15 | > b = 7; // BAD 16 | 7 17 | > var c = 43 // meeeeeeeh... aaaalmost 18 | undefined 19 | > var d = 42; // GOOD 20 | undefined 21 | ``` 22 | 23 | --- 24 | 25 | # Оценяване 26 | 27 | ```javascript 28 | > var a = 20 29 | undefined 30 | > a 31 | 20 32 | > var b = '20' 33 | undefined 34 | > a == b 35 | true 36 | > a === b 37 | false 38 | ``` 39 | 40 | * Познатото ни двойно равно (==) ще сравно стойността обръщайки типовете до подходящ (според вируталната машина) тип 41 | * Ето защо проверката за еквивалентност по тип и стойност в JS се прави с === (тройно равно) 42 | * Макар и незначителна тази разлика може да доведе до неочаквани и най-често неприятни резултати 43 | 44 | --- 45 | 46 | # Типове „неща“ 47 | ## Числа 48 | 49 | ```javascript 50 | > var lowBoundary = 9000; 51 | undefined 52 | > typeof lowBoundary 53 | 'number' 54 | > var pi = 3.14; 55 | undefined 56 | > typeof pi 57 | 'number' 58 | ``` 59 | * Всичко има тип 60 | * `undefined` е ключова дума в езика 61 | * За да проверим дали нещо е дефинирано или не... : 62 | 63 | ```javascript 64 | > typeof notDefinedVar === 'undefined' 65 | true 66 | ``` 67 | 68 | може и така: 69 | 70 | ```javascript 71 | > notDefinedVar === undefined 72 | true 73 | ``` 74 | 75 | --- 76 | 77 | # Типове „неща“ 78 | ## Низове 79 | 80 | ```javascript 81 | > var name = 'Pen\ncho'; 82 | ``` 83 | 84 | Съвсем същото като 85 | 86 | ```javascript 87 | > var name = "Pen\ncho"; 88 | ``` 89 | 90 | --- 91 | 92 | # Типове „неща“ 93 | ## Списъци 94 | 95 | ```javascript 96 | > var team = ['Joro', 'Minko', 'Evgeni']; 97 | undefined 98 | > team.length 99 | 3 100 | > team[0] 101 | 'Joro' 102 | > team[1] 103 | 'Minko' 104 | > team[-1] 105 | undefined 106 | ``` 107 | * Списъците както всичко друго - са обекти. 108 | * Можем да ги създадем с new или директно с `[]` (квадратни скоби) 109 | * Добавянето на елементи НЕ предполага явно 'разщиряване на размера' 110 | * Списъците са динамични и се оразмеряват според броя елементи 111 | 112 | --- 113 | # Типове „неща“ 114 | ## Обекти 115 | 116 | ```javascript 117 | > var panda = {name: 'Стамат', age: 12, cuteness: 9000.001 }; 118 | undefined 119 | > panda 120 | { name: 'Стамат', 121 | age: 12, 122 | cuteness: 9000.001 } 123 | > panda.name 124 | 'Стамат' 125 | > panda.weight = 30 126 | 30 127 | > panda.class 128 | undefined 129 | > panda['cuteness'] 130 | 9000.001 131 | ``` 132 | 133 | * Обект/асоциативен списък са едно и също в javascript се оказва 134 | че всички атрибути на даден обект са обект на autovivification ... 135 | * ...в момента на първото присвояване на стойност!!! 136 | * ...но ако не е било присвоено нищо на даден атрибут, първото му 137 | прочитане връща undefined 138 | * т.е. -> при добавяне на атрибут се самосъздава елемента без да е нужно 139 | неговото експлицитно (по някакъв начин) предефиниране. 140 | 141 | --- 142 | # Функции 143 | 144 | ```javascript 145 | function sayHi(name) { 146 | console.log('Hello, ' + name); 147 | } 148 | ``` 149 | 150 | ```javascript 151 | function sumTwoThings(a, b) { 152 | return a + b; 153 | }; 154 | ``` 155 | 156 | ```javascript 157 | function sumAllTheThings () { 158 | var result = arguments[0]; 159 | for(var i = 1; i < arguments.length; ++i) { 160 | result += arguments[i]; 161 | } 162 | 163 | return result; 164 | } 165 | ``` 166 | 167 | * Дефиницията на функциите е като в C и Java 168 | * Но в JavaScript дефиниция на функция води до 169 | създаване на екземпляр на обект от типа `Function` 170 | * т.е. всичи функции са обекти, но за това повече после... 171 | 172 | ```javascript 173 | var sumTwoThings = function (a, b) { 174 | return a + b; 175 | }; 176 | ``` 177 | 178 | # Асинхронни функции 179 | ```javascript 180 | setTimeout(function () { 181 | console.log('Hello There'); 182 | }, 1000); 183 | ``` 184 | 185 | * Ще се изпълни след хиляда мили секунди 186 | 187 | 188 | ```javascript 189 | for (var i = 0; i < 5; i++) { 190 | setTimeout(function () { 191 | console.log(i); 192 | }, 1000); 193 | } 194 | > 5 195 | 5 196 | 5 197 | 5 198 | 5 199 | ``` 200 | 201 | * Иска да изпечата **i**, но докато е минала една секунда цикълът отдавна е стигнал вече до 5 202 | * Асинхронна операция (не е в нормалния поток на изпълнение) 203 | * Изпълнява се от Event Loop модела на Javascript 204 | 205 | --- 206 | ## `for` е кофти 207 | 208 | * Не, няма смисъл да спорим 209 | * Повече по този въпрос после 210 | * Има няколко други механизъма за итериране, които са в духа на JavaScript 211 | 212 | --- 213 | 214 | # По-подробно за списъци 215 | 216 | ## Методи 217 | 218 | ```javascript 219 | > Object.getOwnPropertyNames(Object.getPrototypeOf(a)) 220 | [ 'length', 221 | 'constructor', 222 | 'toString', 223 | 'toLocaleString', 224 | 'join', 225 | 'pop', 226 | 'push', 227 | 'concat', 228 | 'reverse', 229 | 'shift', 230 | 'unshift', 231 | 'slice', 232 | 'splice', 233 | 'sort', 234 | 'filter', 235 | 'forEach', 236 | 'some', 237 | 'every', 238 | 'map', 239 | 'indexOf', 240 | 'lastIndexOf', 241 | 'reduce', 242 | 'reduceRight' ] 243 | ``` 244 | 245 | --- 246 | ### `for` е гаден 247 | 248 | ```javascript 249 | var albums = ['Lateralus', '10,000 days', 'Ænima']; 250 | albums.forEach(function (album) { 251 | console.log(album + ' is an album by Tool'); 252 | }); 253 | ``` 254 | 255 | --- 256 | # filter 257 | 258 | ```javascript 259 | > albums.filter(function (album) { 260 | return album.charAt(0) === 'Æ'; 261 | }); 262 | ['Ænima'] 263 | ``` 264 | 265 | --- 266 | # map 267 | ```javascript 268 | > albums.map(function (album) { 269 | return album.toLowerCase(); 270 | }); 271 | ['lateralus', '10,000 days', 'ænima'] 272 | ``` 273 | 274 | --- 275 | # push/pop 276 | 277 | ```javascript 278 | > albums.push('Undertow'); 279 | 4 280 | > albums 281 | ['Lateralus', '10,000 days', 'Ænima', 'Undertow'] 282 | > albums.pop(); 283 | 'Undertow' 284 | albums 285 | ['Lateralus', '10,000 days', 'Ænima'] 286 | ``` 287 | 288 | --- 289 | # `Array` 290 | 291 | ```javascript 292 | > var bands = new Array(10); 293 | undefined 294 | > bands 295 | [ , , , , , , , , , ] 296 | ``` 297 | 298 | --- 299 | # `Array` 300 | 301 | ```javascript 302 | > var things = new Array(10, 'asd'); 303 | undefined 304 | > things 305 | [ 10, 'asd' ] 306 | ``` 307 | 308 | --- 309 | # shift/unshift 310 | 311 | Абсолютно същото, но в началото на списъка, а не в края 312 | 313 | --- 314 | # Прости структури от данни 315 | 316 | * Сам по себе си е списък 317 | * `pop`/`push` ⇨ стек 318 | * `unshift`/`pop` или `push`/`shift` ⇨ опашка 319 | 320 | --- 321 | 322 | # Нехомогенни 323 | 324 | ```javascript 325 | > things = [42, 'brie', {species: 'unicorn', пробабилитъ: '0.000000001'}] 326 | ``` 327 | 328 | 329 | --- 330 | # Сложност 331 | 332 | Сложността на операциите върху `Array` обекти най-вероятно не е каквато очаквате. За това има [много добро обяснение](http://stackoverflow.com/questions/11514308/big-o-of-javascript-arrays#answer-11535121). 333 | 334 | **TL;DR** Списъците са обекти, обектите са хешове. 335 | --- 336 | # Javascript runtime (v8) 337 | 338 | --- 339 | # Какво представлява? 340 | ![alt text](../html/img/runtime.png "runtime image") 341 | --- 342 | # Stacking 343 | 344 | javascript 345 | function f(b){ 346 | var a = 12; 347 | return a+b+35; 348 | } 349 | 350 | 351 | function g(x){ 352 | var m = 4; 353 | return f(m*x); //f се слага в stack 354 | } 355 | 356 | g(21); //g се слага в stack 357 | 358 | 359 | * Когато имаме function call push-ваме в stack-а. 360 | * Когато функцияна приключи pop-ваме от stack-а. 361 | 362 | --- 363 | #Javascript is single threaded 364 | --- 365 | # Какво се случва, когато възникне event докато Stack-a е пълен? 366 | --- 367 | ![alt text](../html/img/event_loop.png "event loop image") 368 | * Koгато stack-а Е ПРАЗЕН Event loop-а чете от TaskQueue-то 369 | и ако има task го push-ва в stack-а. 370 | --- 371 | 372 | ## Pushing tasks 373 | 374 | * В browser-а съобщения се добавят, когато възникне някакъв event и 375 | имаме event handler за него. (webAPIs -> addEventListener) 376 | 377 | * Също setTimeout(), process.nextTick() и др. 378 | 379 | javascript 380 | setTimeout(function(){ alert(1); }, 1000) //Изпълни се след минимум 1 сек 381 | 382 | 383 | setTimeout е функция на браузъра (Web APIs). Когато я извикаме browser-а пускай timer и след изтичането му 384 | callback-а се push-ва в TaskQueue. Подобно е и когано правим ajax requests. 385 | 386 | 387 | javascript 388 | setTimeout(function(){ alert(1); }, 0) 389 | // Може да си мислим като - TaskQueue.push(function(){ alert(1); }) 390 | 391 | 392 | * Важно е да пешем non-blocking code! 393 | 394 | * Трябва да се внимава с for, while, forEach защото са синхронни. 395 | 396 | * Render Queue също зависи от javascript. 397 | Не можем да рендерираме, когато имаме функции в stack-а.Render Queue има 398 | по-високо priority от callback-овете, които слагаме в TaskQueue. 399 | --- 400 | ### Blocking the event loop 401 | 402 | javascript 403 | var fs = require('fs'); //require the module for file system ops 404 | 405 | var file = fs.readFileSync('./myBigFile'); //synchronously read file 406 | 407 | 408 | Това блокира thread-а докато целия файл не бъде прочетен. 409 | 410 | Решението е fs.readFile(filepath, callback), което чете файла 411 | на малки парчета (chunks) и след това извиква 412 | callback-а с прочетения файл като аргумент. 413 | 414 | 415 | --- 416 | # За разнообразие и по нужда 417 | ### linter-и(мъхясване?) 418 | 419 | * jslint/jshint 420 | * Интегрират се с всяка разумна среда 421 | 422 | *** 423 | 424 | * vim : 425 | [чрез syntastic](https://github.com/scrooloose/syntastic) 426 | * emacs : 427 | [чрез flycheck](http://www.emacswiki.org/emacs/Flycheck) 428 | * sublime text : 429 | [sublime-jslint](http://opensourcehacker.com/2013/04/12/jslint-integration-for-sublime-text-2/) 430 | * ако не сте си писали редактора сами най-вероятно има разумен начин да подкарате linter с него. 431 | 432 | -------------------------------------------------------------------------------- /lectures/02-modules.markdown: -------------------------------------------------------------------------------- 1 | # js @ фми 2 | 3 | --- 4 | 5 | # Modules (в кратце) 6 | 7 | Разделяме кода в отделни файлове с цел по-добра изолация на отделните парчета от програмата ни. 8 | 9 | Получаваме по-лесен за поддръжка код, с по-лесно подменими компоненти(стига да се постараем да спазваме консистентни интерфейси между отделните парчета). 10 | 11 | --- 12 | 13 | # require 14 | 15 | Вградена функция в nodejs, с която „поискваме“ модули. 16 | 17 | ```javascript 18 | var fs = require('fs'); 19 | ``` 20 | 21 | Получаваме обект, отговарящ на модула, който сме `require`-нали. 22 | 23 | --- 24 | 25 | # module.exports 26 | 27 | Във всеки файл, който изпълняваме с node имаме достъп до `module` обекта. Това е обект, чрез който можем да достъпваме функционалност свързана с текущия модул. 28 | 29 | След време ще си поговорим по-подробно за това, за сега **don't overthink it!** 30 | 31 | --- 32 | # Домашни/задачки 33 | 34 | Обещахме! 35 | 36 | Като добри хора ще (се постараем много сериозно да) спазим обещанието си. 37 | 38 | --- 39 | # Домашни/задачки 40 | 41 | В тоя ред на мисли всяко домашно, което предавате ще трябва да спазва някаква форма. 42 | 43 | ```javascript 44 | module.exports = { 45 | fib: function (n) { 46 | … 47 | }, 48 | … 49 | } 50 | ``` 51 | 52 | Така си гарантираме, че имаме хубав интерфейс, през който да тестваме решенията ви. 53 | 54 | --- 55 | # Домашни/задачки 56 | 57 | [В github (ще) има задачка](). 58 | 59 | Живейте с мисълта, че имате срок до следващия петък. 60 | 61 | Скоро ще се появи обяснение как да предавате решенията си. 62 | 63 | --- 64 | # но първо 65 | 66 | --- 67 | # List quirks 68 | 69 | ## `for … in …` 70 | 71 | Най-вероятно не прави това, което очаквате. Ако знаехте какво точно прави, по-скоро бихте го избягвали. 72 | 73 | --- 74 | # List quirks 75 | 76 | ## `for … in …` 77 | 78 | ### python 79 | ```python 80 | a = ['brie', 42, 'bacon', 'cheddar', []] 81 | for thing in a: 82 | print thing 83 | 84 | brie 85 | 42 86 | bacon 87 | cheddar 88 | [] 89 | ``` 90 | 91 | --- 92 | # List quirks 93 | 94 | ## `for … in …` 95 | 96 | ### javascript 97 | ```javascript 98 | var a = ['brie', 42, 'bacon', 'cheddar', []] 99 | for (thing in a) { 100 | console.log(thing); 101 | } 102 | 103 | 0 104 | 1 105 | 2 106 | 3 107 | 4 108 | ``` 109 | 110 | --- 111 | # List quirks 112 | 113 | ## `for … in …` 114 | 115 | ### javascript 116 | ```javascript 117 | var a = ['brie', 42, 'bacon', 'cheddar', []] 118 | for (thing in a) { 119 | console.log(a[thing]); 120 | } 121 | brie 122 | 42 123 | bacon 124 | cheddar 125 | [] 126 | ``` 127 | 128 | --- 129 | # List quirks 130 | 131 | ## `for … in …` 132 | 133 | ### javascript 134 | ```javascript 135 | var a = ['brie', 42, 'bacon', 'cheddar', []] 136 | a.foo = 'bar' 137 | for (thing in a) { 138 | console.log(a[thing]); 139 | } 140 | brie 141 | 42 142 | bacon 143 | cheddar 144 | [] 145 | bar 146 | ``` 147 | 148 | --- 149 | # List quirks 150 | ## `for … in …` 151 | 152 | [В MDN пише ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in): 153 | > … 154 | > 155 | > A for...in loop iterates over the properties of an object in an arbitrary order 156 | > 157 | > … 158 | 159 | --- 160 | # List quirks 161 | ## `for … in …` 162 | 163 | За да сте информирани казваме изрично: 164 | 165 | Списъци се обхождат **винаги и само** с `forEach`. 166 | 167 | --- 168 | # List quirks 169 | ## `in` 170 | 171 | Същия аналог 172 | 173 | ### python 174 | ```python 175 | > a = [4, 5, 42, 'chunky'] 176 | > 'chunky' in a 177 | True 178 | ``` 179 | 180 | ### javascript 181 | ```javascript 182 | > var a = [4, 5, 42, 'chunky'] 183 | > 'chunky' in a 184 | false 185 | ``` 186 | 187 | --- 188 | # List quirks 189 | ## `in` 190 | 191 | Проверка дали нещо принадлежи на списък се прави с `indexOf`. 192 | 193 | ```javascript 194 | var a = [4, 5, 42, 'chunky'] 195 | a.indexOf('chunky') > -1 196 | true 197 | ``` 198 | 199 | --- 200 | # List quirks 201 | 202 | `Array` в javascript не е свързан списък, или последователни „клетки“ от паметта, или друга структура, която се използва често за представяне на масиви/списъци. 203 | 204 | `Array` е „javascript обект“, което означава, че се държи по-скоро като dict/Hash/HashMap/etc. от колкото като линейна или свързана структура. 205 | 206 | --- 207 | # `global` 208 | 209 | Обекта, в който се пазят всички глобални имена за текущия процес. 210 | 211 | --- 212 | # `typeof` 213 | 214 | Задължителното „ако идвате от php“ 215 | -------------------------------------------------------------------------------- /lectures/03-01-moar-functional.markdown: -------------------------------------------------------------------------------- 1 | # bind 2 | 3 | `bind` задава нов `this` на дадена функция и „забранява“ замяната му 4 | 5 | ```javascript 6 | var getThisValue = function () { 7 | return this.value; 8 | }; 9 | var thing = { value: 42 }, 10 | otherThing = { value: 73 }; 11 | ``` 12 | 13 | ```javascript 14 | > getThisValue.bind(thing)(); 15 | 42 16 | > getThisValue.bind(thing).call(otherThing) 17 | 42 18 | ``` 19 | 20 | --- 21 | 22 | # Функционални забавления 23 | 24 | В javascript липсват някои дребни удобства, които идват „безплатно“ в повечето други популярни езици. Пример за това е `range`. В много езици има възможност код като `range(10, 45)` да създаде обект, който да представлява нещо итеруемо, обхождащо стойностите от 10 до 45. Същото можем да постигнем с прости функционални похвати и в javascript: 25 | 26 | ```javascript 27 | function range(start, end, stopIteration) { 28 | return function () { 29 | if (start > end) { 30 | return stopIteration; 31 | } else { 32 | return start++; 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | --- 39 | 40 | След това можем да използваме `range` функцията по следния начин: 41 | 42 | ```javascript 43 | var stopIteration = {}, 44 | next = range(42, 73, stopIteration), 45 | item; 46 | 47 | while((item = next()) !== stopIteration) { 48 | console.log(item); 49 | } 50 | ``` 51 | 52 | --- 53 | 54 | # `===` и `!==` 55 | 56 | `===` и `!==` са **строги** проверки. Това ще рече, че за стойности от тип `string` и `number` не се прави type coercion. 57 | 58 | * `5 === '5'` се оценява до `false` 59 | * `6 !== '6'` се оценява до `true`. 60 | * в javascript няма предефиниране на оператори, така че `{} == {}` винаги ще бъде `false`, тъй като това са два различни обекта. 61 | * но поради имплицитния type coercion `{} == '[object Object]'` се оценява до `true` 62 | 63 | ⇨ правим проверката с `!==`, а не с `!=` 64 | 65 | --- 66 | 67 | # Алтернативи 68 | 69 | * Вместо проверка за `stopIteration` да „хвърлим“ грешка 70 | * Да получим тялото на цикъла като функция 71 | 72 | ```javascript 73 | function range(start, end, stopIteration) { 74 | return function () { 75 | if (start > end) { 76 | throw new Error('RANGE OVER'); 77 | } else { 78 | return start++; 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | Обаче: 85 | * Все още не знаем какво точно е `Error` 86 | * Поведението му може да ни изненада неприятно 87 | * В javascript „хвърлянето“ на грешки не е на почит, защото не се държи възпитано при асинхронен код 88 | 89 | --- 90 | 91 | # Задачка 92 | ### `function iterator(array, stopIteration)` 93 | 94 | Прави същото като `range`, но обхождайки подадения **Array-like** обект. 95 | 96 | `function iterator(array, stopIteration)`, която прави същото като `range`, но обхождайки подадения **Array-like** обект. 97 | 98 | --- 99 | 100 | # Задачка 101 | ### `rangeMap` 102 | 103 | 104 | ```javascript 105 | > var doubled = rangeMap(1, 3, function (item) { return item + item }); 106 | > console.log(doubled) 107 | [2, 4, 6] 108 | ``` 109 | Алтернативна имплементация на `range`, която не очаква `stopIteration` обект, а се държи като `map`, очаквайки функция, която да изпълни върху всички елементи. 110 | -------------------------------------------------------------------------------- /lectures/04-01-moar-modules.markdown: -------------------------------------------------------------------------------- 1 | # Modules (в малко повече детайл) 2 | 3 | Разделяме кода в отделни файлове с цел по-добра изолация на отделните парчета от програмата ни. 4 | 5 | Спазваме DRY (Don't repeat yourself) принципа - модулите са удобен начин за преизползване на код. 6 | 7 | Възползваме се от ["стандартната библиотека"](https://nodejs.org/api/) на Node.js и [огромната база](http://npmjs.com/) от допълнителни модули. 8 | 9 | --- 10 | 11 | # Собствен модул - библиотека 12 | 13 | ```javascript 14 | // file math.js 15 | function add(x, y) { 16 | return x + y; 17 | } 18 | 19 | module.exports = { 20 | add: add 21 | }; 22 | 23 | ``` 24 | 25 | ```javascript 26 | // file index.js 27 | var math = require('./math'); 28 | console.log(math.add(4, 6)); 29 | ``` 30 | 31 | --- 32 | 33 | # Собствен модул - клас 34 | 35 | ```javascript 36 | // file my_class.js 37 | var MyClass = function() { 38 | this.member = 42; 39 | } 40 | 41 | module.exports = MyClass; 42 | 43 | ``` 44 | 45 | ```javascript 46 | // file index.js 47 | var myClass = require('./my_class'); 48 | var instance = new myClass(); 49 | 50 | console.log(instance.member); 51 | ``` 52 | 53 | --- 54 | 55 | # Модули от "стандартната библиотека" 56 | 57 | ```javascript 58 | var fs = require('fs'); 59 | 60 | // `__dirname` връща пътя към текущия модул 61 | var fileName = __dirname + '/out.txt'; 62 | fs.writeFileSync(fileName, "This will go in the file"); 63 | ``` 64 | 65 | Документация - в страничката на [съответния модул](https://nodejs.org/api/fs.html). 66 | 67 | --- 68 | 69 | # Външни модули 70 | 71 | Инсталираме с `npm install <име>` в основната папка на нашето приложение: 72 | 73 | ```bash 74 | # в терминала 75 | npm install uuid 76 | ``` 77 | 78 | ```javascript 79 | // в някой сорс файл 80 | 81 | var uuid = require('uuid'); 82 | console.log(uuid.v1()); 83 | ``` 84 | 85 | Документация - в страничка на модула в NPM repository-то, в случая [uuid](https://www.npmjs.com/package/uuid). 86 | 87 | --- 88 | 89 | # Пътища за зареждане 90 | 91 | Забележете require-ването само по име на модула - Node търси за файл/папка с това име в подпапка **node_modules** на нашата основна папка. Ако не намери, продължава да търси **node_modules** нагоре по дървото с папки. 92 | 93 | ```bash 94 | /home/modder/some_project/node_modules/uuid 95 | /home/modder/node_modules/uuid 96 | /home/node_modules/uuid 97 | /node_modules/uuid 98 | ``` 99 | 100 | Повече информация за начина, по който Node търси модули - [тук](https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders). 101 | -------------------------------------------------------------------------------- /lectures/05-01-es2015.markdown: -------------------------------------------------------------------------------- 1 | # ES2015 2 | 3 | ### 2015/2016 4 | 5 | --- 6 | # ES2015 7 | 8 | Последната финализирана версия на ECMAScript. Преди няколко месеца беше известен като ES6, но поради желанието на TC39 да прави годишни итерации с цел разширяване на езика, бе преименуван на ES2015. 9 | 10 | --- 11 | 12 | # ES2015 включва неща като... 13 | 14 | - Block lexical scope 15 | - Classes 16 | - Promises 17 | - Generators 18 | - Iterators 19 | - Enhanced object literals 20 | - Destructuring 21 | - Arrow functions 22 | 23 | --- 24 | 25 | # Преди да се забавляваме с ES2015... 26 | ## нека обърнем внимание на ES5... 27 | 28 | --- 29 | 30 | # Property descriptors 31 | 32 | --- 33 | 34 | ```javascript 35 | Object.defineProperty(obj, 'propName', descriptor); 36 | ``` 37 | 38 | --- 39 | 40 | ## Стойности по подразбиране 41 | 42 | ```javascript 43 | var descriptor = { 44 | configurable: false, 45 | enumerable: false, 46 | value: undefined, 47 | writable: false, 48 | get: undefined, 49 | set: undefined 50 | }; 51 | ``` 52 | 53 | --- 54 | 55 | ## Семантика 56 | 57 | - `configurable` - при стойност `true` даденото поле на обекта може да бъде изтрито и/или преконфигурирано (т.е. можем да променим дескриптора) 58 | - `enumerable` - при стойност `true` даденото поле може да бъде енумерирано (например, обходено с `for...in` или получено през `Object.keys`) 59 | - `value` - стойност на полето 60 | - `writable` - при `true` стойността на полете може да бъде променяна с оператор `=` 61 | - `get` - връща стойността на полето (не може да живее заедно с `value`) 62 | - `set` - задава стойност на полето (не може да живее заедно с `value`) 63 | 64 | --- 65 | 66 | # Immutable обекти 67 | 68 | Immutable е обект, който не може да бъде променян след създаването си. 69 | 70 | --- 71 | 72 | ## Защо immutable data rulez? 73 | 74 | - Конкурентно програмиране (няма проблеми с конкурентно модифициране на данни) 75 | - Няма промяна в състоянието (не могат да възникнат бъгове от неконсистентно състояние) 76 | 77 | --- 78 | 79 | # `Object.freeze` & `Object.seal` 80 | 81 | --- 82 | 83 | ## `Object.seal` 84 | 85 | Забранява добавянето на нови полета към даден обект. Прави всички налични полета на обекта `non-configurable` 86 | 87 | ```javascript 88 | var foo = { bar: 42 }; 89 | Object.seal(foo); 90 | 91 | foo.bar = 43; 92 | console.log(foo.bar); // 43 93 | 94 | foo.baz = 1.618; 95 | console.log(foo.baz); // undefined 96 | ``` 97 | --- 98 | 99 | ## `Object.freeze` 100 | 101 | Забранява добавянето и изтриването на съществуващи полета на даден обект. Прави полетата на обекта `non-writable` & `non-configurable`. 102 | 103 | ```javascript 104 | var foo = { bar: 42 }; 105 | Object.freeze(foo); 106 | 107 | foo.bar = 43; 108 | console.log(foo.bar); // 42 109 | 110 | foo.baz = 1.618; 111 | console.log(foo.baz); // undefined 112 | ``` 113 | --- 114 | 115 | ## С други думи: 116 | 117 | ```javascript 118 | var foo = { bar : 42 }; 119 | Object.getOwnPropertyDescriptor(foo, 'bar'); 120 | // Object { value: 42, writable: true, enumerable: true, configurable: true } 121 | 122 | Object.freeze(foo); 123 | // Object { value: 42, writable: false, enumerable: true, configurable: false } 124 | ``` 125 | 126 | --- 127 | 128 | ## Какво ще се случи: 129 | 130 | ```javascript 131 | var foo = { bar: { baz: 42 } }; 132 | 133 | Object.freeze(foo); 134 | foo.bar = { foobar: 12 }; 135 | foo.bar.baz = 1.618; 136 | 137 | console.log(foo.bar); 138 | console.log(foo.bar.baz); 139 | ``` 140 | --- 141 | 142 | # Strict mode 143 | 144 | ```javascript 145 | 'use strict'; 146 | 147 | // Now you're in strict mode... 148 | ``` 149 | 150 | --- 151 | 152 | ## Какво означава да си в strict mode... 153 | 154 | Имаме редица ограничения, които са създадении с цел: 155 | 156 | - Избягване на често допускани грешки 157 | - Оптимизация на кода, който пишем от виртуалната машина 158 | - По-лесна миграция към нови стандарти 159 | 160 | --- 161 | 162 | Забранено е дефинирането на променливи без ключова дума, която експлицитно задава това (`var`, `let`): 163 | 164 | ```javascript 165 | 'use strict'; 166 | 167 | foo = 42; 168 | ``` 169 | --- 170 | 171 | ## Strict mode 172 | ### не ви позволява да нарушавате предварително зададени правила... 173 | 174 | ```javascript 175 | 'use strict'; 176 | var foo = { bar: 42 }; 177 | Object.freeze(foo); 178 | foo.bar = 43; // thorws 179 | ``` 180 | --- 181 | 182 | ## Аргументи с еднакво име... 183 | 184 | ```javascript 185 | function foo(a, a, b) { 186 | return a + b; 187 | } 188 | // Argument name clash in strict mode 189 | 190 | ``` 191 | --- 192 | ## Какъв мислите, че ще бъде резултатът от: 193 | 194 | ```javascript 195 | function f(a) { 196 | a = 1.618; 197 | return [a, arguments[0]]; 198 | } 199 | console.log(f(42)); 200 | ``` 201 | --- 202 | ## Strict mode оправя това... 203 | 204 | ```javascript 205 | function f(a) { 206 | a = 1.618; 207 | return [a, arguments[0]]; 208 | } 209 | console.log(f(42)); // [1.168, 42] 210 | ``` 211 | --- 212 | ## Без use strict... 213 | 214 | ```javascript 215 | function a() { 216 | return this; 217 | } 218 | console.log(this); // ? 219 | ``` 220 | --- 221 | ## С 'use strict'... 222 | 223 | ```javascript 224 | function a() { 225 | 'use strict'; 226 | return this; 227 | } 228 | console.log(this); // undefined 229 | ``` 230 | --- 231 | 232 | ## Без повече `fn.caller` & `fn.arguments` 233 | 234 | ```javascript 235 | function foo() { 236 | console.log(foo.arguments); // [] 237 | console.log(foo.caller); // undefined 238 | } 239 | 240 | foo(); 241 | ``` 242 | 243 | ###...но с `use strict`... 244 | 245 | ```javascript 246 | function foo() { 247 | console.log(foo.arguments); 248 | console.log(foo.caller); 249 | } 250 | 251 | foo(); 252 | // 'caller' and 'arguments' are restricted function properties 253 | ``` 254 | --- 255 | 256 | # ES2015! 257 | 258 | --- 259 | 260 | # TCO (tail call optimization) 261 | 262 | --- 263 | 264 | ## Опашъчна рекурсия 265 | 266 | ![](../html/img/tailfact.png) 267 | 268 | ### Как да я оптимизираме? 269 | 270 | --- 271 | 272 | ## Оптимизацията е валидна за всички tail calls: 273 | 274 | ```javascript 275 | function id(a) { 276 | return a; 277 | } 278 | function sum(a, b) { 279 | return id(a + b); 280 | } 281 | function addOne(a) { 282 | return sum(a, 1); 283 | } 284 | 285 | addOne(41) // 42 286 | ``` 287 | 288 | ### Call stack 289 | 290 | ``` 291 | +---------+ 292 | | id | 293 | +---------+ 294 | | sum | 295 | +---------+ 296 | | addOne | 297 | +---------+ 298 | ``` 299 | --- 300 | 301 | ## Защо `use strict` забранява `fn.caller`? 302 | 303 | --- 304 | 305 | # Block lexical scope 306 | 307 | --- 308 | 309 | ## Какво знаем... 310 | 311 | ```javascript 312 | if (true) { 313 | var answer = 42; 314 | if (answer === 42) { 315 | console.log("You got the answer: " + answer); 316 | } 317 | } 318 | if (true) { 319 | console.log(answer); // 42 320 | } 321 | ``` 322 | --- 323 | 324 | # Block lexical scope 325 | 326 | ```javascript 327 | if (true) { 328 | let answer = 42; 329 | if (answer === 42) { 330 | console.log("You got the answer: " + answer); 331 | } 332 | } 333 | if (true) { 334 | console.log(answer); // dead kittie 335 | } 336 | ``` 337 | --- 338 | 339 | # Enhanced object literals 340 | 341 | --- 342 | 343 | ## Как дефинирахме object literals в миналото... 344 | 345 | ```javascript 346 | var bar = 42; 347 | var talk = function (words) { ... }; 348 | var foo = { 349 | bar: bar, 350 | talk: talk, 351 | walk: function (distance) { 352 | // do stuff 353 | } 354 | }; 355 | ``` 356 | 357 | ## Как го правим сега? 358 | 359 | ```javascript 360 | var bar = 42; 361 | var talk = function (words) { ... }; 362 | var foo = { 363 | bar, 364 | talk, 365 | walk(distance) { 366 | // do stuff 367 | } 368 | }; 369 | ``` 370 | --- 371 | 372 | ## Изчисляеми полета 373 | 374 | ```javascript 375 | var obj = { 376 | [\`guessWhatI'llBe${Math.random()}\`]: 42 377 | }; 378 | ``` 379 | 380 | --- 381 | 382 | # Destructuring 383 | 384 | --- 385 | 386 | ## Как извличахме полетата на обект преди? 387 | 388 | ```javascript 389 | function foobar(config) { 390 | var name = config.name; 391 | var age = config.age; 392 | var gitHubHandle = config.socialMedia.gitHubHandle; 393 | // ...do stuff 394 | } 395 | ``` 396 | 397 | --- 398 | 399 | ## Как го правим сега... 400 | 401 | ```javascript 402 | function foobar(config) { 403 | var { name, age, socialmedia: { githubHandle } } = config; 404 | // ...do stuff 405 | } 406 | ``` 407 | ## или... 408 | 409 | ```javascript 410 | function foobar({ name, age, socialmedia: { githubHandle } }) { 411 | // ...do stuff 412 | } 413 | ``` 414 | --- 415 | 416 | # Arrow functions 417 | 418 | --- 419 | 420 | ## Функции от по-висок ред (super quick recap) 421 | 422 | ```javascript 423 | var sum = [1, 2, 3].reduce(function (a, c) { 424 | return a + c; 425 | }, 0); 426 | 427 | var sorted = obj.sort(function (a, b) { 428 | var ageDif = a.age - b.age; 429 | if (ageDif !== 0) { 430 | return ageDif; 431 | } else { 432 | return a.height - b.height; 433 | } 434 | }); 435 | ``` 436 | --- 437 | 438 | ## High-order arrow functions 439 | 440 | ```javascript 441 | var sum = [1, 2, 3].reduce((a, c) => a + c, 0); 442 | var sorted = obj.sort((a, b) => { 443 | let ageDif = a.age - b.age; 444 | if (ageDif !== 0) { 445 | return ageDif; 446 | } else { 447 | return a.height - b.height; 448 | } 449 | }); 450 | ``` 451 | --- 452 | 453 | ## Запазват `this` на заобикалящия ги код! 454 | 455 | ```javascript 456 | function MyHero() { 457 | var self = this; 458 | setTimeout(function () { 459 | this.startSuperPowers(); 460 | }, 1000); 461 | } 462 | new MyHero(); 463 | ``` 464 | 465 | ```javascript 466 | function MyHero() { 467 | var self = this; 468 | setTimeout(() => { 469 | this.startSuperPowers(); 470 | }, 1000); 471 | } 472 | new MyHero(); 473 | ``` 474 | --- 475 | # Класове 476 | --- 477 | 478 | ## Защо са ни нужни? 479 | ### ...много хора ги мразят... 480 | 481 | - Оптимизации от виртуалната машина 482 | - По-кратък learning curve 483 | - Повечето популярни езици за програмиране ги поддържат 484 | - Добре известна семантика 485 | - Те са просто syntax sugar 486 | 487 | --- 488 | ## Синтаксис: 489 | 490 | ```javascript 491 | class Person { 492 | static totalPeople = 0; 493 | constructor(name) { 494 | Person.totalPeople += 1; 495 | this._name = name; 496 | } 497 | set name(name) { 498 | this._name = name; 499 | } 500 | get name() { 501 | return this._name; 502 | } 503 | } 504 | ``` 505 | --- 506 | ## Транслира се до нещо от рода на: 507 | 508 | ```javascript 509 | var Person = (function () { 510 | _createClass(Person, null, [{ 511 | key: "totalPeople", 512 | value: 0, 513 | enumerable: true 514 | }]); 515 | function Person(name) { 516 | _classCallCheck(this, Person); 517 | Person.totalPeople += 1; 518 | this._name = name; 519 | } 520 | _createClass(Person, [{ 521 | key: "name", 522 | set: function set(name) { 523 | this._name = name; 524 | }, 525 | get: function get() { 526 | return this._name; 527 | } 528 | }]); 529 | return Person; 530 | })(); 531 | ``` 532 | --- 533 | ## Следните особености: 534 | 535 | - Не съществува преди изпълнение на кода за неговата дефиниция 536 | - Има само 1 конструктор (защо?) 537 | - Всички методи са добавени към прототипа и са `nonenumerable` 538 | - `set` и `get` се използват за създаване на setters и getters 539 | - Няма модификатори за видимост (към момента) 540 | - Всички статични полета се добавят като полета на конструкторната функция 541 | --- 542 | 543 | ## Extends... 544 | 545 | ```javascript 546 | class Person { ... } 547 | 548 | class Developer extends Person { 549 | constructor(name, langs) { 550 | super(name); 551 | this.languages = langs; 552 | } 553 | talk() { 554 | return super.talk() + \` and knows ${this.languages.join(', ')}\`; 555 | } 556 | // ... 557 | } 558 | ``` 559 | --- 560 | # Promises 561 | --- 562 | 563 | ## Какво е callback hell? 564 | 565 | ![](./img/callback-hell.png) 566 | 567 | --- 568 | 569 | ## Задача... 570 | 571 | Искаме да заредим всички потребители, които са зададени в JSON файл. 572 | 573 | Във файла имаме списък с JSON файлове, които представляват отделните потребители. 574 | 575 | Имаме дефинирана функция `getJSON(file, cb)`. `cb` ще бъде изпълнен с резултатът от заявката, когато тя приключи. 576 | 577 | --- 578 | 579 | ## Решение с callbacks 580 | 581 | ```javascript 582 | function loadUsers() { 583 | getJSON('users.json', function (users) { 584 | console.log(users.group); 585 | users.users_list.forEach(function (u) { 586 | getJSON(u, function (user) { 587 | console.log(user); 588 | }, function (error) { 589 | console.error(error); 590 | }); 591 | }); 592 | }, function (error) { 593 | console.error(error); 594 | }); 595 | } 596 | ``` 597 | --- 598 | ## Решение с promises 599 | 600 | Вместо функцията да приема callback като аргумент може да връща обект, които да има метод, който приема функция и да връща обект от същия тип. Тази функция ще бъде извикана веднъж, когато асинхронното действие бъде приключено. 601 | 602 | --- 603 | 604 | ## Promises 605 | 606 | ```javascript 607 | function loadUsers() { 608 | getJSON('users.json') 609 | .then(function (data) { 610 | console.log(data.group); 611 | Promise.all(data.users_list.map(getJSON)) 612 | .then(function (users) { 613 | console.log(users); 614 | }); 615 | }); 616 | } 617 | ``` 618 | --- 619 | ## Нека го разгледаме стъпка по стъпка... 620 | 621 | ### Стъпка 1 622 | 623 | ```javascript 624 | function loadUsers() { 625 | getJSON('users.json') 626 | .then(function (data) { 627 | // ... 628 | // Ще бъде извикана, когато имаме 629 | // получен отговор от заявката: GET users.json 630 | // ... 631 | }); 632 | } 633 | ``` 634 | 635 | --- 636 | 637 | ### Стъпка 2 638 | 639 | ```javascript 640 | // ... 641 | .then(function (data) { 642 | // Ще извика функцията getJSON с всеки един от файловете 643 | // които сме получили като резултат от заявката до 644 | // users.json, намиращи се в users_list 645 | Promise.all(data.users_list.map(getJSON)) 646 | }); 647 | // ... 648 | ``` 649 | --- 650 | 651 | ### Стъпка 3 652 | 653 | ```javascript 654 | // На Promise.all ще подадем масив от обекти от тип Promise 655 | // т.е. ще имат then метод, към който можем да 656 | // закачим callback. 657 | Promise.all(data.users_list.map(getJSON)) 658 | ``` 659 | --- 660 | 661 | ### Стъпка 4 662 | ```javascript 663 | // Promise.all ще върне нов обект от тип Promise 664 | // чийто callback ще бъде извикан, веднъж когато 665 | // всички Promise обекти от масива са получили стойност. 666 | Promise.all(data.users_list.map(getJSON)) 667 | .then(function (users) { 668 | // масив с всички резултати, с които 669 | // callback на promises от масива върнат 670 | // от map са извикани 671 | }); 672 | ``` 673 | --- 674 | 675 | ## Promises не са нова идея... 676 | 677 | > The future and/or promise constructs were first implemented in programming languages such as MultiLisp and Act 1. 678 | 679 | ### Multilisp 680 | 681 | > It was designed by Robert H. Halstead in the early 1980s 682 | --- 683 | 684 | ## Chaining promises 685 | 686 | ```javascript 687 | asyncFunction() 688 | // ще бъде извикана веднъж, когато 689 | // изпълнението на asyncFunction е довело до резултат 690 | .then(result => { 691 | return anotherAsyncFunction(result); 692 | }) 693 | // Ще бъде извикана веднъж, когато promise 694 | // върнат от anotherAsyncFunction има стойност 695 | // (т.е. когато изпълнението на асинхронната функция 696 | // anotherAsyncFunction е довело до резултат) 697 | .then(anotherResult => { 698 | return thirdAsyncFunction(anotherResult); 699 | }) 700 | // ... 701 | .then(result => { 702 | // final result 703 | }); 704 | ``` 705 | --- 706 | 707 | # Генератори 708 | 709 | --- 710 | 711 | ## Генераторът е функция, която връща множество резултати 712 | 713 | ```javascript 714 | function *countTo(n) { 715 | for (let i = 0; i < n; i += 1) { 716 | yield i; 717 | } 718 | } 719 | 720 | let generator = countTo(3); 721 | generator.next(); // { done: false, value: 0 } 722 | generator.next(); // { done: false, value: 1 } 723 | generator.next(); // { done: false, value: 2 } 724 | generator.next(); // { done: true, value: undefined } 725 | ``` 726 | 727 | --- 728 | 729 | ## Подаване на стойност на генератора: 730 | 731 | ```javascript 732 | function *countTo(n) { 733 | for (let i = 0; i < n; i += 1) { 734 | let isEven = yield i; 735 | console.log(i, (isEven) ? 'is' : 'isn\'t', 'even.'); 736 | } 737 | } 738 | 739 | let generator = countTo(3); 740 | let res = generator.next(); 741 | res = generator.next(!(res.value % 2)); 742 | res = generator.next(!(res.value % 2)); 743 | res = generator.next(!(res.value % 2)); 744 | 745 | ``` 746 | --- 747 | 748 | ## По време на следващото упражнение ще си поиграе(те|м) с Promises & Generators 749 | 750 | --- 751 | 752 | # Итератори 753 | 754 | --- 755 | 756 | ## Дефиниция на итератор 757 | 758 | ```javascript 759 | var foo = {}; 760 | foo[Symbol.iterator] = function * () { 761 | for (var i = 0; i < 5; i += 1) { 762 | yield i; 763 | } 764 | }; 765 | ``` 766 | 767 | ...и можем да го обходим с: 768 | 769 | ```javascript 770 | for (var val of foo) { 771 | console.log(val); 772 | } 773 | ``` 774 | --- 775 | 776 | ## Ресурси 777 | 778 | - [Immutable.js](https://facebook.github.io/immutable-js/) 779 | - [A generator-based sequence generator and utility.](https://github.com/nmn/Grunge) 780 | - [Минимална имплементация на promises](https://gist.github.com/mgechev/afbf6a5802b8fa55d79c) 781 | - [IterJS](https://github.com/abozhilov/IterJS) 782 | -------------------------------------------------------------------------------- /lectures/05-02-promises-and-generators-short.markdown: -------------------------------------------------------------------------------- 1 | ### Continuation Passing 2 | 3 | ```javascript 4 | fs.readFile(inputFile, function(err, data) { 5 | if (err) return res.status(500).send(err); 6 | process1(data, function(err, data) { 7 | if (err) return res.status(500).send(err); 8 | process2(data, function(err, data) { 9 | if (err) return res.status(500).send(err); 10 | process3(data, function(err, data) { 11 | if (err) return res.status(500).send(err); 12 | fs.writeFile(outputFile, data, function(err) { 13 | if (err) return res.status(500).send(err); 14 | res.status(200).send('processed successfully using callback hell'); 15 | }); 16 | }); 17 | }); 18 | }); 19 | }); 20 | ``` 21 | --- 22 | ###Named Continuation Passing 23 | 24 | ```javascript 25 | fs.readFile(inputFile, onReadFile); 26 | 27 | function onReadFile(err, data) { 28 | if (err) return res.status(500).send(err) 29 | process1(data, onProcess1) 30 | } 31 | 32 | function onProcess1(err, data) { 33 | if (err) return res.status(500).send(err) 34 | process2(data, onProcess2) 35 | } 36 | 37 | function onProcess2(err, data) { 38 | if (err) return res.status(500).send(err) 39 | process3(data, onProcess3) 40 | } 41 | 42 | function onProcess3(err, data) { 43 | if (err) return res.status(500).send(err) 44 | fs.writeFile(outputFile, data, onWriteFile) 45 | } 46 | 47 | function onWriteFile(err) { 48 | if (err) return res.status(500).send(err) 49 | res.status(200).send('processed successfully using callback hell') 50 | } 51 | ``` 52 | --- 53 | 54 | #Promises ES2015 (ES6) 55 | 56 | --- 57 | ###Какво са promises? 58 | Promise е обект който се използва за отлагане. 59 | 60 | Той представлява операция, която все още не е завършила, но се очаква да завърши. 61 | 62 | ```javascript 63 | new Promise(executor); 64 | new Promise(function(resolve, reject) { ... }); 65 | ``` 66 | 67 | Този конструктор взима като аргумент функция с два аргумента: resolve, reject. 68 | 69 | Тези два аргумента са функции като едната се използва когато искаме да си изпълним обешанието, а другата когато искаме да го отхвърлим. 70 | 71 | Като използваме обещания постигаме синхронност на асинхронните операции. 72 | 73 | --- 74 | 75 | ###States 76 | 77 | Всяко обещание има 3 състояния: 78 | 79 | - pending (initial state) 80 | - fulfilled 81 | - rejected 82 | 83 | --- 84 | ###Example 85 | 86 | ```javascript 87 | function httpGet(url) { 88 | return new Promise( 89 | function (resolve, reject) { 90 | var request = new XMLHttpRequest(); 91 | request.onreadystatechange = function () { 92 | if (this.status === 200) { 93 | // Success 94 | resolve(this.response); 95 | } else { 96 | // Something went wrong (404 etc.) 97 | reject(new Error(this.statusText)); 98 | } 99 | } 100 | request.onerror = function () { 101 | reject(new Error( 102 | 'XMLHttpRequest Error: '+this.statusText)); 103 | }; 104 | request.open('GET', url); 105 | request.send(); 106 | }); 107 | } 108 | 109 | ``` 110 | 111 | --- 112 | 113 | ###Example 114 | ```javascript 115 | httpGet('http://example.com/file.txt') 116 | .then( 117 | function (value) { 118 | console.log('Contents: ' + value); 119 | }, 120 | function (reason) { 121 | console.error('Something went wrong', reason); 122 | }); 123 | ``` 124 | --- 125 | ###Chaining (using then) 126 | 127 | ```javascript 128 | 129 | asyncFunc() 130 | .then(function (value1) { 131 | return 123; 132 | }) 133 | .then(function (value2) { 134 | console.log(value2); // 123 135 | }); 136 | ``` 137 | --- 138 | 139 | ### Error handling 140 | 141 | ```javascript 142 | 143 | asyncFunc1() 144 | .then(asyncFunc2) 145 | .then(asyncFunc3) 146 | .catch(function (reason) { 147 | // Something went wrong above 148 | }); 149 | ``` 150 | --- 151 | ###Composition (map via Promise.all) 152 | 153 | ```javascript 154 | var fileUrls = [ 155 | 'http://example.com/file1.txt', 156 | 'http://example.com/file2.txt' 157 | ]; 158 | var promisedTexts = fileUrls.map(httpGet); 159 | 160 | Promise.all(promisedTexts) 161 | .then(function (texts) { 162 | texts.forEach(function (text) { 163 | console.log(text); 164 | }); 165 | }) 166 | .catch(function (reason) { 167 | // Receives first rejection among the promises 168 | }); 169 | ``` 170 | --- 171 | ###Workflow 172 | 173 | --- 174 | 175 | #Итериране на обекти 176 | 177 | В ES5 нямаме много възможности да използваме някакви операции върху обекти и за това използвахме 178 | библиотеки като lodash и underscore. 179 | 180 | За радост в ES2015 това не е така. Вече имаме Iterators. 181 | 182 | --- 183 | 184 | #Какво е Iterator? 185 | 186 | --- 187 | 188 | Iterator е design pattern, който ни дава начин да достъпваме последователно елементи на някакъв контейнер (обект). 189 | 190 | Това значи, че можем да използваме един и същ интерфейс за итериране независимо от репрезентацията на обекта. 191 |

192 |

193 |

194 |

195 | В езици като C#, Java за да можем за инерираме по обект трябва да наследяваме някакъв интерфейс или да extend-нем някакъв клас, но в JS не е така. 196 | 197 | В JS за да бъдеш итератор трябва да изглеждаш като такъв! Това означава, че можем да използваме итератори 198 | върху всеки един обект. 199 | 200 | --- 201 | ###Какво трябва да направим за да можем да итерираме по даден обект? 202 | 203 | За да можем да итерираме по обект, той или някой от обектите от прототипната му верига трябва да има свойството Symbol.iterator 204 | 205 | [Symbol.iterator] е функция без аргументи, която връща обект, който има имплементиран метод - next 206 | 207 | next трябва да е функция, която не взима аргументи и връща обект с две свойства: done(boolean) и value 208 | 209 |

210 | Обекти които имат [Symbol.iterator] 211 | 212 | 213 | --- 214 | ###Пример 215 | 216 | ```javascript 217 | var someString = "hi"; 218 | 219 | var iterator = someString[Symbol.iterator](); 220 | 221 | iterator.next(); // { value: "h", done: false } 222 | iterator.next(); // { value: "i", done: false } 223 | iterator.next(); // { value: undefined, done: true } 224 | 225 | var arr = ['a', 'b', 'c']; 226 | iterator = arr[Symbol.iterator](); 227 | iter.next() //{ value: 'a', done: false } 228 | iter.next() //{ value: 'b', done: false } 229 | iter.next() //{ value: 'c', done: false } 230 | iter.next() //{ value: undefined, done: true } 231 | 232 | ``` 233 | --- 234 | ###Как да си пишем custom iterables 235 | 236 | ```javascript 237 | let iterable = { 238 | [Symbol.iterator]() { 239 | let step = 0; 240 | let iterator = { 241 | next() { 242 | if (step <= 2) { 243 | step++; 244 | } 245 | switch (step) { 246 | case 1: 247 | return { value: 'hello', done: false }; 248 | case 2: 249 | return { value: 'world', done: false }; 250 | default: 251 | return { value: undefined, done: true }; 252 | } 253 | } 254 | }; 255 | return iterator; 256 | } 257 | }; 258 | 259 | for (let x of iterable) { 260 | console.log(x); 261 | } 262 | // Output: 263 | // hello 264 | // world 265 | ``` 266 | --- 267 | 268 | for ... of е от ES2015 и работи с Iterator-и 269 | ```javascript 270 | for (variable of object) { 271 | statement 272 | } 273 | ``` 274 | 275 | Някои вътрешни конструкции като spread оператора използват итератори 276 | ```javascript 277 | [...someString] // ["h", "i"] 278 | ``` 279 | --- 280 | 281 | #Generators 282 | 283 | --- 284 | 285 | ###Какво са генераторите 286 | 287 | Генераторите са функциии с множество крайни точки. 288 | 289 | Чрез ключовата дума yield ние можем да замразим изпълнението на нашата генератор функция след което можем да продължим изпълнението и. 290 | 291 | ```javascript 292 | function* fibonacci() { 293 | var a = 0, b = 1, c = 0; 294 | 295 | while(true){ 296 | yield a; //<-- suspend the generator function 297 | c = a, a = b, b = c + b; 298 | } 299 | } 300 | 301 | 302 | var seq = fibonacci(); //<--- call the generator function and to get an iterator object 303 | 304 | //След извикване на next получаваме обект { value: 0, done: false }; 305 | console.log(seq.next().value); // 0 306 | console.log(seq.next().value); // 1 307 | console.log(seq.next().value); // 1 308 | console.log(seq.next().value); // 2 309 | console.log(seq.next().value); // 3 310 | console.log(seq.next().value); // 5 311 | ``` 312 | --- 313 |


314 |


315 |


316 |

Това ли е всичко? ... Ко? Не!

317 | 318 | --- 319 | 320 | ###Можем да пращаме стойности на генеранорите си. 321 | ```javascript 322 | function* powerGenerator() { 323 | var result = Math.pow(yield 'a', yield 'b'); 324 | return result; 325 | } 326 | 327 | var g = powerGenerator(); 328 | console.log(g.next().value); 329 | console.log(g.next(10).value); 330 | console.log(g.next(2).value); // <--- what's the FINAL result? 331 | ``` 332 | Ако извикаме още един път next получаваме обект 333 | 334 | { value: undefined, done: true }; 335 | --- 336 |


337 |


338 |


339 |

Можем ли да използваме генератори за работа с асинхронните си функции?

340 | --- 341 | #Example 342 | ```javascript 343 | sync(function* (resume){ 344 | try{ 345 | var profile = yield _get('/profile', resume); 346 | console.log('Hello ' + profile.name); 347 | }catch(err){ 348 | console.log(err); 349 | } 350 | }); 351 | 352 | function sync(gen){ //<-- our sync function takes a generator 353 | var iteratorObj = null; 354 | 355 | function resume(err, result){ 356 | if(err) return iteratorObj.throw(err); 357 | iteratorObj.next(result); 358 | } 359 | 360 | iteratorObj = gen(resume); 361 | iteratorObj.next(); 362 | } 363 | 364 | ``` 365 | --- 366 | # Може и още по-добре ... 367 | ```javascript 368 | 369 | function _get(val, callback) { 370 | setTimeout(function(){ 371 | callback(null, val); 372 | }, val); 373 | } 374 | 375 | function _post(url, data, callback) { 376 | setTimeout(function(){ 377 | callback(null, url + ': data received'); 378 | }, 1000); 379 | } 380 | 381 | sync(function* (resume) { 382 | try{ 383 | var responses = yield [_get(2000, resume()), _get(300, resume())]; 384 | console.log(responses); 385 | var resp = yield _post('/myData', responses, resume()); 386 | console.log(resp); 387 | } 388 | catch(err){ 389 | console.log(err); 390 | } 391 | }); 392 | ``` 393 | --- 394 | ```javascript 395 | function sync(gen) { 396 | var returnedValues = [], operationCounter = 0, returnedValuesCounter = 0; 397 | 398 | var reset = function(){ 399 | returnedValues = []; 400 | operationCounter = 0; 401 | returnedValuesCounter = 0; 402 | }; 403 | 404 | var check = function() { 405 | if (returnedValuesCounter == operationCounter) { 406 | var result = null; 407 | if (operationCounter == 1) result = returnedValues[0]; 408 | else result = returnedValues; 409 | reset(); 410 | iterable.next(result); 411 | } 412 | }; 413 | 414 | var resume = function() { 415 | var slot = operationCounter++; 416 | 417 | return function(err, returnedValue) { 418 | if (err) iterable.throw(err); 419 | returnedValuesCounter++; 420 | returnedValues[slot] = returnedValue; 421 | check(); 422 | }; 423 | }; 424 | 425 | var iterable = gen(resume); 426 | iterable.next(); 427 | } 428 | ``` 429 | --- 430 | 431 | class: middle, center 432 | [Още за генератори](https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/) -------------------------------------------------------------------------------- /lectures/06-node-js.markdown: -------------------------------------------------------------------------------- 1 | class: middle, center 2 | 3 | # Какво е V8? 4 | 5 | --- 6 | 7 | class: middle, center 8 | ### V8 is a Javascript engine designed by Google 9 | 10 | Преди v8 повечето JS engine-и бяха просто интерпретатори и не бяха бързи. 11 | 12 | --- 13 | class: middle, center 14 | 15 | #Защо е проблем да се направи бърз JS enigine? 16 | 17 | --- 18 | 19 | ### JS е много динамичен език 20 | 21 | * Обектите са хеш таблици и няма нищо статично по тях. 22 | 23 | * Можем да добавяме и махаме свойства към обектите когато си искаме. 24 | 25 | * Имаме Prototype Chains, които можем да модифицираме в run time. 26 | 27 | * Можем да променяме контекста (call, apply, bind). 28 | 29 | --- 30 | ### Когато говорим за големи уеб приложения са ни необходими 31 | 32 | * Бързо да достъпваме свойствата на обектите. 33 | 34 | * Бързи function calls. 35 | 36 | * И мн добър memory management. 37 | 38 | --- 39 | ### Тези 3 неща се решават от V8 чрез: 40 | 41 | * Hidden classes. 42 | 43 | * Inline caching or polymorphic inline cache (used with the JIT compiler) 44 | 45 | С тези две неща Google постигат бързо достъпване на свойства на обектите и бързи function calls 46 | 47 | * Generational Garbage Collector (memory pools holding objects of different ages) 48 | 49 | С това се постига добър memory management 50 | --- 51 | ### Hidden classes 52 | 53 | 54 | --- 55 | ### Какво ще стане тук? 56 | ```javascript 57 | var p1 = { 58 | x: 2, 59 | y: 5 60 | }; 61 | 62 | var p2 = { 63 | x: 2, 64 | y: 5 65 | }; 66 | 67 | var p3 = { 68 | y: 5, 69 | x: 7 70 | } 71 | ``` 72 | --- 73 | ### Arrays 74 | * Скритите класове на масивите следят типовете на елементите 75 | * Ако един масив е пълен с doubles той се upgrade-ва (unboxed) към fast double 76 | * Невнимателно манипулиране на масивите може да причини оптимизационни проблеми (unboxing - boxing) 77 | 78 | ```javascript 79 | var a = new Array(); 80 | a[0] = 77; // Allocates 81 | a[1] = 88; 82 | a[2] = 0.5; // Allocates, converts 83 | a[3] = true; // Allocates, converts 84 | 85 | var a = [77, 88, 0.5, true]; //Is better 86 | ``` 87 | --- 88 | ### Arrays 89 | 90 | * Не пре-алокирайте големи масиви (>=100K елементи). 91 | 92 | * Ако знаем колко елемента ще се съдържат в масива е хубаво да пре-алокираме. 93 | 94 | * Не е хубаво да трием елементи. (Отнася се и за обекти). 95 | 96 | * За масиви с различни типове трябва да използваме Array literal. 97 | --- 98 | class: middle, center 99 | 100 | ##### Обектните литерали, които имат една и съща структура също използват едни и същи скрити класове. 101 | 102 | * За това е хубаво когато правим нови обекти да слагаме в тях всички property-та, които те ще съдържат. 103 | --- 104 | ###Какво прави v8? 105 | 106 | * v8 компилира Javascript директно до машинни инструкции (unoptimized native code) преди изпълнението му. 107 | (Няма никакво интерпретиране и bytecode) 108 | 109 | * Функциите не се компилират докато не бъдат извикани, което е хубаво защото виртуалната машина не губи време за големи библиотеки 110 | 111 | * След компилирането се пуска profiler, който избира "hot" функции, които да бъдат оптимизирани от Crankshaft (optimization compiler) 112 | 113 | --- 114 | class: middle, centeri 115 | ###Какво е NodeJS? 116 | --- 117 | class: middle, center 118 | ####Open-source, cross-platform runtime environment for developing server-side web applications. 119 | 120 | 121 | 122 | * libuv - high performance, cross-platform evented I/O library 123 | 124 | * js/c++ - Node екосистемата npm (node package manager) е най-голямата open-source екосистема от библиотеки в света. 125 | 126 | --- 127 | ###Малко история 128 | * Node e създаден 2009 от Ryan Dahl и други програмисти от Joyent. 129 | 130 | * 2011 година излиза npm 131 | 132 | * 2012 Ryan Dahl се оттегля от проекта 133 | 134 | * 2014 поради вътрешен конфликт излиза форк на node с името io.js 135 | 136 | * 2015 node and io.js merge 137 | 138 | --- 139 | ###Runtime 140 | 141 | * v8 event loop се грижи за javascript и C++ модулите, които работят в main thread-а. 142 | 143 | * Ако нещо работи извън main thread-а libev и libuv го обработват в thread pool. libuv прави връзката с main thread-а. 144 | 145 | 146 | --- 147 | ###Конвенции 148 | 149 | ```javascript 150 | 151 | var callback = function(err, data) { 152 | //DO STUFF 153 | }; 154 | 155 | saveData(myData, callback); 156 | 157 | ``` 158 | 159 | * callback функцията е винаги последният аргумент, който се подава на асинхронната функция. 160 | 161 | * Първата стойност, която се подава на callback функцията е винаги error. 162 | --- 163 | ###Modules 164 | 165 | ```javascript 166 | 167 | var fs = require('fs'); //default module 168 | var myModule = require('../app_modules/myModule'); //custom module 169 | 170 | myModule.foo(); // > this is foo! 171 | ``` 172 | Някои други default модули са: 173 | 174 | * http 175 | * crypto 176 | * os 177 | * path 178 | 179 | Тук може да откриете всички: https://nodejs.org/api/ 180 | 181 | --- 182 | ###Как да пишем собствени модули: 183 | 184 | ```javascript 185 | 186 | module.exports = { 187 | foo: function() { 188 | return 'this is foo!'; 189 | }, 190 | bar: function() { 191 | return 'this is bar'; 192 | } 193 | }; 194 | 195 | ``` 196 | 197 | ```javascript 198 | 199 | module.exports.foo = function() { 200 | return 'this is foo!'; 201 | }; 202 | 203 | module.exports.bar = function() { 204 | return 'this is bar!'; 205 | }; 206 | ``` 207 | --- 208 | ###NPM 209 | 210 | * Когато започнем да правим нов проект започваме с npm init за да си създадем package.json, който съдържа полезна информация за проекта ни. 211 | 212 | ```javascript 213 | npm install module_name 214 | ``` 215 | 216 | * При добавяне на нов модул към проекта е хубаво да използвамe npm install module_name --save за да запишем този модул към dependencies в package.json. 217 | 218 | * Свалените модули се запазват в папка node_modules, която трябва да добавям в .gitignore за да не я commit-ваме към source control-а. 219 | 220 | * При сваляне на приложението от github посто трябва да напишем npm install или накратко npm i за да се свалят всички dependency-та от package.json. 221 | 222 | * Понякога искаме модулите да се свалят глобално, за да можем да ги използваме навсякъде. Тогава използваме npm install module_name -g. 223 | 224 | --- 225 | ###Events 226 | 227 | * Събитията ни дават друг начин, по който можем да пишем асинхронни операции. 228 | 229 | ```javascript 230 | //Callback example 231 | getUserData(param1, function(err, results) { 232 | //Do stuff 233 | }); 234 | 235 | 236 | 237 | //Event example 238 | var userData = getUserData(param1); 239 | 240 | //Subscribe to the data event 241 | userData.on('data', function(data) { 242 | //Do stuff 243 | }); 244 | 245 | //Subscribe to the item event 246 | userData.on('item', function(item) { 247 | //Do stuff 248 | }); 249 | ``` 250 | Тук казваме, че всеки път когато възникне евент с име data, трябва да се изпълни тази функция. Ако искаме да се изпълни само веднъж, използваме once. 251 | 252 | --- 253 | ###Custom Event Emitters (1) 254 | 255 | ```javascript 256 | var EventEmitter = require('events').EventEmitter; 257 | 258 | var emitter = new EventEmitter(); 259 | 260 | module.exports.getUserData = function() { 261 | setTimeout(function(){ 262 | //Publish the item event after at least 5sec 263 | emitter.emit('item', { id: 123, name: 'ivan' }); 264 | }, 5000); 265 | return emitter; 266 | }; 267 | ``` 268 | 269 | --- 270 | ###Custom Event Emitters (2) 271 | ```javascript 272 | var EventEmitter = require('events').EventEmitter; 273 | var util = require('util'); 274 | 275 | function CustomEmitter() { 276 | EventEmitter.call(this); 277 | }; 278 | 279 | CustomEmitter.prototype.connect = function() { 280 | var self = this; 281 | setTimeout(function(){ 282 | self.emit('connected', { name: 'ivan', age: 19 }); 283 | }, 5000); 284 | }; 285 | 286 | util.inherits(CustomEmitter, EventEmitter); 287 | module.exports = CustomEmitter; 288 | ``` 289 | ```javascript 290 | var CustomEmitter = require('./CustomEmitter'); 291 | 292 | var cm = new CustomEmitter(); 293 | 294 | cm.on('connected', function(data) { 295 | console.log(data); 296 | }); 297 | 298 | cm.connect(); 299 | ``` 300 | --- 301 | ###Streams 302 | 303 | * Потоците разширяват EventEmitter. 304 | 305 | * Те представляват абстракция за менажиране на data flow. (Network traffic, File I/O ...) 306 | 307 | * Може да правим 3 типа потоци: Readable, Writable, Transform (Readable and Writable) 308 | 309 | * Можем да pipe-ваме Readable потоците към Writable (подобно на unix command pipeing) 310 | --- 311 | ###Readable Streams 312 | 313 | 327 | 328 | --- 329 | ### Writable Streams 330 |
331 | 343 |
344 | --- 345 | ###Example (1) 346 | ```javascript 347 | var request = require('request'); 348 | var arr = []; 349 | 350 | var rs = request('http://www.google.com/'); 351 | rs.on('data', function(chunk) { 352 | arr.push(chunk); 353 | }); 354 | 355 | rs.on('end', function() { 356 | var result = arr.toString(); 357 | result = result.replace(/div/g,'span'); 358 | var newBuffer = new Buffer(result); 359 | 360 | //Send new buffer somewhere ... 361 | 362 | console.log(result); 363 | }); 364 | ``` 365 | --- 366 | ###Example (2) 367 | 368 | ```javascript 369 | var fs = require('fs'); 370 | var file = []; 371 | 372 | var readStream = fs.createReadStream('path/to/file'); 373 | 374 | readStream.on('data', function(chunk) { 375 | file.push(chunk); //chunk is a buffer 376 | }); 377 | 378 | readStream.on('end', function() { 379 | console.log(file.toString()); 380 | }); 381 | 382 | var writeStream = fs.createWriteStream('path/to/otherfile'); 383 | 384 | readStream.pipe(writeStream); 385 | ``` 386 | --- 387 | ### Example (3) 388 | 389 | ```javascript 390 | var fs = require('fs'); 391 | 392 | var writeStream = fs.createWriteStream('./test.txt'); 393 | 394 | writeStream.on('finish', function() { 395 | console.log('all done ...'); 396 | }); 397 | 398 | writeStream.write('Hello'); 399 | writeStream.write(' '); 400 | writeStream.write('World!'); 401 | 402 | writeStream.end(); 403 | ``` 404 | --- 405 | 406 | ### Example (4) 407 | ```javascript 408 | var fs = require('fs'); 409 | var zlib = require('zlib'); 410 | var request = require('request'); 411 | 412 | var writeStream = fs.createWriteStream('path/to/file'); 413 | //zlib.createGzip() is a Transform stream 414 | request('http://google.com/').pipe(zlib.createGzip()).pipe(writeStream); 415 | ``` 416 | --- 417 | ### Custom Streams 418 | 419 | ```javascript 420 | var stream = require('stream'); 421 | var util = require('util'); 422 | 423 | function EchoStream () { 424 | stream.Writable.call(this); 425 | }; 426 | 427 | util.inherits(EchoStream, stream.Writable); 428 | 429 | EchoStream.prototype._write = function (chunk, encoding, done) { 430 | console.log(chunk.toString()); 431 | done(); 432 | } 433 | 434 | var myStream = new EchoStream(); 435 | process.stdin.pipe(myStream); 436 | ``` 437 | * Потоците обикновенно работят с buffers. 438 | * ObjectMode: true ни позволява да работим с JS обекти, вместо с буфери. 439 | * Ако ни трябва Readable или Transform stream трябва съответно да реализираме _read или _transform методи вместо _write 440 | 441 | --- 442 | ##### Transform Stream Example 443 | ```javascript 444 | var stream = require('stream'); 445 | var fs = require('fs'); 446 | var liner = new stream.Transform( { objectMode: true } ); 447 | 448 | liner._transform = function (chunk, encoding, done) { 449 | var data = chunk.toString(); 450 | if (this._lastLineData) data = this._lastLineData + data; 451 | 452 | var lines = data.split('\n'); 453 | this._lastLineData = lines.pop(); 454 | 455 | lines.forEach(this.push.bind(this)); 456 | done(); 457 | }; 458 | 459 | liner._flush = function (done) { 460 | if (this._lastLineData) this.push(this._lastLineData); 461 | this._lastLineData = null; 462 | done(); 463 | }; 464 | 465 | var source = fs.createReadStream('path/to/file'); 466 | source.pipe(liner); 467 | 468 | liner.on('readable', function () { 469 | var line = null; 470 | while((line = liner.read())){ 471 | console.log(line); 472 | } 473 | }); 474 | ``` 475 | --- 476 | ### Readable Stream Example 477 | 478 | ```javascript 479 | var util = require('util'); 480 | var Readable = require('stream').Readable; 481 | 482 | var MyStream = function(options) { 483 | Readable.call(this, options); // pass through the options to the Readable constructor 484 | this.counter = 1000; 485 | }; 486 | 487 | util.inherits(MyStream, Readable); // inherit the prototype methods 488 | 489 | MyStream.prototype._read = function(n) { 490 | this.push('foobar'); 491 | if (this.counter-- === 0) { // stop the stream 492 | this.push(null); 493 | } 494 | }; 495 | 496 | var mystream = new MyStream(); 497 | mystream.pipe(process.stdout); 498 | ``` 499 | 500 | --- 501 | class: middle, center 502 | [More about Streams](https://github.com/substack/stream-handbook) 503 | --- 504 | ### Buffers 505 | Когато четем от файлове или работим с мрежата ние получаваме сурова (raw) памет, която е извън v8 heap-а. Buffer-ът представлява mapping (handle) между тази памет и JS Array. 506 | v8 няма контрол върху тази памет, за това този mapping, който се създава, позволява на libuv да деалокира суровата памет след като Garbage Collector-а (GC) премахне нашия buffer. 507 | 508 | Buffer-ите могат да бъдат гадни! 509 | 510 | Нека си представим, че pre-alloc-ираме голямо парче памет (fs.readFile без encoding) и след това разбием тази памет на малки парчета към конкретни JS обекти. Какво ще се случи с нашата памет, ако без да искаме оставим референции към някои от тези обекти? 511 | 512 | Използването на slice в Node върху буфери не е добра идея, защото може да доведе до memory leaks. Slice не прави копие на array, а създава пойнтъри към паметта, която държи array-я. 513 | За това е по-добре е да си създадем изцяло нов Buffer. 514 | 515 | Да разгледаме също какво се случва в Stream Example(1). 516 | --- 517 | ### Process 518 | 519 | * process обектът е достъпен навсякъде в нашето node приложение и разширява Event Emitter. 520 | 521 | * има полезни референции към: 522 | - process.stdin, process.stdout, process.stderr (Streams) 523 | 524 | - process.argv, process.pid, process.title, process.cwd() и др (Attrbutes) 525 | 526 | - process.abort(), process.kill(pid) (methods) 527 | 528 | - 'exit', 'error' + POSIX signal events (events) 529 | 530 | --- 531 | ### Using Node as a client 532 | 533 | Вече разгледахме request, а сега нека видим http 534 | ```javascript 535 | var http = request('http'); 536 | var options = { 537 | host: 'www.google.com', 538 | port: 80, 539 | path: '/', 540 | method: 'GET' 541 | }; 542 | 543 | var req = http.request(options, function(resp){ 544 | console.log(resp.statusCode); 545 | resp.pipe(process.stdout); 546 | }); 547 | //req is a WritableStream 548 | //resp is a ReadableStream 549 | 550 | req.end(); //close the writable stream 551 | 552 | //We can use get instead 553 | http.get('www.google.com', function(res){ 554 | res.pipe(process.stdout); 555 | }); 556 | ``` 557 | 558 | --- 559 | ### Using Node as web server Example(1) 560 | 561 | ```javascript 562 | var http = require('http'); 563 | var server = http.createServer(function(req, res){ 564 | //req and res are streams 565 | //process request 566 | }); 567 | 568 | server.listen(8081, function() { 569 | console.log('Server is listening on 8081 ...'); 570 | }); 571 | ``` 572 | --- 573 | ```javascript 574 | var http = require("http"), 575 | url = require("url"), 576 | path = require("path"), 577 | fs = require("fs"), 578 | port = process.argv[2] || 8888; 579 | 580 | http.createServer(function(request, response) { 581 | var uri = url.parse(request.url).pathname 582 | , filename = path.join(process.cwd(), uri); 583 | 584 | fs.exists(filename, function(exists) { 585 | if(!exists) { 586 | response.writeHead(404, {"Content-Type": "text/plain"}); 587 | response.write("404 Not Found\n"); 588 | response.end(); 589 | return; 590 | } 591 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 592 | fs.readFile(filename, "binary", function(err, file) { 593 | if(err) { 594 | response.writeHead(500, {"Content-Type": "text/plain"}); 595 | response.write(err + "\n"); 596 | response.end(); 597 | return; 598 | } 599 | response.writeHead(200); 600 | response.write(file, "binary"); 601 | response.end(); 602 | }); 603 | }); 604 | }).listen(parseInt(port, 10)); 605 | ``` -------------------------------------------------------------------------------- /lectures/07-express/expressServer/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = require('./router'); 3 | var fs = require('fs'); 4 | var bodyParser = require('body-parser'); 5 | var busboy = require('connect-busboy'); 6 | var app = express(); 7 | var port = 8888; 8 | 9 | app.use(bodyParser.json()); 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | app.use('/api/books', router); 12 | 13 | app.post('/upload', busboy(), function(req, res) { 14 | if(!req.busboy) return res.status(500).send('Incorrect post headers!'); 15 | req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { 16 | var ws = fs.createWriteStream('./uploads/'+ filename); 17 | ws.on('finish', function() { 18 | console.log('File saved ...'); 19 | res.status(200).end(); 20 | }); 21 | file.pipe(ws); 22 | }); 23 | req.pipe(req.busboy); 24 | }); 25 | 26 | //Serve static content 27 | app.use(express.static(__dirname + '/static')); 28 | 29 | app.listen(port, function() { 30 | console.log('Server listening on ' + port + ' ...'); 31 | }); -------------------------------------------------------------------------------- /lectures/07-express/expressServer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "07-express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "= <=>", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.14.1", 13 | "connect-busboy": "0.0.2", 14 | "express": "^4.13.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lectures/07-express/expressServer/router.js: -------------------------------------------------------------------------------- 1 | var expressRouter = require('express').Router(); 2 | var fs = require('fs'); 3 | 4 | var books = [{ 5 | id: '1', 6 | author: 'None', 7 | name: 'book1' 8 | },{ 9 | id: '2', 10 | author: 'None1', 11 | name: 'book2' 12 | },{ 13 | id: '3', 14 | author: 'None', 15 | name: 'book3' 16 | }]; 17 | 18 | expressRouter.get('/', function(req, res) { 19 | var filter = req.query; 20 | if(Object.keys(filter).length === 0) return res.json(books); 21 | var result = books.filter(function(book) { 22 | var flag = true; 23 | for(var prop in filter) { 24 | flag = (filter[prop] === book[prop]); 25 | } 26 | return flag; 27 | }); 28 | res.json(result); 29 | }); 30 | 31 | expressRouter.get('/:bookId', function(req, res) { 32 | var book = books[req.params.bookId]; 33 | if(!book) res.status(404).send('Book not found!'); 34 | res.json(book); 35 | }); 36 | 37 | expressRouter.post('/', function(req, res) { 38 | books.push(req.body); 39 | res.status(201).end(); 40 | }); 41 | 42 | module.exports = expressRouter; 43 | -------------------------------------------------------------------------------- /lectures/07-express/expressServer/static/index.html: -------------------------------------------------------------------------------- 1 | Hello world! 2 | -------------------------------------------------------------------------------- /lectures/07-express/generator.js: -------------------------------------------------------------------------------- 1 | function readFile(filename, callback) { 2 | setTimeout(function() { 3 | callback(null, 'File ' + filename + ' contents'); 4 | }, 5000); 5 | } 6 | 7 | function* f(maxN) { 8 | while(maxN < 5) { 9 | var x = yield readFile('alabala', niakva); 10 | console.log(x); 11 | maxN++; 12 | //return 1000; 13 | } 14 | } 15 | 16 | function niakva(error, result) { 17 | //console.log(result); 18 | iter.next(result); 19 | } 20 | 21 | 22 | var iter = f(1); 23 | console.log(iter.next()); //kick start 24 | //console.log(iter.next(500)); 25 | -------------------------------------------------------------------------------- /lectures/07-express/httpServer/app.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var port = 8888; 3 | 4 | var server = http.createServer(function(req, res) { 5 | var allData = []; 6 | 7 | req.on('data', function(chunk) { 8 | allData.push(chunk); 9 | }); 10 | 11 | req.on('end', function() { 12 | var host = allData.toString(); 13 | var request = http.request({ host: host, path: '/' }, function(req) { 14 | var htmlData = []; 15 | req.on('data', function(htmlChunk) { 16 | htmlData.push(htmlChunk); 17 | }); 18 | req.on('end', function() { 19 | res.setHeader('Status code', 200); 20 | res.write(htmlData.toString()); 21 | res.end(); 22 | }); 23 | }); 24 | request.end(); 25 | }); 26 | }); 27 | 28 | server.listen(port, function(){ 29 | console.log('Server listening on ' + port); 30 | }); -------------------------------------------------------------------------------- /lectures/08-dom-intro-and-events.markdown: -------------------------------------------------------------------------------- 1 | # Съдържание 2 | 3 | * DOM 4 | * DOM vs HTML 5 | * Типове данни 6 | * Селектиране на елементи 7 | * Добавяне на събития 8 | * addEventListener 9 | * eventObject 10 | * bubbling 11 | * capturing 12 | * on.\* 13 | * Премахване на събития 14 | * Memory leaks при обработката на събития 15 | --- 16 | 17 | # Document Object Model 18 | 19 | API за работа с HTML и XML 20 | 21 | Можем да си мислим за HTML като за сериализирана версия на DOM. 22 | 23 | --- 24 | 25 | # DOM, типове елементи 26 | 27 | - `document` - коренът на DOM дървото 28 | - `Element` - елемент в DOM дървото, който има дъщерни елементи 29 | - `NodeList` - списък с DOM обекти, които могат да бъдат както елементи, така и `text` 30 | - `Text` - DOM обект, който съдържа само текст, не можа да съдържа елементи 31 | 32 | --- 33 | 34 | ## Достъп до елементи в DOM дървото 35 | 36 | - `document.createElement(tag_name)` - създава нов елемент 37 | - `element.childNodes` - достъп до всички дъщерни елементи на даден елемент 38 | - върнатият резултат е от тип `NodeList`, не е масив 39 | - `element.children` - връща всички дъщерни елементи от тип `Element` 40 | - `element.appendChild(child)` - добавя дъщерен елемент на даден елемент 41 | - `element.removeChild(child)` - изтрива дъщерен елемент от даден елемент 42 | 43 | --- 44 | 45 | # Атрибути vs полета 46 | 47 | в HTML можем да зададем атрибути на различните елементи: 48 | 49 | ```html 50 | 51 | ``` 52 | 53 | След като браузърът обработи HTML ще създаде `HTMLInputElement`, който ще има различни полета: 54 | 55 | - checked 56 | - type 57 | - disabled 58 | - value 59 | - ... 60 | 61 | --- 62 | 63 | # Работа с атрибути 64 | 65 | - `element.setAttribute(attr_name, value)` 66 | - `element.getAttribute(attr_name)` 67 | - `element.removeAttribute(attr_name)` 68 | 69 | --- 70 | 71 | # Селектиране на елементи 72 | 73 | - `document.getElementById(id)` - селектира елемент по уникален идентификатор 74 | - `element.getElementsByTagName(tag_name)` - селектира елементи по име на таг 75 | - `element.getElementsByClassName(class_name)` - селектира елементи по име на клас 76 | - `element.querySelectorAll(selector)` - селектира всички елементи, които отговарят на зададения селектор 77 | - `element.querySelector(selector)` - селектира първият елемент, които отговаря на зададения селектор 78 | 79 | --- 80 | 81 | # CSS селектори 82 | 83 | - `element` - селектира всички елементи с име `element` 84 | - `#element-id` - селектира елемента с идентификатор `element-id` 85 | - `.class-name` - селектира всички елемент, които използват клас с име `class-name` 86 | - `[attribute="value"]` - селектира всички елементи, които имат атрибут `attribute` със стойност `value` 87 | - `parentSelector childSelector` - селектира всички елемент, които съвпадат със селектора `childSelector` и се намират в елементи съвпадащи със селектора `parentSelector` 88 | 89 | --- 90 | 91 | # Примери 92 | 93 | ```javascript 94 | document.querySelectorAll('#foo'); 95 | 96 | document.querySelector('.foo-bar'); 97 | 98 | document.querySelectorAll('content.container [title="foobar"]:nth-child(2)'); 99 | ``` 100 | --- 101 | 102 | # Shadow DOM 103 | 104 | Част от стандарта на WebComponents. Позволява ни по-добра енкапсулация. 105 | 106 | - Можем да създадем т.нар. "shadow root", който е корен на поддърво, което се третира по други правила от браузъра 107 | - Създаденото поддърво е независимо откъм стилове 108 | 109 | --- 110 | 111 | # Shadow DOM 112 | 113 | ```html 114 | 124 |
Foobar
125 | ``` 126 | 127 | 128 | ```javascript 129 | let root = document.querySelector('#foo').createShadowRoot(); 130 | let clone = document.importNode(document.querySelector('#greeting').content, true); 131 | root.appendChild(clone); 132 | ``` 133 | 134 | --- 135 | 136 | # Template елемента 137 | 138 | Позволява да добавим markup в документа без той да бъде обработван от браузъра. 139 | 140 | Не е нужно да използваме ` 12 | 13 | 14 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/public/src/main.js: -------------------------------------------------------------------------------- 1 | /* global document, 2 | location, XMLHttpRequest, JSON */ 3 | 4 | (function () { 5 | 6 | 'use strict'; 7 | 8 | function $(id) { 9 | return document.getElementById(id); 10 | } 11 | 12 | function postArticle(title, content) { 13 | var xhr = new XMLHttpRequest(), 14 | post = { title: title, content: content }; 15 | xhr.open('post', location.protocol + '//' + location.host + '/post', true); 16 | xhr.setRequestHeader('Content-type', 'application/json'); 17 | xhr.send(JSON.stringify(post)); 18 | } 19 | 20 | $('send-btn').onclick = function () { 21 | postArticle($('title').value, $('content').value); 22 | }; 23 | }()); 24 | -------------------------------------------------------------------------------- /tasks/03/solutions.js: -------------------------------------------------------------------------------- 1 | //1. 2 | Array.prototype.myMap = function(operation){ 3 | var result = []; 4 | this.forEach(function(elem){ 5 | result.push(operation(elem)); 6 | }); 7 | return result; 8 | }; 9 | 10 | [1,2,3,4,5,6,7,8,9].myMap(function(el){ return el + 10; }); // -> [11,12,13,14,15,16,17,18,19] 11 | 12 | 13 | 14 | //2. 15 | function Point(x, y){ 16 | this.x = x; 17 | this.y = y; 18 | } 19 | 20 | Point.prototype.toArray = function(){ 21 | var self = this; 22 | var result = []; 23 | Object.keys(this).forEach(function(key){ 24 | var prop = self[key]; 25 | if(prop instanceof Function) return; 26 | result.push(prop); 27 | }); 28 | return result; 29 | }; 30 | 31 | function Point3D(x, y, z){ 32 | Point.call(this, x, y); 33 | this.z = z; 34 | } 35 | 36 | Point3D.prototype = Object.create(Point.prototype); 37 | 38 | var p = new Point(1,2); 39 | var p1 = new Point3D(1,2,3); 40 | console.log(p.toArray()); 41 | console.log(p1.toArray()); 42 | 43 | 44 | 45 | //3 46 | function Vehicle(mileage){ 47 | 48 | this.getMileage = function(){ 49 | return mileage.val; 50 | }; 51 | 52 | this.tostring = function(){ 53 | return 'This Vehicle mileage is ' + mileage.val; 54 | }; 55 | 56 | } 57 | 58 | 59 | function Car(brand, consumption){ 60 | var mileage = { val: 0 }; 61 | Vehicle.call(this, mileage); 62 | var superToString = this.tostring; 63 | 64 | this.drive = function(miles){ 65 | mileage.val += miles; 66 | }; 67 | 68 | this.getBrand = function(){ 69 | return brand; 70 | }; 71 | 72 | this.getConsumption = function(){ 73 | return consumption; 74 | }; 75 | 76 | this.tostring = function(){ 77 | return brand + ' ' + consumption + ' ' + superToString(); 78 | }; 79 | } 80 | 81 | var c1 = new Car('honda', 5); 82 | console.log(c1.getMileage()); // -> 0 83 | console.log(c1.tostring()); // -> honda 5 This Vehicle mileage is 0 84 | c1.drive(1000); 85 | console.log(c1.getMileage()); // -> 1000 86 | console.log(c1.tostring()); // -> honda 5 This Vehicle mileage is 1000 87 | 88 | 89 | 90 | //4 91 | var FMIjs = (function(){ 92 | 93 | var BinaryHeap = function(list){ 94 | var self = this; 95 | if(!(list instanceof Array)) return this; //or use Array.isArray(list) 96 | list.forEach(function(el){ 97 | self.insert(el); 98 | }); 99 | }; 100 | 101 | BinaryHeap.prototype = Object.create(Array.prototype); 102 | 103 | BinaryHeap.prototype.parentIndex = function(i){ 104 | return i === 0 ? 0 : Math.ceil(i/2) - 1; 105 | }; 106 | 107 | BinaryHeap.prototype.leftIndex = function(i){ 108 | return (2 * i) + 1; 109 | }; 110 | 111 | BinaryHeap.prototype.rightIndex = function(i){ 112 | return (2 * i) + 2; 113 | }; 114 | 115 | BinaryHeap.prototype.parentElement = function(i){ 116 | return this[this.parentIndex(i)]; 117 | }; 118 | 119 | 120 | BinaryHeap.prototype.leftElement = function(i){ 121 | return this[this.leftIndex(i)]; 122 | }; 123 | 124 | BinaryHeap.prototype.rightElement = function(i){ 125 | return this[this.rightIndex(i)]; 126 | }; 127 | 128 | BinaryHeap.prototype.getMinimum = function(){ 129 | var lastIndex = this.length - 1; 130 | this.swap(0, lastIndex); 131 | var result = this.pop(); 132 | this.bubbleDown(); 133 | return result; 134 | }; 135 | 136 | BinaryHeap.prototype.isEmpty = function(){ 137 | return this.length === 0; 138 | }; 139 | 140 | BinaryHeap.prototype.insert = function(value){ 141 | this.push(value); 142 | this.bubbleUp(); 143 | return this.toString(); 144 | }; 145 | 146 | BinaryHeap.prototype.swap = function(firstIndex, secondIndex){ 147 | var tmp = this[firstIndex]; 148 | this[firstIndex] = this[secondIndex]; 149 | this[secondIndex] = tmp; 150 | }; 151 | 152 | BinaryHeap.prototype.bubbleUp = function(){ 153 | var index = this.length - 1; 154 | while(this.parentElement(index) > this[index]){ 155 | var parentIndex = this.parentIndex(index); 156 | this.swap(index, parentIndex); 157 | index = parentIndex; 158 | } 159 | }; 160 | 161 | BinaryHeap.prototype.bubbleDown = function(){ 162 | var index = 0; 163 | var rightValue = null; 164 | var leftIndex = null; 165 | var currentValue = null; 166 | while(this.leftElement(index)){ 167 | leftValue = this.leftElement(index); 168 | rightValue = this.rightElement(index); 169 | currentValue = this[index]; 170 | if(currentValue > leftValue || currentValue > rightValue){ 171 | if(leftValue > rightValue){ 172 | this.swap(this.rightIndex(index), index); 173 | index = this.rightIndex(index); 174 | }else{ 175 | this.swap(this.leftIndex(index), index); 176 | index = this.leftIndex(index); 177 | } 178 | }else{ 179 | break; 180 | } 181 | } 182 | }; 183 | 184 | var heapSort = function(list){ 185 | var heap = new BinaryHeap(list); 186 | var result = []; 187 | while(heap.length){ 188 | result.push(heap.getMinimum()); 189 | } 190 | return result; 191 | }; 192 | return { 193 | BinaryHeap: BinaryHeap, 194 | heapSort: heapSort 195 | }; 196 | }()); 197 | 198 | var arr = [1,2,6,9,2,4,9,87,36,21,1]; 199 | console.log(FMIjs.heapSort(arr)); 200 | 201 | var b = new FMIjs.BinaryHeap(); 202 | b.insert(5); 203 | b.insert(10); 204 | b.insert(1); 205 | 206 | console.log(b.getMinimum()); //-> 1 207 | -------------------------------------------------------------------------------- /tasks/04/README.md: -------------------------------------------------------------------------------- 1 | # ES2015 Задачи 2 | 3 | ## Async Execution and Promises 4 | 5 | 1. Направете функция която изчаква 1000ms след което записва във файл произволен стринг. След това изчаква още 1000ms и записва в друг файл отново произволен стринг. След още 1000ms изчакване отваря първия файл, отново изчаква 1000ms, отваря втория файл, изчаква отново 1000ms и накрая записва в трети файл конкатениран резултатa от четенето на двата предходни файла. 6 | Използвайте функции [writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) и [readFile](https://nodejs.org/api/fs.html#fs_fs_readfile_file_options_callback) и _не използвайте_ Promises. 7 | 8 | 1. Направете предната задача използвайки Promises 9 | 10 | 1. Използвайте примера за httpGet oт първия час и напишете генератор на произволни имена на класове за браузъра. За целта вземете имена на класове от [ClassNamer](http://www.classnamer.com/) - подайте http request към адрес http://www.classnamer.com/index.txt?generator=generic. 11 | 12 | 1. Напишете функция simulateDocs, която ще ви помогне да симулирате, че сте написали документация за кода си. Тя трябва да използва генератора от предната задача, за да получи произволни имена на класове и при последващи извиквания да връща стрингoве: 13 | simulateDocs() -> "ClassName1 extends ClassName2" 14 | simulateDocs() -> "ClassName1 extends ClassName2 extends ClassName3" 15 | ... 16 | ClassName1,..N са върнати от генератора имена и при всяко следващо извикване функцията simulateDocs върнатият стринг е продължение на този от предходното извикване. При повече от 5 извиквания функцията трябва да спре да връща резултат. 17 | 18 | -------------------------------------------------------------------------------- /tasks/04/solutions/task1.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function getRandomString() { 4 | return Math.random().toString(36).substring(7); 5 | } 6 | 7 | function handleError(err) { 8 | console.log(err); 9 | } 10 | 11 | function wait1000(err, callback) { 12 | if(err) return handleError(err); 13 | console.log('simulating async operation...'); 14 | setTimeout(function(){ 15 | callback(null); 16 | }, 1000); 17 | } 18 | 19 | function writeFile(err, data, callback) { 20 | if(err) return handleError(err); 21 | fs.writeFile(data.path, data.string, callback); 22 | } 23 | 24 | function readFile(err, data, callback) { 25 | if(err) return handleError(err); 26 | fs.readFile(data.path, callback); 27 | } 28 | 29 | function doStuff(){ 30 | var processPath = process.cwd(); 31 | var FILE1_PATH = processPath + '/file1'; 32 | var FILE2_PATH = processPath + '/file2'; 33 | var FILE3_PATH = processPath + '/file3'; 34 | 35 | wait1000(null, function() { 36 | var randString = getRandomString(); 37 | writeFile(null, { path: FILE1_PATH, string: randString } , function(err) { 38 | wait1000(err, function(err) { 39 | randString = getRandomString(); 40 | writeFile(err, { path: FILE2_PATH, string: randString }, function(err) { 41 | wait1000(err, function(err) { 42 | readFile(err, { path: FILE1_PATH }, function(err, content1) { 43 | wait1000(err, function(err){ 44 | readFile(err, { path: FILE2_PATH }, function(err, content2) { 45 | wait1000(err, function(err) { 46 | writeFile(err, { path: FILE3_PATH, string: content1 + content2 }, function(err) { 47 | if(err) return handleError(err); 48 | console.log('This is so ugly ... '); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | }); 55 | }); 56 | }); 57 | }); 58 | }); 59 | } 60 | 61 | doStuff(); -------------------------------------------------------------------------------- /tasks/04/solutions/task2-es6.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var promisify = require('es6-promisify'); 3 | var _readFile = promisify(fs.readFile); 4 | var _writeFile = promisify(fs.writeFile); 5 | var text1; 6 | 7 | function wait(time) { 8 | return new Promise(res => setTimeout(res, time)); 9 | } 10 | 11 | wait(1000) 12 | .then(() => _writeFile('hello.txt', 'text of file I', 'utf8')) 13 | .then(() => wait(1000)) 14 | .then(() => _writeFile('hello2.txt', 'text of file II', 'utf8')) 15 | .then(() => wait(1000)) 16 | .then(() => _readFile('hello.txt')) 17 | .then((txt) => text1 = txt) 18 | .then(() => wait(1000)) 19 | .then(() => _readFile('hello2.txt')) 20 | .then((text2) => _writeFile('result.txt', `${text1} ${text2}`, 'utf8')) 21 | .catch((err) => console.log(err)); -------------------------------------------------------------------------------- /tasks/04/solutions/task2.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | //In this solution we can create a function promisify which takes a function as an argument 4 | //and retuns a promise but I will stick to the more generic way. 5 | //PS. On github there are modules that provide this functionality. 6 | 7 | //Because we dont have an async operation here the better solution is to return a resolved Promise 8 | //instead of creating a new promise like readFile and writeFile and resolve it inside. 9 | function getRandomString(data) { 10 | data = data || {}; //if data is undefined or null assign an empry object 11 | data.string = Math.random().toString(36).substring(7); 12 | return Promise.resolve(data); 13 | } 14 | 15 | function handleError(err) { 16 | console.log(err); 17 | } 18 | 19 | function wait1000(data) { 20 | return new Promise(function(resolve, reject){ 21 | console.log('simulating async operation...'); 22 | 23 | //Its always a good idea to name our functions for debuging purposes. 24 | //This way in the stack trace we can see the function name instad of anonymous function. 25 | setTimeout(function asyncOperation(){ 26 | resolve(data); 27 | }, 1000); 28 | }); 29 | } 30 | 31 | //I will curry the writeFile and readFile Functions so I can pass the necessary paths when I chain the promises. 32 | function writeFile(path) { 33 | return function writeFilePromise(data) { 34 | return new Promise(function(resolve, reject){ 35 | fs.writeFile(path, data.string, function(err){ 36 | if(err) return reject(err); 37 | resolve(data); 38 | }); 39 | }); 40 | }; 41 | } 42 | 43 | function concatContents(data){ 44 | if(!data.contents) throw new Error('File contents not found!'); 45 | data.string = data.contents.join(''); 46 | return Promise.resolve(data); 47 | } 48 | 49 | //We will store the contents of the files in an array - contents. 50 | function readFile(path) { 51 | return function readFilePromise(data) { 52 | return new Promise(function(resolve, reject) { 53 | fs.readFile(path, function(err, content) { 54 | if(err) return reject(err); 55 | data = data || {}; 56 | if(!data.contents) data.contents = []; 57 | data.contents.push(content); 58 | resolve(data); 59 | }); 60 | }); 61 | }; 62 | } 63 | 64 | function doStuff() { 65 | var processPath = process.cwd(); 66 | var FILE1_PATH = processPath + '/file1'; 67 | var FILE2_PATH = processPath + '/file2'; 68 | var FILE3_PATH = processPath + '/file3'; 69 | var data = { 70 | string : '', 71 | contents: [] 72 | }; 73 | 74 | wait1000(data) 75 | .then(getRandomString) //Then expects a function as arguments .then(onFulfilled, onRejected). 76 | .then(writeFile(FILE1_PATH)) //Because readFile and writeFile are curried we can pass an argument. 77 | .then(wait1000) 78 | .then(getRandomString) 79 | .then(writeFile(FILE2_PATH)) 80 | .then(wait1000) 81 | .then(readFile(FILE1_PATH)) 82 | .then(wait1000) 83 | .then(readFile(FILE2_PATH)) 84 | .then(wait1000) 85 | .then(concatContents) 86 | .then(writeFile(FILE3_PATH)) 87 | .catch(handleError) //handleError will catch an error from any of the above functions. 88 | .then(function finalMessage(data) { 89 | console.log('Thats much better...'); 90 | }); 91 | } 92 | 93 | doStuff(); 94 | -------------------------------------------------------------------------------- /tasks/05/01.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Writable = require('stream').Writable; 4 | 5 | 6 | class FileReader extends Writable { 7 | constructor() { 8 | super(); 9 | this.buffer = []; 10 | console.log(this); 11 | } 12 | 13 | _write() 14 | } 15 | 16 | 17 | let fr = new FileReader(); 18 | console.log(fr); 19 | -------------------------------------------------------------------------------- /tasks/05/README.md: -------------------------------------------------------------------------------- 1 | # ES2015 Задачи 2 | 3 | ## NodeJS Streams, Events, Http 4 | 5 | 1. Да се напише конструктор функция **FileReader**, която има properties: 6 | 7 | ``` 8 | lastLine (число) 9 | buffer (масив) 10 | ... и каквито други са ви необходими 11 | ``` 12 | 13 | и [разширява](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor) [stream.Writable](https://nodejs.org/api/stream.html#stream_class_stream_writable). 14 | 15 | При идване на данни към потока те трябва да се convert-нат към String, който се разцепва спрямо '\n'. 16 | Ако последния елемент на стринга, не е '\n' и все още потокът не е затворен трябва да запомним (в `lastLine`) 17 | последния елемен от масива (не е цял ред). На следващото писане трябва да го конкатенираме с 18 | новополучените данни. Всички останали редове се слагат във вътрешния масив `buffer`. Четенето на нови 19 | данни от страна на потока трябва да става, когато масивът `buffer` е празен. 20 | 21 | **[stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable)** (прочетен файл, мрежов източник, пр.) -----*data*-----> **FileReader** (наследник на stream.Writable, имплементира [write](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback)) 22 | 23 | FileReader също трябва да има метод `fetchLine`, който извършва асинхронна операция, която трябва да 24 | се реализира със `setTimeout`. Този метод трябва да хвърля евент `next-line` с поредния елемент от 25 | масива `buffer`, а ако той не съществува и все още потокът не е затворен трябва да се прочете нов 26 | chunk от данни и да се повтори същата операция. Ако потокът е затворен значи сме приключили с 27 | четенето на данни и можем да хвърлим евента с `null`. 28 | 29 | Въпрос: Защо трябва `fetchLine` да е асинхронен и да използваме `setTimeout` вместо [`process.nextTick`](https://nodejs.org/api/process.html#process_process_nexttick_callback_arg)? 30 | 31 | ------ 32 | 33 | 2. Да се реализира конструктор функция с името **RoundRobin**, която взима като аргументи масив streams от FileReader-и. RoundRobin разширява EventEmitter и има properties: 34 | 35 | ``` 36 | streams, 37 | currentLine (масив), 38 | generator 39 | ... и каквито други са ви необходими. 40 | ``` 41 | 42 | Обектите създадени с тази функция трябва да имат и метод start, който предизвиква стартирането на генератора. 43 | 44 | Ако имаме n на брой файла трябва към всеки един от тях да има отворен [fs.ReadStream](https://nodejs.org/api/fs.html#fs_class_fs_readstream), който е pipe-нат 45 | към FileReader. Искаме да обикаляме по всеки един от тези FileReader-и и да взимаме по един ред. 46 | След първото преминаване по всички потоци резултатът намиращ се в currentLine (масив с N елемента в случая) трябва да изглежда така: 47 | 48 | ``` 49 | ["file1-Line1", "file2-Line1", ... , "fileN-Line1"] 50 | ``` 51 | 52 | След създаването на string от вида: 53 | 54 | ``` 55 | file1-Line1 file2-Line1 ... fileN-Line1 56 | ``` 57 | 58 | искаме да хвърлим евент `new-line` с получения резултат и да продължим нататък. 59 | Процесът спира, когато всички неща в `currentLine` са `null` и трябва да хвърлим евент `finish`. 60 | (Някои файлове може да са по-дълги от други) 61 | 62 | ------ 63 | 64 | 3. Да се създаде http сървър работещ на порт, който сме подали при стартирането на скрипта. Когато дойде request искаме да вземем pathname-а (пример http://localhost/file1/file2/file3 - pathname е /file1/file2/file3) и да го разбием на '/'. В зависимост, колко файла има зададени в url-то и дали ги има на файловата система искаме да използваме нещата от задача 1 и 2 за да върнем резултат от вида: 65 | 66 | ```text 67 | file1-Line1 file2-Line1 file3-Line1 68 | file1-Line2 file2-Line2 file3-Line2 69 | ... 70 | ``` 71 | 72 | Когато получим request, който иска от сървъра да му върне favicon трябва да върнем 404 със съобщение "File not found". 73 | -------------------------------------------------------------------------------- /tasks/05/solutions/asyncgen.js: -------------------------------------------------------------------------------- 1 | // runner for async shit 2 | var run = function(generator) { 3 | var gi; 4 | var util = function(asyncFun) { 5 | console.log('use the util to have a reference to thyself in async.'); 6 | asyncFun(gi); 7 | } 8 | 9 | gi = generator(util); 10 | gi.next(); // kick/start the generator 11 | } 12 | 13 | // main program 14 | run( function *(util) { 15 | 16 | util(function(main) { 17 | setTimeout(function () { 18 | console.log('first async completed.'); 19 | main.next(); 20 | }); 21 | }); 22 | yield; 23 | console.log('do something after first async'); 24 | /* do some more shit */ 25 | 26 | yield util(function(main) { 27 | setTimeout(function () { 28 | console.log('seocnd async completed.'); 29 | main.next(); 30 | }); 31 | }); 32 | 33 | console.log('do something after first async'); 34 | /* do some more shit */ 35 | }) 36 | 37 | 38 | -------------------------------------------------------------------------------- /tasks/05/solutions/simpleGenerator.js: -------------------------------------------------------------------------------- 1 | function countFive(gen) { 2 | var n = 0; 3 | var nextNumber = function() { 4 | setTimeout(function() { 5 | if(n < 6) { 6 | return iterable.next(n++);//go to yield with n++ 7 | } 8 | },0); 9 | return undefined; 10 | }; 11 | var iterable = gen(nextNumber); 12 | iterable.next(); //kick start 13 | } 14 | 15 | countFive(function* (nextNumber) { 16 | while(true){ 17 | var num = yield nextNumber(); 18 | console.log(num); 19 | } 20 | }) -------------------------------------------------------------------------------- /tasks/05/solutions/task1.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | //Resource for writable stream: https://nodejs.org/api/stream.html#stream_class_stream_writable 4 | var WritableStream = require('stream').Writable; 5 | 6 | function FileReader() { 7 | WritableStream.call(this); 8 | this.lastDone = null; 9 | this.lastLine = null; 10 | this.buffer = []; 11 | this.hasFinished = false; 12 | var self = this; 13 | 14 | //Add callback to the finish event and when its called set the property hasFinished to true. 15 | //We can use self._writableState.finished instad of doing this but we have to know about it. 16 | this.on('finish', function() { 17 | self.hasFinished = true; 18 | }); 19 | } 20 | 21 | util.inherits(FileReader, WritableStream); 22 | 23 | FileReader.prototype.fetchLine = function() { 24 | var self = this; 25 | 26 | //If stream's not finished and we don't have a nextLine we must let some data to be written to the stream 27 | //and try to fetch the line again. We use setTimeout because if we use process.nextTick the callback 28 | //will be executed before the _write is called (its executed before any I/O operations) 29 | //Resource: 30 | //https://nodejs.org/api/process.html#process_process_nexttick_callback_arg 31 | setTimeout(function() { 32 | var nextLine = self.buffer[0]; 33 | self.buffer = self.buffer.slice(1); //create new array without the first element 34 | if(!nextLine) { 35 | if(!self.hasFinished) { 36 | //if there is a done callback and stream is not closed 37 | //then exec done so data can be written into the stream 38 | if(self.lastDone && !self.hasFinished) self.lastDone(); 39 | self.fetchLine(); 40 | } else { 41 | //If stream is closed then send null 42 | self.emit('next-line', null); 43 | } 44 | return; //Don't execute line 46 45 | } 46 | self.emit('next-line', nextLine); 47 | }, 0); 48 | }; 49 | 50 | FileReader.prototype._write = function(chunk, encoding, done) { 51 | var chunkString = chunk.toString(); 52 | 53 | //Check if lastLine is set. If so append it to the current result. 54 | if(this.lastLine) { 55 | chunkString = this.lastLine + chunkString; 56 | this.lastLine = null; 57 | } 58 | 59 | var chunkLines = chunkString.split('\n'); //Split the lines 60 | 61 | //If the last symbol is not a \n we don't have the full line so we must save the 62 | //last element and append next chunkString to it 63 | if(chunkString.slice(chunkString.length - 1) !== '\n' && this.hasFinished) { 64 | this.lastLine = chunkLines.pop(); 65 | } 66 | this.buffer = this.buffer.concat(chunkLines); 67 | 68 | //Keep a reference to the done call so we can execute the function later when the 69 | //buffer array is empty 70 | this.lastDone = done; 71 | }; 72 | 73 | module.exports = FileReader; 74 | -------------------------------------------------------------------------------- /tasks/06/README.md: -------------------------------------------------------------------------------- 1 | # Express.js Server 2 | 3 | * Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. 4 | 5 | [API Reference](http://expressjs.com/api.html) 6 | 7 | 8 | ## Task 1 - Restful API 9 | 10 | * `GET /all_books` - returns all the books for all the users 11 | * `GET /books/:bookId` - returns the data for a book by a given bookId 12 | * `POST /book` - expects user, author, title, descriptionText and rate in the body data. Creates a new book and returns a bookId, which should be unique for every book! 13 | * `GET /all_users` - returns all the registered users. 14 | * `GET /all_authors` - returns all the authors for the books we have in our library. 15 | * `POST /register` - expects user as an argument. Creates a new user and returns a key for that user. If the user exists just returns a 409 response code. 16 | * `DELETE /book` - expects bookId as an argument. Deletes the bookId if the book exists, otherwise return a 403 response code. 17 | 18 | * `GET /all_books?:someField=:someVal` - A dynamic filter API endpoint which returns all books by a passed fiter properties 19 | ``` 20 | /all_books?rate=7 21 | /all_books?title=The%20Great%20Gatsby 22 | ``` 23 | 24 | ### Testing 25 | ``` 26 | curl -H "Content-Type: application/json" --data '{"user": "billy", "author": "Steinbeck",... }' http://localhost:8080/ 27 | ``` 28 | ## Task 2 - Async each Function 29 | ### Направете функция forEach, която приема като аргументи: 30 | * масив от елементи 31 | * итератор функция, която приема аргументи 32 | * пореден елемент от масива 33 | * callback фунцкия, която да извика, когато е приключил с обработката на елемента от масива 34 | * callback функция, приемаща единствен аргумент грешка. Тя ще се извика, когато възникне грешка или всички други callbacks са приключили 35 | 36 | Идеята на вашата функция forEach e да осигури паралелна "обработка" на елементи в масива чрез итератор функцията (втория си аргумент) 37 | 38 | Пример за употребата: 39 | 40 | ```javascript 41 | forEach(openFiles, function (file, next) { 42 | 43 | // Асинхронна обработка на елемента от масива 44 | console.log('Processing file ' + file); 45 | fs.readFile(file, function (err, file) { 46 | if (err) { 47 | next(err); 48 | } 49 | console.log(file); 50 | next(); 51 | }); 52 | 53 | }, function (err) { 54 | // Ако при обработката на елементите се хвърли грешка, тя ще се намира в аргумента err 55 | if (err) { 56 | console.log('A file failed to process', err); 57 | } else { 58 | console.log('All files have been processed successfully'); 59 | } 60 | }); 61 | ``` -------------------------------------------------------------------------------- /tasks/07/README.md: -------------------------------------------------------------------------------- 1 | # Maze traversal 2 | 3 | 1. Отворете следната връзка http://plnkr.co/edit/HFdwuA1B48eG01Rn1IGY 4 | 2. На базата на API описан в plnkr, в `script.js`, реализирайте: 5 | - `drawMaze(maze, parentNode)` - метод, който рисува лабиринта в елемента `parentNode`. 6 | - `traverseMaze(maze, start, target, strategy)` - метод, който на базата на подадените аргументи открива път в лабиринта и го рисува на екрана. 7 | - `maze` - матрица, която представлява дадения лабиринт. 8 | - `start` - начална точка от вида `[row, col]`. 9 | - `target` - стойност на целта (в зададения като пример масив е 2). 10 | - `strategy` - генератор, който обхожда матрицата. 11 | - Генераторът връща следните стойности за `value`: 12 | - `current` - текущата клетка в лабиринта. 13 | - `neighbors` - съседите на текущата клетка. 14 | 15 | 16 | # Query selector 17 | 18 | 1. Имплементирайте query selector (polyfill за `document.querySelectorAll`). Вашата имплементация трябва да поддържа следните опрерации: 19 | - `*` - селектира всички елементи. 20 | - `tagName` - селектира всички елементи с име `tagName`. 21 | - `#id` - селектира всички елементи с id със стойност `id`. 22 | - `.className` - селектира всички елементи, които имат добавен клас със стойност `className`. 23 | - `parentSelector childSelector` - селектира всички преки и непреки наследници, които удовлетворяват `childSelector`, на всички елементи, които са селектирани от `parentSelector`. 24 | - `parentSelector>childSelector` - селектира всички преки наследници, които удовлетворяват `childSelector`, на всички елементи, които са селектирани от `parentSelector`. -------------------------------------------------------------------------------- /tasks/08/wsChat/app.js: -------------------------------------------------------------------------------- 1 | var port = 8888, 2 | app = require('express')(), 3 | http = require('http').Server(app), 4 | io = require('socket.io')(http), 5 | counter = 0; 6 | 7 | io.on('connection', function(socket) { 8 | socket.username = socket.handshake.query.username || 'user' + (++counter); 9 | socket.emit('username', socket.username); 10 | 11 | socket.on('message', function(msg) { 12 | console.log('new message from ' + socket.username + ': ' + msg); 13 | this.broadcast.emit('message', { text: msg, username: this.username }); 14 | }); 15 | 16 | socket.on('disconnect', function() { 17 | console.log('user ' + this.username + ' disconnected'); 18 | counter--; 19 | }); 20 | }); 21 | 22 | http.listen(port, function() { 23 | console.log('listening on ' + port); 24 | }); -------------------------------------------------------------------------------- /tasks/08/wsChat/client.js: -------------------------------------------------------------------------------- 1 | var username = process.argv[2], 2 | query = username ? { query: "username=" + username } : undefined, 3 | socket = require('socket.io-client')('http://localhost:8888', query), 4 | keypress = require('keypress'), 5 | line = ''; 6 | 7 | keypress(process.stdin); 8 | 9 | socket.on('connect', function() { 10 | console.log('connected ...'); 11 | }); 12 | 13 | socket.on('disconnect', function() { 14 | console.log('disconnected'); 15 | }); 16 | 17 | socket.on('username', function(data){ 18 | username = data; 19 | console.log('username is ' + username); 20 | }); 21 | 22 | socket.on('message', function(message) { 23 | process.stdout.clearLine(); 24 | process.stdout.cursorTo(0); 25 | process.stdout.write(message.username + ': ' + message.text + '\n'); 26 | if(line) process.stdout.write(line); 27 | }); 28 | 29 | process.stdin.on('keypress', function (ch, key) { 30 | if (key && key.ctrl && key.name == 'c') { 31 | process.exit(); 32 | } else if(key && key.name === 'return') { 33 | socket.emit('message', line); 34 | process.stdout.write('\n'); 35 | line = ''; 36 | } else if(key && key.name === 'backspace') { 37 | process.stdout.clearLine(); 38 | process.stdout.cursorTo(0); 39 | line = line.slice(0 , -1); 40 | process.stdout.write(line); 41 | } else { 42 | if(ch === undefined) return; 43 | line += ch; 44 | process.stdout.write(ch); 45 | } 46 | }); 47 | 48 | process.stdin.setRawMode(true); 49 | process.stdin.resume(); 50 | -------------------------------------------------------------------------------- /tasks/08/wsChat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-socket", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "= <=>", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.13.3", 13 | "keypress": "^0.2.1", 14 | "socket.io": "^1.3.7", 15 | "socket.io-client": "^1.3.7" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tasks/09/README.md: -------------------------------------------------------------------------------- 1 | # Lightweight AngularJS implementation 2 | 3 | In this exercise we are going to create a lightweight implementation of AngularJS. 4 | 5 | Our framework is going to support directives, two-way data-binding, services, controllers...and even more! 6 | 7 | ## Provider 8 | 9 | 0. Create object literal called `Provider`. The provider object should has the following public methods: 10 | * `get` - used for getting services. 11 | * `directive` - used for defining directives. 12 | * `controller` - used for defining controllers. 13 | * `service` - used for defining services. 14 | * `annotate` - used for getting array of the names of the dependencies of given "provider" (i.e. service, controller or directive). 15 | * `invoke` - used for invoking services (i.e. resolving their dependencies and calling the factory method). 16 | * `Provider` should have properties called `DIRECTIVES_SUFFIX` and `CONTROLLERS_SUFFIX` with values "Directive" and "Controller". 17 | 18 | 0. Define property of type object, which is called `_providers` 19 | 20 | 0. Define a property called `_cache`. It should contains a property with value `new Scope` and key `rootScope` (we are going to implement the `Scope` in later section, for now you can comment the statement). 21 | 22 | 0. Define a method called `annotate`, which accepts a function and returns an array of its arguments' names. 23 | 24 | 0. Add method called `_register`. `_register` should accept two arguments - `name` (name of the provider) and `fn` (factory method of the provider). It should add new property of the `_providers` hash map with key the first argument passed to the method and value `fn`.. 25 | 26 | 0. Define a method called `get`. It should accept arguments called `name` (name of the provider) and `locals` (hash with local dependencies, the keys in the hash are the names of the dependencies and its values are the actual dependencies). `get` should return service with name `name`, if it is already cached (i.e. property of `this._cache`), otherwise it should call the factory method of the service (`this._provider[name]`) with the method `this.invoke` and cache the result. Do not forget to pass the local dependencies to `this.invoke`. 27 | 28 | 0. Define method called `invoke`. It should accept two arguments - `fn` (factory method) and `locals` (local dependencies). Using `annotate` and `get` resolve all dependencies of the current factory method (`fn`) and invoke the factory method. Return the result of the invocation. Note that the dependencies could be located both in `locals` hash and 29 | 30 | 0. Add methods called `directive`, `controller` and `service`. They should accept two arguments - `name` (name of the provider) and `fn` (factory method). They should call `_register` with appropriate name for the provider (i.e. with special suffix for `directive` and `controller` - `DIRECTIVES_SUFFIX`, `CONTROLLERS_SUFFIX`) and the factory method, which is passed as second argument. 31 | 32 | 33 | ## Scope 34 | 35 | 0. Define a constructor function called `Scope`. It should initialize the properties: 36 | * `$$watchers` - an empty array. 37 | * `$$children` - an empty array. 38 | * `$parent` - it should accept the value of a parameter called `parent` passed to the constructor function. 39 | * `$id` - it should accept the value of a parameter called `id` passed to the constructor function or `0` if the passed parameter is `undefined`. 40 | * `Scope` should have a "static" property called `counter`, with initial value `0`. 41 | 0. Define method called `$eval`. It should **evaluate expressions in the context of the current scope**. The expressions, which would be evaluated are: 42 | * Method invocation (i.e. `foo()`) 43 | * Function invocation, i.e. the value of the expression passed to `$eval` will be a function, which should be invoked in the context of the scope. 44 | * Get property value (i.e. `bar`). 45 | 46 | 0. Add method called `$watch` to the prototype of the `Scope` function. It should accept two arguments `exp` and `fn`. It should add new object to the `$$watchers` array. The properties of the new object should be called `exp`, `fn` and `last`. The first two properties should accept the values of the arguments passed to `$watch`, `last` should be set to be equals to **cloned** (`Utils.clone`) value of the value result from evaluation of the expression (`exp`). 47 | 48 | 0. Add method called `$new` - a factory method for creating new scopes. The created scope should inherit prototypically from `this` and its `$id` should be equals to `Scope.counter + 1` (do not forget to increment `Scope.counter`). Add the created scope to the `$$children` array and return it. 49 | 50 | 0. Define method called `$destroy`. It should remove the current scope from the `$$children` array of its parent. 51 | 52 | 0. Define method called `$digest`. Inside the body of the method a loop should iterate over the watchers (`$$watchers`) until all watchers are "clean" (i.e. their current value is equals to their last value - `Utils.equals`). In the end of the method invocation it should be called recursively for all children of the method. If any of the values of the watchers is found dirty, the `fn` (i.e. the observer associated with the current watcher), should be invoked with arguments: 53 | * the current value of the watcher expression 54 | * the previous value of the watcher expression 55 | 56 | 57 | ## DOMCompiler 58 | 59 | 0. Define an object literal called `DOMCompiler`, which has the following public interface: 60 | * `bootstrap` - method responsible for doing the initial parsing of the DOM tree. 61 | * `compile` - method, which accepts a DOM element and scope and compiles the subtree, which has root the passed element, in the context of the passed scope. 62 | 63 | 0. `bootstrap` accepts a single argument - the root element of your application (for example `document.body`) and should invoke `compile` with the `$rootScope` and the root element of your application. 64 | 65 | 0. `compile`, should apply the `link` function of all directives found on the current element and after that should invoke itself recursively with all children elements of the current element. Each registered directive must have two properties: 66 | * `scope` - a boolean property, which indicates that the current directive requires new scope to be created. **NOTE** that no more than one new scope per directive should be created (you should implement this restriction in the `DOMCompiler`). 67 | * `link` - a link function, which is responsible for encapsulating the directive's logic. 68 | 69 | **NOTE** Do not use third party libraries for the implementation of the `DOMCompiler`. For getting all children of given element use: `el.children`. For getting all attributes of given element use `el.attributes`. The returned collection from `el.attributes` will be of type `NamedNodeMap`, which means that you may need to cast it into an array, if necessary. You can access the name of the attribute by: `attr.name` and its value by `attr.value`. 70 | 71 | ## ngl-bind 72 | 73 | Define a directive called `ngl-bind`. When applied to given DOM element as attribute, it should accept an expression as value. When the value of the expression is being changed it should update the content of the element according to the new value of the expression. As initial value of the DOM element `ngl-bind` should set the initial value of the evaluation of the expression. 74 | 75 | ## ngl-model 76 | 77 | Define a directive called `ngl-model`. When applied to given DOM input element as attribute, it should accept name of property of the scope associated with the directive. When the value of the scope's property is being changed this should reflect on the value of the input, once the value of the input is being changed by user interaction this should reflect on the value of the property (i.e. it should implement two-way data-binding). 78 | 79 | ## ngl-controller 80 | 81 | Define a directive called `ngl-controller`. When applied to given DOM element as attribute, as value it should accept name of controller. The link function of this directive should create new controller with name the value of the attribute. The controller should be invoked with local dependencies hash containing property called `$scope` and value the new scope passed to the directive's link function (i.e. the directive should require creation of new scope). 82 | 83 | ## ngl-repeat 84 | 85 | Create new directive called `ngl-repeat`. When applied to given DOM element as attribute it should accept expression in the format `item in items`, as value. When the scope associated with the directive has collection called `items` the directive should iterate over the items in the collection and reference each item in the collection as `item`. The current item should be accessible via the property `item`. For each item in the collection should be created new scope with property called `item`. 86 | 87 | Example: 88 | 89 | When compiled in the context of the scope: 90 | 91 | ```JavaScript 92 | $scope.items = [1, 2, 3]; 93 | ``` 94 | the result of the compilation of the following markup: 95 | 96 | ```HTML 97 | 102 | ``` 103 | should be: 104 | 105 | ```HTML 106 | 117 | 118 | ``` 119 | 120 | ## ngl-click 121 | 122 | Define directive called `ngl-click`. When applied to given DOM element as attribute it should accept expression, expressing function invocation (i.e. `foo()`). 123 | When the user clicks on element on which the `ngl-click` directive is applied the expression associated with the directive should be invoked in the context of the current scope. 124 | 125 | 126 | ### Sample directive 127 | 128 | ```JavaScript 129 | Provider.directive('ngl-click', function () { 130 | 'use strict'; 131 | return { 132 | scope: false, 133 | link: function (el, scope, exp) { 134 | el.onclick = function () { 135 | scope.$eval(exp); 136 | scope.$digest(); 137 | }; 138 | } 139 | }; 140 | }); 141 | ``` 142 | -------------------------------------------------------------------------------- /tasks/09/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tasks/09/src/DOMCompiler.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/tasks/09/src/DOMCompiler.js -------------------------------------------------------------------------------- /tasks/09/src/Provider.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/tasks/09/src/Provider.js -------------------------------------------------------------------------------- /tasks/09/src/Scope.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/tasks/09/src/Scope.js -------------------------------------------------------------------------------- /tasks/09/src/Utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DO NOT USE IN PRODUCTION 3 | */ 4 | var Utils = { 5 | equals: function (a, b) { 6 | 'use strict'; 7 | return JSON.stringify(a) === JSON.stringify(b); 8 | }, 9 | clone: function (a) { 10 | 'use strict'; 11 | try { 12 | return JSON.parse(JSON.stringify(a)); 13 | } catch (e) { 14 | return undefined; 15 | } 16 | } 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /tasks/09/src/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/f290e24efdfac767d98b9e0ba289751f7ba48e2e/tasks/09/src/app.js --------------------------------------------------------------------------------