diff --git a/Additional-Resources/README.md b/Additional-Resources/README.md index d32c7e6..f68a4f5 100755 --- a/Additional-Resources/README.md +++ b/Additional-Resources/README.md @@ -9,6 +9,8 @@ - OOP in JavaScript with ES2015 - [Video Course](https://app.pluralsight.com/library/courses/javascript-es6-object-oriented-programming/table-of-contents) - [Article MDN](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) + - [ES2015 Classes essentials](http://exploringjs.com/es6/ch_classes.html) + - [Rich article on when and why you should avoid ES2015 classes](https://github.com/joshburgess/not-awesome-es6-classes) - Mixins - [Article MDN] (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes#Mix-ins) - [Article](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/) diff --git a/Practical Exams/academy-online-catalogs/README.md b/Practical Exams/academy-online-catalogs/README.md index e7d953c..2d1a280 100644 --- a/Practical Exams/academy-online-catalogs/README.md +++ b/Practical Exams/academy-online-catalogs/README.md @@ -108,7 +108,7 @@ has the following: * Each item **must contain** the pattern as substring either in its **name** or in its **description** * **Returns an empty array**, if **none of the items** contain the pattern * The `pattern` is a string containing at least 1 character - * The search is **case insensitive** + * The search is **case sensitive** ##`BookCatalog` has the following: @@ -157,7 +157,9 @@ has the following: * The media must sorted by: * descending by duration * ascending by id - + * find(options) + * Extends find(options) from parent, but adds a key `rating` + * i.e. books can be found by `id`, `name` and/or `rating` ##Example: @@ -168,11 +170,11 @@ has the following: }, getMedia: function (name, rating, duration, description) { //return a media instance - } + }, getBookCatalog: function (name) { //return a book catalog instance }, getMediaCatalog: function (name) { //return a media catalog instance } - }; \ No newline at end of file + }; diff --git a/Practical Exams/academy-online-catalogs/package.json b/Practical Exams/academy-online-catalogs/package.json index 7e71ad1..66f844e 100644 --- a/Practical Exams/academy-online-catalogs/package.json +++ b/Practical Exams/academy-online-catalogs/package.json @@ -1,14 +1,15 @@ { - "name": "test-driven-hw-mocha", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "mocha -R spec tests" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "chai": "^3.0.0" - } -} + "name": "test-driven-hw-mocha", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "node_modules/mocha/bin/mocha -R spec tests" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "chai": "^3.0.0", + "mocha": "^3.0.2" + } +} \ No newline at end of file diff --git a/Practical Exams/academy-online-catalogs/tasks/solution-es2015-classes-cuki.js b/Practical Exams/academy-online-catalogs/tasks/solution-es2015-classes-cuki.js new file mode 100644 index 0000000..a87ba07 --- /dev/null +++ b/Practical Exams/academy-online-catalogs/tasks/solution-es2015-classes-cuki.js @@ -0,0 +1,279 @@ +function solve() { + const getId = (function() { + let id = 0; + + return function() { + id += 1; + return id; + }; + }()); + + class Item { + constructor(description, name) { + this.id = getId(); + this.description = description; + this.name = name; + } + + get description() { + return this._description; + } + set description(description) { + if(typeof description !== 'string') { + throw 'Description should be a string'; + } + if(description === '') { + throw 'Description should not be empty'; + } + this._description = description; + } + + get name() { + return this._name; + } + set name(name) { + if(typeof name !== 'string') { + throw 'Name should be a string'; + } + if(name.length < 2 || name.length > 40) { + throw 'Name length should be between 2 and 40'; + } + this._name = name; + } + } + + class Book extends Item { + constructor(description, name, isbn, genre) { + super(description, name); + this.isbn = isbn; + this.genre = genre; + } + + get isbn() { + return this._isbn; + } + set isbn(isbn) { + if(typeof isbn !== 'string') { + throw 'Isbn should be a string'; + } + if(isbn.length !== 10 && isbn.length !== 13) { + throw 'Isbn length should be either 10 or 13'; + } + if(!isbn.match(/^[0-9]*$/)) { + throw 'Isbn should be only digits'; + } + + this._isbn = isbn; + } + + get genre() { + return this._genre; + } + set genre(genre) { + if(typeof genre !== 'string') { + throw 'Genre should be a string'; + } + if(genre.length < 2 || genre.length > 20) { + throw 'Genre length should be between 2 and 20'; + } + + this._genre = genre; + } + } + + class Media extends Item { + constructor(description, name, duration, rating) { + super(description, name); + this.duration = duration; + this.rating = rating; + } + + get duration() { + return this._duration; + } + set duration(duration) { + if(typeof duration !== 'number') { + throw 'Duration should be a number'; + } + if(duration <= 0) { + throw 'Duration must be a number greater than 0'; + } + + this._duration = duration; + } + + get rating() { + return this._rating; + } + set rating(rating) { + if(typeof rating !== 'number') { + throw 'Rating should be a number'; + } + if(rating < 1 || rating > 5) { + throw 'Rating must be a number between 1 and 5'; + } + + this._rating = rating; + } + } + + class Catalog { + constructor(name) { + this.id = getId(); + this.name = name; + this.items = []; + } + + get name() { + return this._name; + } + set name(name) { + if(typeof name !== 'string') { + throw 'Name should be a string'; + } + if(name.length < 2 || name.length > 40) { + throw 'Name length should be between 2 and 40'; + } + this._name = name; + } + + add(...items) { + if(Array.isArray(items[0])) { + items = items[0]; + } + + if(items.length === 0) { + throw 'No items are added'; + } + + this.items.push(...items); + + return this; + } + + find(x) { + if(typeof x === 'number') { + for(let item of this.items) { + if(item.id === x) { + return item; + } + } + + return null; + } + + if(x !== null && typeof x === 'object') { + return this.items.filter(function(item) { + return Object.keys(x).every(function(prop) { + return x[prop] === item[prop]; + }); + }); + } + + throw 'Invalid options or id'; + } + + search(pattern) { + if(typeof pattern !== 'string' || pattern === '') { + throw 'Search pattern should be non-empty a string'; + } + + return this.items.filter(function(item) { + return item.name.indexOf(pattern) >= 0 + || item.description.indexOf(pattern) >= 0; + }); + } + } + + class BookCatalog extends Catalog { + constructor(name) { + super(name); + } + + add(...books) { + if(Array.isArray(books[0])) { + books = books[0]; + } + + books.forEach(function(x) { + if(!(x instanceof Book)) { + throw 'Must add only books'; + } + }); + + return super.add(...books); + } + + getGenres() { + let uniq = {}; + this.items.forEach(x => uniq[x.genre] = true); + return Object.keys(uniq); + } + + find(x) { + return super.find(x); + } + } + + class MediaCatalog extends Catalog { + constructor(name) { + super(name); + } + + add(...medias) { + if(Array.isArray(medias[0])) { + medias = medias[0]; + } + + medias.forEach(function(x) { + if(!(x instanceof Media)) { + throw 'Must add only medias'; + } + }); + + return super.add(...medias); + } + + getTop(count) { + if(typeof count !== 'number') { + throw 'Count should be a number'; + } + if(count < 1) { + throw 'Count must be more than 1'; + } + + return this.items + .sort((a, b) => a.rating < b.rating) + .filter((_, ind) => ind < count) + .map(x => ({id: x.id, name: x.name})); + } + + getSortedByDuration() { + return this.items + .sort((a, b) => { + if(a.duration === b.duration) { + return a.id < b.id; + } + + return a.duration > b.duration; + }); + } + } + + return { + getBook(name, isbn, genre, description) { + return new Book(description, name, isbn, genre); + }, + getMedia(name, rating, duration, description) { + return new Media(description, name, duration, rating); + }, + getBookCatalog(name) { + return new BookCatalog(name); + }, + getMediaCatalog(name) { + return new MediaCatalog(name); + } + }; +} + +module.exports = solve; diff --git a/Practical Exams/academy-online-catalogs/tasks/solution-es2015-doncho.js b/Practical Exams/academy-online-catalogs/tasks/solution-es2015-doncho.js new file mode 100644 index 0000000..b12517a --- /dev/null +++ b/Practical Exams/academy-online-catalogs/tasks/solution-es2015-doncho.js @@ -0,0 +1,301 @@ +/* globals module */ + +function solve() { + function* idGenerator() { + let lastId = 0, + forever = true; + + while (forever) { + yield(lastId += 1); + } + } + + let itemsIdGenerator = idGenerator(); + + const validator = { + isStringValid(str, min = 1, max = Number.MAX_VALUE) { + return (typeof str === "string") && + str.length >= min && str.length <= max; + }, + isNumberValid(n, min = 0, max = Number.MAX_VALUE) { + return (typeof n === "number") && min <= n && n <= max; + }, + isNonEmptyArrayWithValidObjects(array, validFunc) { + validFunc = validFunc || function() { + return true; + }; + return Array.isArray(array) && array.length > 0 && + array.every(validFunc); + } + }; + + const MIN_ITEM_NAME_LENGHT = 2, + MAX_ITEM_NAME_LENGHT = 40; + + const ISBN_LENGTH_10 = 10, + ISBN_LENGTH_13 = 13; + + const MIN_GENRE_LENGTH = 2, + MAX_GENRE_LENGTH = 20; + + const MIN_RATING = 1, + MAX_RATING = 5; + + const MIN_CATALOG_NAME_LENGHT = 2, + MAX_CATALOG_NAME_LENGTH = 40; + + const MIN_GET_TOP_COUNT = 1, + MIN_PATTERN_LENGTH = 1; + + class Item { + constructor(name, description) { + this.id = itemsIdGenerator.next().value; + this.name = name; + this.description = description; + } + + get name() { + return this._name; + } + + set name(name) { + if (!validator.isStringValid(name, MIN_ITEM_NAME_LENGHT, MAX_ITEM_NAME_LENGHT)) { + throw new Error("Invalid name"); + } + + this._name = name; + } + + get description() { + return this._description; + } + + set description(description) { + if (!validator.isStringValid(description)) { + throw new Error("Invalid description"); + } + + this._description = description; + } + } + + class Book extends Item { + constructor(name, isbn, genre, description) { + super(name, description); + this.isbn = isbn; + this.genre = genre; + } + + get isbn() { + return this._isbn; + } + + set isbn(isbn) { + if (!validator.isStringValid(isbn, ISBN_LENGTH_10, ISBN_LENGTH_10) && + !validator.isStringValid(isbn, ISBN_LENGTH_13, ISBN_LENGTH_13)) { + throw new Error("Invalid ISBN"); + } + + this._isbn = isbn; + } + + get genre() { + return this._genre; + } + + set genre(genre) { + if (!validator.isStringValid(genre, MIN_GENRE_LENGTH, MAX_GENRE_LENGTH)) { + throw new Error("Invalid genre"); + } + + this._genre = genre; + } + } + + class Media extends Item { + constructor(name, rating, duration, description) { + super(name, description); + this.rating = rating; + this.duration = duration; + } + + get rating() { + return this._rating; + } + + set rating(rating) { + if (!validator.isNumberValid(rating, MIN_RATING, MAX_RATING)) { + throw new Error("Invalid rating"); + } + + this._rating = rating; + } + + get duration() { + return this._duration; + } + + set duration(duration) { + if (!validator.isNumberValid(duration, 1)) { + throw new Error("Invalid duration"); + } + + this._duration = duration; + } + } + + let catalogsIdGenerator = idGenerator(); + + class Catalog { + constructor(name) { + this.id = catalogsIdGenerator.next().value; + this.name = name; + this.items = []; + } + + get name() { + return this._name; + } + + set name(name) { + if (!validator.isStringValid(name, MIN_CATALOG_NAME_LENGHT, MAX_CATALOG_NAME_LENGTH)) { + throw new Error("Invalid Catalog name"); + } + + this._name = name; + } + + add(...items) { + if (Array.isArray(items[0])) { + items = items[0]; + } + + if (!validator.isNonEmptyArrayWithValidObjects(items, this._itemLikeObjectValidation)) { + throw new Error("Invalid items"); + } + + this.items.push(...items); + + return this; + } + + find(options) { + if (typeof options === "undefined") { + throw new Error("Invalid find options"); + } + + let isSingleResult = false; + if (typeof options === "number") { + options = { + id: options + }; + isSingleResult = true; + } + + if (typeof options !== "object") { + throw new Error("Invalid options"); + } + + let result = this.items.filter(item => { + return Object.keys(options) + .every(key => item[key] === options[key]); + }); + + return (!isSingleResult) ? result : (result.length) ? result[0] : null; + } + + search(pattern) { + if (!validator.isStringValid(pattern, MIN_PATTERN_LENGTH)) { + throw new Error("Invalid pattern"); + } + + pattern = pattern.toLowerCase(); + + return this.items.filter(item => + item.name.toLowerCase().includes(pattern) || + item.description.toLowerCase().includes(pattern)); + } + + _itemLikeObjectValidation(item) { + + return (item instanceof Item) || + (typeof item.id === "number" && + typeof item.name === "string" && + typeof item.description === "string"); + } + } + + class BookCatalog extends Catalog { + constructor(name) { + super(name); + } + + _itemLikeObjectValidation(item) { + return super._itemLikeObjectValidation(item) && + ((item instanceof Book) || + (typeof item.isbn === "string" && + typeof item.genre === "string")); + } + + getGenres() { + let genres = {}; + this.items.forEach(item => { + genres[item.genre] = true; + }); + + return Object.keys(genres); + } + } + + class MediaCatalog extends Catalog { + constructor(name) { + super(name); + } + + getTop(count) { + if (typeof count !== "number" || count < MIN_GET_TOP_COUNT) { + throw new Error("Invalid count"); + } + return this.items.sort((x, y) => x.rating - y.rating) + .slice(0, count) + .map(item => { + return { + id: item.id, + name: item.name + }; + }); + } + + getSortedByDuration() { + return [...this.items] + .sort((x, y) => (0 === y.duration - x.duration) ? y.duration - x.duration : x.id - y.id); + } + + _itemLikeObjectValidation(item) { + + return super._itemLikeObjectValidation(item) && + ((item instanceof Media) || + (typeof item.rating === "number" && + typeof item.duration === "number")); + } + + } + + function getBook(name, isbn, genre, description) { + return new Book(name, isbn, genre, description); + } + + function getMedia(name, rating, duration, description) { + return new Media(name, rating, duration, description); + } + + function getBookCatalog(name) { + return new BookCatalog(name); + } + + function getMediaCatalog(name) { + return new MediaCatalog(name); + } + return { getBook, getMedia, getBookCatalog, getMediaCatalog }; +} +module.exports = solve; \ No newline at end of file diff --git a/Practical Exams/academy-online-catalogs/tasks/solution.js b/Practical Exams/academy-online-catalogs/tasks/solution.js new file mode 100644 index 0000000..e3c1fb8 --- /dev/null +++ b/Practical Exams/academy-online-catalogs/tasks/solution.js @@ -0,0 +1,18 @@ +function solve() { + return { + getBook: function (name, isbn, genre, description) { + // return a book instance + }, + getMedia: function (name, rating, duration, description) { + // return a media instance + }, + getBookCatalog: function (name) { + // return a book catalog instance + }, + getMediaCatalog: function (name) { + // return a media catalog instance + } + }; +} + +module.exports = solve; diff --git a/Practical Exams/academy-online-catalogs/tests/tests-solution.js b/Practical Exams/academy-online-catalogs/tests/tests-solution.js index 64a6357..2e3efa6 100644 --- a/Practical Exams/academy-online-catalogs/tests/tests-solution.js +++ b/Practical Exams/academy-online-catalogs/tests/tests-solution.js @@ -1,9 +1,11 @@ /* globals require, describe, it */ var expect = require('chai').expect, - result = require('../tasks/solution')(); + // result = require("../tasks/solution-es2015-doncho")(); + + result = require("../tasks/solution-es2015-doncho")(); /* beforeach: start */ -var utils = (function () { +var utils = (function() { var CONSTS = { NAME: { MIN: 2, @@ -36,7 +38,7 @@ var utils = (function () { }; function getRandom(min, max) { - if (typeof (max) === 'undefined') { + if (typeof(max) === 'undefined') { max = min; min = 0; } @@ -48,84 +50,84 @@ var utils = (function () { function getRandomString(chars, length) { return Array.apply(null, { length: length - }).map(function () { + }).map(function() { return chars[getRandom(chars.length)]; }).join(''); } var utils = { valid: { - getName: function () { + getName: function() { var length = getRandom(CONSTS.NAME.MIN, CONSTS.NAME.MAX); return getRandomString(CONSTS.CHARS, length); }, - getISBN10: function () { + getISBN10: function() { var length = 10; return getRandomString(CONSTS.DIGIS, length); }, - getISBN13: function () { + getISBN13: function() { var length = 13; return getRandomString(CONSTS.DIGIS, length); }, - getGenre: function () { + getGenre: function() { var length = getRandom(CONSTS.GENRE.MIN, CONSTS.GENRE.MAX); return getRandomString(CONSTS.CHARS, length); }, - getDescription: function () { + getDescription: function() { var length = getRandom(CONSTS.DESCRIPTION.MIN, CONSTS.DESCRIPTION.MAX); return getRandomString(CONSTS.CHARS, length); }, - getDuration: function () { + getDuration: function() { return getRandom(0, 1000); }, - getRating: function () { + getRating: function() { return getRandom(1, 5); } }, invalid: { - getShorterName: function () { + getShorterName: function() { var length = getRandom(0, CONSTS.NAME.MIN - 1); return getRandomString(CONSTS.CHARS, length); }, - getLongerName: function () { + getLongerName: function() { var length = getRandom(CONSTS.NAME.MAX + 1, CONSTS.NAME.MAX * 2); return getRandomString(CONSTS.CHARS, length); }, - getInvalidISBN10WithLetters: function () { + getInvalidISBN10WithLetters: function() { var isbn = utils.valid.getISBN10().split(''), index = getRandom(isbn.length); isbn.splice(index, 1, 'a'); return isbn; }, - getInvalidISBN13WithLetters: function () { + getInvalidISBN13WithLetters: function() { return utils.valid.getISBN13().substring(1); }, - getInvalidISBNNot10or13: function () { + getInvalidISBNNot10or13: function() { var isbn = utils.valid.getISBN13().split(''), index = getRandom(isbn.length); isbn.splice(index, 1, 'a'); return isbn; }, - getShorterDescription: function () { + getShorterDescription: function() { var length = getRandom(0, CONSTS.DESCRIPTION.MIN - 1); return getRandomString(CONSTS.CHARS, length); }, - getLongerDescription: function () { + getLongerDescription: function() { var length = getRandom(CONSTS.DESCRIPTION.MAX + 1, CONSTS.DESCRIPTION.MAX * 2); return getRandomString(CONSTS.CHARS, length); }, - getShorterGenre: function () { + getShorterGenre: function() { var length = getRandom(0, CONSTS.GENRE.MIN - 1); return getRandomString(CONSTS.CHARS, length); }, - getLongerGenre: function () { + getLongerGenre: function() { var length = getRandom(CONSTS.GENRE.MAX + 1, CONSTS.GENRE.MAX * 2); return getRandomString(CONSTS.CHARS, length); }, - getSmallRating: function () { + getSmallRating: function() { return 0; }, - getLargeRating: function () { + getLargeRating: function() { return getRandom(6, 1000); } } @@ -135,11 +137,11 @@ var utils = (function () { }()); /* beforeach: end */ -describe('Academy Catalogs', function () { - describe('Book tests', function () { - describe('Valid tests', function () { +describe('Academy Catalogs', function() { + describe('Book tests', function() { + describe('Valid tests', function() { // test 1 - it('expect getBook to exist, to be a function and to return object with properties unique id, name, description, isbn and ganre', function () { + it('expect getBook to exist, to be a function and to return object with properties unique id, name, description, isbn and genre', function () { var name, isbn, genre, @@ -184,11 +186,12 @@ describe('Academy Catalogs', function () { } }); }); - describe('Invalid tests', function () { + describe('Invalid tests', function() { // test 2 - it('Expect invalid name to throw', function () { + it('Expect invalid name to throw', function() { expect(result.getBook).to.exist; + function testName_undefined() { result.getBook(undefined, utils.valid.getISBN10(), utils.valid.getGenre(), utils.valid.getDescription()); } @@ -251,7 +254,7 @@ describe('Academy Catalogs', function () { expect(testNameSetter_Long).to.throw(); }); // test 3 - it('Expect invalid isbn to throw', function () { + it('Expect invalid isbn to throw', function() { function testISBN_undefined() { result.getBook(utils.valid.getName(), undefined, utils.valid.getGenre(), utils.valid.getDescription()); } @@ -321,7 +324,7 @@ describe('Academy Catalogs', function () { expect(testISBNSetter_not10or13).to.throw(); }); // test 4 - it('Expect invalid genre to throw', function () { + it('Expect invalid genre to throw', function() { function testGenre_undefined() { result.getBook(utils.valid.getName(), utils.valid.getISBN10(), undefined, utils.valid.getDescription()); } @@ -384,7 +387,7 @@ describe('Academy Catalogs', function () { expect(testGenreSetter_Long).to.throw(); }); // test 5 - it('Expect invalid description to throw', function () { + it('Expect invalid description to throw', function() { function testDescription_undefined() { result.getBook(utils.valid.getName(), utils.valid.getISBN10(), utils.valid.getGenre(), undefined); @@ -424,10 +427,10 @@ describe('Academy Catalogs', function () { }); }); - describe('Media tests', function () { - describe('Valid tests', function () { + describe('Media tests', function() { + describe('Valid tests', function() { // test 6 - it('expect getMedia to exist, to be a function and to return object with properties unique id, name, description, duration and rating', function () { + it('expect getMedia to exist, to be a function and to return object with properties unique id, name, description, duration and rating', function() { var name, duration, rating, @@ -469,9 +472,9 @@ describe('Academy Catalogs', function () { } }); }); - describe('Invalid tests', function () { + describe('Invalid tests', function() { // test 7 - it('Expect invalid name to throw', function () { + it('Expect invalid name to throw', function() { function testName_undefined() { /*jshint ignore: start */ @@ -574,7 +577,7 @@ describe('Academy Catalogs', function () { expect(testNameSetter_Long).to.throw(); }); // test 8 - it('Expect invalid description to throw', function () { + it('Expect invalid description to throw', function() { function testDescription_undefined() { var name = utils.valid.getName(); @@ -636,7 +639,7 @@ describe('Academy Catalogs', function () { expect(testDescriptionSetter_EmptyString).to.throw(); }); // test 9 - it('Expect invalid rating to throw', function () { + it('Expect invalid rating to throw', function() { function testRating_undefined() { var name = utils.valid.getName(); @@ -718,7 +721,7 @@ describe('Academy Catalogs', function () { expect(testRatingSetter_Large).to.throw(); }); // test 10 - it('Expect invalid duration to throw', function () { + it('Expect invalid duration to throw', function() { function testDuration_undefined() { var name = utils.valid.getName(); @@ -782,14 +785,15 @@ describe('Academy Catalogs', function () { }); }); - describe('BookCatalog tests', function () { - describe('Valid tests', function () { + describe('BookCatalog tests', function() { + describe('Valid tests', function() { // test 11 beforeEach(function(done) { - result = require('../tasks/solution')(); + // result = require("../tasks/solution-es2015-doncho")(); + result = require("../tasks/solution-es2015-doncho")(); done(); }); - it('expect getBookCatalog to exist, to be a function and to return object with properties name and unique id and methods: add(), find() with 1 param and search() with 1 param', function () { + it('expect getBookCatalog to exist, to be a function and to return object with properties name and unique id and methods: add(), find() with 1 param and search() with 1 param', function() { var name = utils.valid.getName(), catalog = result.getBookCatalog(name); @@ -818,7 +822,7 @@ describe('Academy Catalogs', function () { expect(catalog.getGenres.length).to.equal(0); }); // test 12 - it('expect bookCatalog.add() to add books only and to work with array or books separated with comma', function () { + it('expect bookCatalog.add() to add books only and to work with array or books separated with comma', function() { var catalog, name, description, @@ -855,7 +859,7 @@ describe('Academy Catalogs', function () { expect(catalog.items[5]).to.equal(books[2]); }); // test 13 - it('expect bookCatalog.getGenres() to get unique genres', function () { + it('expect bookCatalog.getGenres() to get unique genres', function() { var catalog, genre, book, @@ -904,10 +908,10 @@ describe('Academy Catalogs', function () { catalog = result.getBookCatalog(utils.valid.getName()); findResult = catalog.getGenres(); expect(findResult).to.exits; - expect(findResult.length).to.equal(0);//*/ + expect(findResult.length).to.equal(0); //*/ }); // test 14 - it('expect bookCatalog.find() by id to find the leftmost book in the items array or return null', function () { + it('expect bookCatalog.find() by id to find the leftmost book in the items array or return null', function() { var catalog, book, i, @@ -974,7 +978,7 @@ describe('Academy Catalogs', function () { expect(testFindID_string).to.throw(); }); // test 15 - it('expect bookCatalog.find() by options to find an array of book in the items array or return null', function () { + it('expect bookCatalog.find() by options to find an array of book in the items array or return null', function() { var catalog, book, findResult, @@ -990,10 +994,10 @@ describe('Academy Catalogs', function () { } expect(testFindById_Undefined).to.throw(); - expect(catalog.find({name: 'nonexistent'})).to.exits; - expect(Array.isArray(catalog.find({name: 'nonexistent'}))).to.be.true; - expect(catalog.find({name: 'nonexistent'}).length).to.exits; // it is an array-like object - expect(catalog.find({name: 'nonexistent'}).length).to.equal(0); + expect(catalog.find({ name: 'nonexistent' })).to.exits; + expect(Array.isArray(catalog.find({ name: 'nonexistent' }))).to.be.true; + expect(catalog.find({ name: 'nonexistent' }).length).to.exits; // it is an array-like object + expect(catalog.find({ name: 'nonexistent' }).length).to.equal(0); // test with one book book = { @@ -1004,7 +1008,7 @@ describe('Academy Catalogs', function () { description: utils.valid.getDescription() }; catalog.items.push(book); - findResult = catalog.find({id: id}); + findResult = catalog.find({ id: id }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); expect(findResult[0]).to.equal(book); @@ -1023,22 +1027,22 @@ describe('Academy Catalogs', function () { catalog.items.push(book); } - findResult = catalog.find({name: 'myName'}); + findResult = catalog.find({ name: 'myName' }); expect(findResult).to.exits; expect(findResult.length).to.equal(len); - findResult = catalog.find({id: 2 + len, name: 'myName'}); + findResult = catalog.find({ id: 2 + len, name: 'myName' }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); expect(findResult[0]).to.equal(books[2]); // test search by genre - findResult = catalog.find({genre: 'generic1'}); + findResult = catalog.find({ genre: 'generic1' }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); }); // test 16 - it('expect bookCatalog.search() to return an array of found items or empty array', function () { + it('expect bookCatalog.search() to return an array of found items or empty array', function() { var i, catalog, book, @@ -1073,14 +1077,14 @@ describe('Academy Catalogs', function () { catalog.items.push(book); } pattern = catalog.items[0].name.substr(1, 3); - var matchingBooks = catalog.items.filter(function (book) { + var matchingBooks = catalog.items.filter(function(book) { return book.name.indexOf(pattern) >= 0 || book.description.indexOf(pattern) >= 0; }); expect(catalog.search(pattern)).to.eql(matchingBooks); }); // test 17 - it('Expect bookCatalog.search() to return empty array, when no books in catalog and when no books that contain the pattern ', function () { + it('Expect bookCatalog.search() to return empty array, when no books in catalog and when no books that contain the pattern ', function() { var catalog, i, pattern, @@ -1101,13 +1105,13 @@ describe('Academy Catalogs', function () { expect(catalog.search(pattern)).to.eql([]); }); }); - describe('Invalid tests', function () { + describe('Invalid tests', function() { // test 18 beforeEach(function(done) { - result = require('../tasks/solution')(); + result = require("../tasks/solution-es2015-doncho")(); done(); }); - it('Expect bookCatalog.search() to throw if pattern is undefined, null or empty string ', function () { + it('Expect bookCatalog.search() to throw if pattern is undefined, null or empty string ', function() { var catalog = result.getBookCatalog(utils.valid.getName()); function testSearch_Undefined() { @@ -1127,7 +1131,7 @@ describe('Academy Catalogs', function () { expect(testSearch_EmptyString).to.throw(); }); // test 19 - it('expect invalid name to throw', function () { + it('expect invalid name to throw', function() { var catalog, name, count, @@ -1205,7 +1209,7 @@ describe('Academy Catalogs', function () { expect(testNameSetter_Long).to.throw(); }); // test 20 - it('expect bookCatalog.add() to throw', function () { + it('expect bookCatalog.add() to throw', function() { var duration, rating, description, @@ -1252,14 +1256,14 @@ describe('Academy Catalogs', function () { }); }); }); - describe('MediaCatalog tests', function () { - describe('Valid tests', function () { + describe('MediaCatalog tests', function() { + describe('Valid tests', function() { // test 21 beforeEach(function(done) { - result = require('../tasks/solution')(); + result = require("../tasks/solution-es2015-doncho")(); done(); }); - it('expect getMediaCatalog to exist, to be a function and to return object with properties name and unique id and methods: add(), find() with 1 param and search() with 1 param', function () { + it('expect getMediaCatalog to exist, to be a function and to return object with properties name and unique id and methods: add(), find() with 1 param and search() with 1 param', function() { var name = utils.valid.getName(), catalog = result.getMediaCatalog(name); @@ -1292,7 +1296,7 @@ describe('Academy Catalogs', function () { expect(catalog.getSortedByDuration.length).to.equal(0); }); // test 22 - it('expect mediaCatalog.add() to add media only and to work with array or media separated with comma', function () { + it('expect mediaCatalog.add() to add media only and to work with array or media separated with comma', function() { var catalog, name, description, @@ -1329,7 +1333,7 @@ describe('Academy Catalogs', function () { expect(catalog.items[5]).to.equal(medias[2]); }); // test 23 - it('expect mediaCatalog.getTop() to get unique genres', function () { + it('expect mediaCatalog.getTop() to get unique genres', function() { var catalog, name, rating, @@ -1398,7 +1402,7 @@ describe('Academy Catalogs', function () { expect(testGetTop_ltOne).to.throw(); }); // test 24 - it('expect mediaCatalog.find() by id to find the leftmost media in the items array or return null', function () { + it('expect mediaCatalog.find() by id to find the leftmost media in the items array or return null', function() { var catalog, media, book, @@ -1458,10 +1462,10 @@ describe('Academy Catalogs', function () { expect(testFindID_undefined).to.throw(); expect(testFindID_null).to.throw(); - expect(testFindID_string).to.throw();//*/ + expect(testFindID_string).to.throw(); //*/ }); // test 25 - it('expect mediaCatalog.find() by options to find an array of media in the items array or return null', function () { + it('expect mediaCatalog.find() by options to find an array of media in the items array or return null', function() { var catalog, media, findResult, @@ -1477,10 +1481,10 @@ describe('Academy Catalogs', function () { } expect(testFindById_Undefined).to.throw(); - expect(catalog.find({name: 'nonexistent'})).to.exits; - expect(Array.isArray(catalog.find({name: 'nonexistent'}))).to.be.true; - expect(catalog.find({name: 'nonexistent'}).length).to.exits; // it is an array-like object - expect(catalog.find({name: 'nonexistent'}).length).to.equal(0); + expect(catalog.find({ name: 'nonexistent' })).to.exits; + expect(Array.isArray(catalog.find({ name: 'nonexistent' }))).to.be.true; + expect(catalog.find({ name: 'nonexistent' }).length).to.exits; // it is an array-like object + expect(catalog.find({ name: 'nonexistent' }).length).to.equal(0); // test with one book media = { @@ -1491,7 +1495,7 @@ describe('Academy Catalogs', function () { description: utils.valid.getDescription() }; catalog.items.push(media); - findResult = catalog.find({id: id}); + findResult = catalog.find({ id: id }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); expect(findResult[0]).to.equal(media); @@ -1510,22 +1514,22 @@ describe('Academy Catalogs', function () { catalog.items.push(media); } - findResult = catalog.find({name: 'myName'}); + findResult = catalog.find({ name: 'myName' }); expect(findResult).to.exits; expect(findResult.length).to.equal(len); - findResult = catalog.find({id: 2 + len, name: 'myName'}); + findResult = catalog.find({ id: 2 + len, name: 'myName' }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); expect(findResult[0]).to.equal(medias[2]); // test search by genre - findResult = catalog.find({rating: 4}); + findResult = catalog.find({ rating: 4 }); expect(findResult).to.exits; expect(findResult.length).to.equal(1); }); // test 26 - it('expect mediaCatalog.search() to return an array of found items or empty array', function () { + it('expect mediaCatalog.search() to return an array of found items or empty array', function() { var i, catalog, media, @@ -1560,14 +1564,14 @@ describe('Academy Catalogs', function () { catalog.items.push(media); } pattern = catalog.items[0].name.substr(1, 3); - var matchingMedia = catalog.items.filter(function (media) { + var matchingMedia = catalog.items.filter(function(media) { return media.name.indexOf(pattern) >= 0 || media.description.indexOf(pattern) >= 0; }); expect(catalog.search(pattern)).to.eql(matchingMedia); }); // test 27 - it('Expect mediaCatalog.search() to return empty array, when no media in catalog and when no media that contain the pattern ', function () { + it('Expect mediaCatalog.search() to return empty array, when no media in catalog and when no media that contain the pattern ', function() { var catalog, i, pattern, @@ -1589,13 +1593,13 @@ describe('Academy Catalogs', function () { expect(catalog.search(pattern)).to.eql([]); }); }); - describe('Invalid tests', function () { + describe('Invalid tests', function() { // test 28 beforeEach(function(done) { - result = require('../tasks/solution')(); + result = require("../tasks/solution-es2015-doncho")(); done(); }); - it('Expect mediaCatalog.search() to throw if pattern is undefined, null or empty string ', function () { + it('Expect mediaCatalog.search() to throw if pattern is undefined, null or empty string ', function() { var catalog = result.getMediaCatalog(utils.valid.getName()); function testSearch_Undefined() { @@ -1615,7 +1619,7 @@ describe('Academy Catalogs', function () { expect(testSearch_EmptyString).to.throw(); }); // test 29 - it('expect invalid name to throw', function () { + it('expect invalid name to throw', function() { var catalog, name, count, @@ -1693,7 +1697,7 @@ describe('Academy Catalogs', function () { expect(testNameSetter_Long).to.throw(); }); // test 30 - it('expect mediaCatalog.add() to throw', function () { + it('expect mediaCatalog.add() to throw', function() { var duration, rating, description, @@ -1740,4 +1744,4 @@ describe('Academy Catalogs', function () { }); }); }); -}); \ No newline at end of file +}); diff --git a/Sample Exams/audio-player/package.json b/Sample Exams/audio-player/package.json index 996938b..71a5702 100644 --- a/Sample Exams/audio-player/package.json +++ b/Sample Exams/audio-player/package.json @@ -4,11 +4,12 @@ "description": "", "main": "index.js", "scripts": { - "test": "mocha -R spec tests" + "test": "node_modules/mocha/bin/mocha -R spec tests" }, "author": "", "license": "ISC", "devDependencies": { - "chai": "^3.0.0" + "chai": "^3.0.0", + "mocha": "^3.0.2" } -} \ No newline at end of file +} diff --git a/Sample Exams/audio-player/tests/task-1-tests-playlist.js b/Sample Exams/audio-player/tests/task-1-tests-playlist.js index b079b0d..22f1c1c 100644 --- a/Sample Exams/audio-player/tests/task-1-tests-playlist.js +++ b/Sample Exams/audio-player/tests/task-1-tests-playlist.js @@ -36,7 +36,7 @@ describe('Sample exam tests', function () { it('expect playlist.addPlayable() to exists, to be a function and to take a single parameter and to enable chaining', function () { var name = 'Rock and roll', playlist = result.getPlaylist(name), - playable = {id: 1, name: 'Banana Rock', author: 'Wombles'}; + playable = {id: 1, title: 'Banana Rock', author: 'Wombles'}; expect(playlist.addPlayable).to.exist; expect(playlist.addPlayable).to.be.a('function'); @@ -58,7 +58,7 @@ describe('Sample exam tests', function () { var returnedPlayable, name = 'Rock and roll', playlist = result.getPlaylist(name), - playable = {id: 1, name: 'Banana Rock', author: 'Wombles'}; + playable = {id: 1, title: 'Banana Rock', author: 'Wombles'}; returnedPlayable = playlist.addPlayable(playable).getPlayableById(1); expect(returnedPlayable.id).to.equal(playable.id); @@ -80,7 +80,7 @@ describe('Sample exam tests', function () { plName = 'Banana Rock', plAuthor = 'Wombles', playlist = result.getPlaylist(name), - playable = {id: 1, name: plName, author: plAuthor}; + playable = {id: 1, title: plName, author: plAuthor}; playlist.addPlayable(playable); playlist.removePlayable(playable); @@ -110,7 +110,7 @@ describe('Sample exam tests', function () { playlist = result.getPlaylist(name); for (i = 0; i < 35; i += 1) { - playlist.addPlayable({id: (i + 1), name: 'Rock' + (9 - (i % 10))}); + playlist.addPlayable({id: (i + 1), title: 'Rock' + (9 - (i % 10))}); } expect(playlist.listPlayables(2, 10).length).to.equal(10); diff --git a/Topics/01. JavaScript-Transpilers-and-Babel/README.md b/Topics/01. JavaScript-Transpilers-and-Babel/README.md index 7716e5a..fbfceb0 100644 --- a/Topics/01. JavaScript-Transpilers-and-Babel/README.md +++ b/Topics/01. JavaScript-Transpilers-and-Babel/README.md @@ -47,7 +47,7 @@ - `babel-core` - for advanced scenarios - `babel-cli` - in order to run babel from the command line -- Separate packages for each EcmaScript2015 feature in Bable6 +- Separate packages for each EcmaScript2015 feature in Babel6 - Turn on individual features **babel-plugin-* ** - _Example_: `babel-plugin-transform-es2015-arrow-functions` - Or use **babel-presets-* ** diff --git a/Topics/03. Closures-and-Scope/homework/README.md b/Topics/03. Closures-and-Scope/homework/README.md index 4ae6500..bd186bb 100644 --- a/Topics/03. Closures-and-Scope/homework/README.md +++ b/Topics/03. Closures-and-Scope/homework/README.md @@ -16,7 +16,7 @@ - **List all categories** - Return an array of categories - Categories are sorted by ID - - **Each book/catagory has a unique identifier (ID) that is a number greater than 1** + - **Each book/category has a unique identifier (ID) that is a number greater than 1** - When adding a book/category, the ID is generated automatically - **Add validation everywhere, where possible** - Book title and category name must be between 2 and 100 characters, including letters, digits and special characters ('!', ',', '.', etc) diff --git a/Topics/06. Class-Inheritance/demos/demo.js b/Topics/06. Class-Inheritance/demos/demo.js deleted file mode 100644 index 2c02d83..0000000 --- a/Topics/06. Class-Inheritance/demos/demo.js +++ /dev/null @@ -1 +0,0 @@ -console.log('06. Class-Inheritance'); diff --git a/Topics/06. Class-Inheritance/demos/pet-store.js b/Topics/06. Class-Inheritance/demos/pet-store.js new file mode 100644 index 0000000..658a0da --- /dev/null +++ b/Topics/06. Class-Inheritance/demos/pet-store.js @@ -0,0 +1,109 @@ +const petStore = (function () { + + class Animal { + constructor(name, age, sound) { + this.name = name; + this.age = age; + this.sound = sound; + } + + get name() { + return this._name; + } + + set name(value) { + if(typeof value !== 'string' || value.length < 2) { + throw new Error('Name must be string value with at least 2 symbols!'); + } + + this._name = value; + } + + get age() { + return this._age; + } + + set age(value) { + if(value < 0) { + throw new Error('Age cannot be negative number!'); + } + + this._age = value; + } + + get sound() { + return this._sound; + } + + set sound(value) { + if(typeof value !== 'string') { + throw new Error('Sound must be string!'); + } + + this._sound = value; + } + + makeSound() { + console.log(this.sound); + } + + toString() { + return `${this.name} is ${this.age} years old` + } + } + + class Cat extends Animal { + constructor(name, age, color) { + super(name, age, 'mew'); + + this.color = color; + } + + toString() { + return `${super.toString()} ${this.color} cat`; + } + } + + class MythicalDragonHydra extends Animal { + constructor(name, age, headsCount) { + super(name, age, 'RAWRWRWR'); + + if(headsCount < 2) { + throw new Error('MythicalDragonHydra must have at least 2 heads!'); + } + + this._headsCount = headsCount; + } + + get headsCount() { + return this._headsCount; + } + + growHead() { + this._headsCount += 1; + this.makeSound(); + } + + toString() { + return `${super.toString()} and is a mythic dragon hydra with ${this.headsCount} heads!`; + } + } + + return { + getCat: function(name, age, color) { + return new Cat(name, age, color); + }, + getHydra: function(name, age, headsCount) { + return new MythicalDragonHydra(name, age, headsCount); + } + }; +} ()); + +const davinci = petStore.getCat('davinci', 2, 'gray'), + petko = petStore.getHydra('petko', 1000, 5); + +davinci.makeSound(); +console.log(davinci.toString()); + +petko.makeSound(); +console.log(petko.toString()); \ No newline at end of file diff --git a/Topics/07. Mixins/README.md b/Topics/07. Mixins/README.md index 26fea32..84796c6 100644 --- a/Topics/07. Mixins/README.md +++ b/Topics/07. Mixins/README.md @@ -44,7 +44,7 @@ printName() { console.log(`My name is ${this.name}`); } - } + }; ``` - Create your class: diff --git a/Topics/08. ES6-Features/README.md b/Topics/08. ES6-Features/README.md index f6bbfe3..8ba738c 100644 --- a/Topics/08. ES6-Features/README.md +++ b/Topics/08. ES6-Features/README.md @@ -1,41 +1,59 @@ - - + +# ES 2015 Features +## The new cool stuff in JS +
+

JavaScript OOP

+

Telerik Software Academy

+ http://academy.telerik.com +
+ # Table of Contents - JavaScript History - The ECMAScript standard - Variables - - var, let, const + - `var`, `let`, `const` - Data Structures - `Set` and `WeakSet` - `Map` and `WeakMap` - Async operations - Promises - Callbacks with arrow functions + + +# Table of Contents - Modules - - imports, exports, compitability + - imports, exports, compatibility - Strings - templated strings - `repeat()`, `startsWith()`, `endsWith()`, `includes()` - Numbers - Binary, Octal and hexadecimal literals - Math methods + + +# Table of Contents + - Functions - - Arraw functions + - Arrow functions - Preserving `this` - Generators - Arrays - `Array.of()` - `Array.from()` - spread operator + + +# Table of Contents + - Data Types - Symbols - Objects - - `Object.asign()` + - `Object.assign()` - Iterators - Properties - Destructuring @@ -46,12 +64,8 @@ - Default - rest operator - - - - # JavaScript History - **JavaScript** is a front-end scripting language developed by Netscape for dynamic content @@ -59,34 +73,31 @@ - Can be used as object-oriented language - Embedded in your HTML page - Interpreted by the Web browser -- **Client-side**, **mobile**and**desktop** technology +- **Client-side**, **mobile** and **desktop** technology - Simple and flexible - Powerful to manipulate the DOM - - - - -# Using JavaScript.next -- There are a few ways to use JavaScript.next today - - Enable tags in Chrome and Firefox - - Compile to JavaScript 5 using Traceur or Babel -- A compatibility table for ES6 support can be found at https://kangax.github.io/compat-table/es6/ - + +# Using ES 2015 +- There are a few ways to use ES 2015 today: + - Most browsers already support it + - Node.js 6.X supports it + - For support for older browsers (IE 8, 9): + - Transpilers: **Babel**, **Traceur** +- A compatibility table for ES6 support can be found at https://kangax.github.io/compat-table/es6/ - # ES6 Variables - ES6 introduces new ways to declare variables: - - let – creates a scope variable - - Accessible only in its scope + - `let` – creates a scoped variable + - Accessible only in its own scope ```javascript for(let number of [1, 2, 3, 4]){ @@ -95,7 +106,7 @@ for(let number of [1, 2, 3, 4]){ //accessing number here throws exception ``` - - const – creates a constant variable + - `const` – creates a constant variable - Its value is read-only and cannot be changed ```javascript @@ -105,50 +116,63 @@ MAX_VALUE = 15; // throws exception - - - - - -# For-of loop +# Loops -```javascript -The for-of loop iterates over the values - Of an array -``` +- ES 2015 has a new kind of `for` loop + - `for-of` iterates though the members of an array: -- function* generator(maxValue){ -- for(let i = 0; i < maxValue; i+=1) { -- yield i; -- } -- } -- let iter = generator(10); -- for(let val of iter()){ -- console.log(val); -- } + ```javascript + let numbers = [1, 2, 3, 5]; + for(let number of numbers) { + console.log(`The number is ${number}`); + } + ``` -```javascript -let sum = 0; -for(let number of [1, 2, 3]) { - sum += number; } -``` + +# Loops + - Or iteratable objects (objects that have `Symbol.iterator`): + - Discussed in detail later -```javascript - Of An iteratable object -``` + ```javascript + let fib = { + [Symbol.iterator]() { + //magic code to return next Fibonacci number + } + }; + let i = 0; + for (let number of obj) { + if (i > 10) { + break; + } + i += 1; + console.log(number); + } + ``` + +# Data Structures +- ES6 supports maps and sets natively + - They do pretty much the same as associative arrays, but in cleaner way: + ```javascript + let names = new Set(); + names.add('Doncho'); + names.add('Nikolay'); + names.add('Ivaylo'); + names.add('Evlogi'); + names.add('Doncho'); // won't be added + ``` - + # Templated Strings in ES6 @@ -169,144 +193,16 @@ for (let person of people){ - - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - - - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - -
Constructor of the class
- - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - -
Getters and setters
-
Constructor of the class
- - - - - - - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - -```javascript -numbers.sort((a, b) => b – a); -``` - - -```javascript -Becomes -``` - - - # Arrow Functions - Arrow functions easify the creation of functions: -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - ```javascript numbers.sort((a, b) => b – a); ``` - ```javascript -var fullnames = +var fullnames = people.filter(function (person) { return person.age >= 18; }).map(function (person) { @@ -314,68 +210,13 @@ var fullnames = }); ``` - -```javascript -Becomes -``` - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - -```javascript -numbers.sort((a, b) => b – a); -``` - - -```javascript -var fullnames = - people.filter(function (person) { - return person.age >= 18; - }).map(function (person) { - return person.fullname; - }); -``` - - -```javascript -var fullnames2 = - people.filter(p => p.age >= 18) - .map(p => p.fullname); -``` - - -```javascript -Becomes -``` - - -```javascript -Becomes -``` - - - - - - - # Object Literals - ES6 adds a new feature (rule) to the way of defining properties: - - Instead of + - Instead of ```javascript let name = 'Doncho Minkov', @@ -389,45 +230,37 @@ let person = { - We can do just: ```javascript -let name = 'Doncho Minkov'; -let person = { - name, - age -}; +let name = 'Doncho Minkov', + age = 25; +let person = { name, age }; ``` - - - - - - - + # Destructuring Assignments - Destructuring assignments allow to set values to objects in an easier way: - Destructuring assignments with arrays: -```javascript -var [a,b] = [1,2]; //a = 1, b = 2 -var [x, , y] = [1, 2, 3] // x = 1, y = 3 -var [first, second, ...rest] = people; -``` + ```javascript + var [a,b] = [1,2]; //a = 1, b = 2 + var [x, , y] = [1, 2, 3] // x = 1, y = 3 + var [first, second, ...rest] = people; + ``` - Swap values: -```javascript -[x, y] = [y, x] -``` + ```javascript + [x, y] = [y, x] + ``` - Result of method: -```javascript -function get(){ return [1, 2, 3]; } -var [x, y] = get(); -``` + ```javascript + function get(){ return [1, 2, 3]; } + var [x, y] = get(); + ``` @@ -454,28 +287,6 @@ var {name, address: {city}} = person; - - - - -# Maps and Sets -- ES6 supports maps and sets natively - - They do pretty much the same as associative arrays, but in cleaner way: - -```javascript -let names = new Set(); -names.add('Doncho'); -names.add('Nikolay'); -names.add('Ivaylo'); -names.add('Evlogi'); -names.add('Doncho'); // won't be added -``` - - - - - - @@ -502,46 +313,40 @@ import classes from './persons' import {Mammal, Person} form '.persons' ``` - -```javascript -persons.js -``` - - - - - - - -# Extenden parameter handling +# Extended parameter handling - Simple and intuitive default values for function parameters - Aggregation of remaining arguments into single parameter of variadic functions - Spreading of elements of an interable collection -- function f (x, y = 7, z = 42) { return x + y + z } -- f(1) // 50 -```javascript -function f (x, y, ...a) { return (x + y) * a.length } -f(1, 2, "hello", true, 7) // 9 -``` - - -```javascript -var params = [ "hello", true, 7 ] -var other = [ 1, 2, ...params ] -f(1, 2, ...params) // 9 -``` - - - - -# JavaScript.next -- http://academy.telerik.com + ```javascript + function f (x, y = 7, z = 42) { return x + y + z } + f(1) // 50 + function f (x, y, ...a) { return (x + y) * a.length } + f(1, 2, "hello", true, 7) // 9 + ``` + + + + + + +# Free Trainings @ Telerik Academy +- "Web Design with HTML 5, CSS 3 and JavaScript" course @ Telerik Academy + - [javascript course](http://academy.telerik.com/student-courses/web-design-and-ui/javascript-fundamentals/about) + - Telerik Software Academy + - [academy.telerik.com](http://academy.telerik.com) + - Telerik Academy @ Facebook + - [facebook.com/TelerikAcademy](https://facebook.com/TelerikAcademy) + - Telerik Software Academy Forums + - [forums.academy.telerik.com](https://telerikacademy.com/Forum/Home) + + diff --git a/Topics/08. ES6-Features/demos/live-demos/features-Symbol-and-iterators.js b/Topics/08. ES6-Features/demos/live-demos/features-Symbol-and-iterators.js new file mode 100644 index 0000000..176a852 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-Symbol-and-iterators.js @@ -0,0 +1,72 @@ +/* globals console setTimeout document */ + + +// class DataStorage { +// constructor() { +// this._items = []; +// } + +// add(item) { +// this._items.push(item); +// return this; +// } + +// [Symbol.iterator]() { +// let index = -1; +// return { +// next: () => { +// index += 1; +// let isDone = false; +// if (index >= this._items.length) { +// isDone = true; +// } + +// return { +// value: this._items[index], +// done: isDone +// }; +// } +// }; +// } +// } + +// let ds = new DataStorage(); +// ds.add(3); +// ds.add(1); +// ds.add(2); +// ds.add(999); + +// for (let item of ds) { +// console.log(item); +// } + + + +let fib = { + [Symbol.iterator]() { + let prev = 1, + current = 0; + + return { + next() { + let valueToReturn = { + value: current, + done: false + }; + [prev, current] = [current, prev + current]; + return valueToReturn; + } + }; + } +}; + +let index = 0; + +for (let fn of fib) { + if (index >= 100) { + break; + } + index += 1; + + console.log(fn); +} \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-destructuring.js b/Topics/08. ES6-Features/demos/live-demos/features-destructuring.js new file mode 100644 index 0000000..ea2ae22 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-destructuring.js @@ -0,0 +1,49 @@ +/* globals console setTimeout document */ + +// let arr = [1, 2, 3]; + +// let one = "One", +// two = "Two"; + +// let [first, second] = [one, two]; +// console.log(first); +// console.log(second); + + +// function getMinMax(numbers) { +// return [Math.min(...numbers), Math.max(...numbers)]; +// } + + +// let [min, max] = getMinMax([1, 2, 3, -1, 1000, 5, -3]); +// console.log(min); +// console.log(max); + + +// let cat = { +// name: "Puhcho", +// age: 7, +// owner: { +// name: "John", +// height: 180 +// } +// }; + +// let name = cat.name; + +// var { +// age, +// name: nickname, +// owner: { name } +// } = cat; +// console.log(name); +// console.log(nickname); +// console.log(age); + + +// let arr = [1, 2, 3, 4, 5, 6]; + +// let [x, y, ...rest] = arr; +// console.log(x); +// console.log(y); +// console.log(rest); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-events.js b/Topics/08. ES6-Features/demos/live-demos/features-events.js new file mode 100644 index 0000000..7a0710c --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-events.js @@ -0,0 +1,13 @@ +/* globals console setTimeout document */ + +document.getElementById("declation-btn") + .addEventListener("click", (function(ev) { + console.log(this); + }).bind(this)); + + +//this +document.getElementById("arrow-btn") + .addEventListener("click", (ev) => { + console.log(this); + }); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-features-arrays.js b/Topics/08. ES6-Features/demos/live-demos/features-features-arrays.js new file mode 100644 index 0000000..7bf8b90 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-features-arrays.js @@ -0,0 +1,21 @@ +/* globals console setTimeout document */ + +let a1 = [1, 2]; +let a2 = Array.from({ length: 5 }); +console.log(a2); + +console.log(Array.of(4, 6, 7, 8)); + + +//spread (...) + +function sum(x, y, z) { + return x + y + z; +} + +let xyz = [1, 2, 3]; +console.log(sum(...xyz)); +console.log(xyz); +// xyz.push(4); +xyz = [0, ...xyz, 4]; +console.log(xyz); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-function-params.js b/Topics/08. ES6-Features/demos/live-demos/features-function-params.js new file mode 100644 index 0000000..a51ef41 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-function-params.js @@ -0,0 +1,26 @@ +/* globals console setTimeout document */ + +//default params +//rest params + + +// function getRandom(min = 0, max = 50) { +// return 0 | (Math.random() * (max - min) + min); +// } + +// console.log(getRandom(100, 110)); +// console.log(getRandom(45)); +// console.log(getRandom()); + + +// function getMinMax(...numbers) { +// return { +// min: Math.min(...numbers), +// max: Math.max(...numbers) +// }; +// } + +// let { min, max } = getMinMax(1, 2, 3, 4, 5, 6, 7); + +// console.log(min); +// console.log(max); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-generators.js b/Topics/08. ES6-Features/demos/live-demos/features-generators.js new file mode 100644 index 0000000..10fe153 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-generators.js @@ -0,0 +1,64 @@ +/* globals console setTimeout document */ + + +function* idGenerator() { + let lastId = 0; + while (true) { + yield lastId += 1; + } +} + + +// let idGen1 = idGenerator(); +// let idGen2 = idGenerator(); + +// console.log(idGen1.next()); +// console.log(idGen1.next()); +// console.log(idGen1.next()); + +// console.log(idGen2.next()); + + +// function* fibGenerator() { +// let prev = 1, +// current = 0; + +// while (true) { +// yield current; +// [prev, current] = [current, prev + current]; +// } +// } + +// let fib = fibGenerator(); +// let fib2 = fibGenerator(); + +// for (let i = 0; i < 5; i += 1) { +// console.log(fib.next()); +// console.log(fib2.next()); +// } + +// let index = 0; +// for (let fibNumber of fibGenerator()) { +// if (index > 10) { +// break; +// } +// console.log(fibNumber); +// index += 1; +// } + + +function* genFunc() { + let name = "[INITIAL]"; + let name2 = ""; + while (true) { + name = yield name + } +} + +let f = genFunc(); + +f.next(); + +console.log(f.next(["John", "Gosho"])); +console.log(f.next("Doe")); +console.log(f.next("Doe2")); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-numbers.js b/Topics/08. ES6-Features/demos/live-demos/features-numbers.js new file mode 100644 index 0000000..0e60993 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-numbers.js @@ -0,0 +1,12 @@ +/* globals console setTimeout document */ + + +let dec = 123, + hex = 0x123, //1*16^2 + 2* 16 + 3 = 256 + 32 + 3 = 291 + oct = 0o123, //1*8^2 + 2*8 + 3 = 83 + bin = 0b1010; // 12 + +console.log(`Dec: ${dec}`); +console.log(`Hex: ${hex}`); +console.log(`Oct: ${oct}`); +console.log(`Bin: ${bin}`); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-objects.js b/Topics/08. ES6-Features/demos/live-demos/features-objects.js new file mode 100644 index 0000000..fa83ac8 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-objects.js @@ -0,0 +1,16 @@ +/* globals console setTimeout document */ + + +// let person = { +// [(() => "name")()]: "John" +// }; + +// console.log(person); + + +// let obj1 = { +// name: "John" +// }; + +// let obj2 = Object.assign({ age: 17, name: "Pesho" }, obj1); +// console.log(obj2); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/features-promises.js b/Topics/08. ES6-Features/demos/live-demos/features-promises.js new file mode 100644 index 0000000..99fe96e --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-promises.js @@ -0,0 +1,70 @@ +/* globals console setTimeout document */ + +const selector = "printing-div", + printerDiv = document.getElementById(selector); + +let asyncOperations = { + asyncOperation(t) { + setTimeout(function() { + printerDiv.innerHTML += "

It works

"; + }, t); + }, + asyncOperationFunc(func, t) { + setTimeout(function() { + func(); + }, t); + }, + asyncOperationFuncPromise(t) { + let promise = new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, t); + }); + return promise; + } +}; + +asyncOperations.asyncOperationFuncPromise(1000) + .then(function() { + printerDiv.innerHTML += "

1. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(1000) + }) + .then(function() { + printerDiv.innerHTML += "

2. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(1000) + }) + .then(function() { + printerDiv.innerHTML += "

3. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(2500) + }) + .then(function() { + printerDiv.innerHTML += "

4. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(1000) + }) + .then(function() { + printerDiv.innerHTML += "

5. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(1000) + }) + .then(function() { + printerDiv.innerHTML += "

6. It works with Promise

"; + return asyncOperations.asyncOperationFuncPromise(1000) + }); + +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

1. It works

"; +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

2. It works

"; +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

3. It works

"; +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

4. It works

"; +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

5. It works

"; +// asyncOperations.asyncOperationFunc(function() { +// printerDiv.innerHTML += "

6. It works

"; +// }, 1000); +// }, 1000); +// }, 1000); +// }, 2000); +// }, 1000); +// }, 1000); diff --git a/Topics/08. ES6-Features/demos/live-demos/features-strings.js b/Topics/08. ES6-Features/demos/live-demos/features-strings.js new file mode 100644 index 0000000..1ca460a --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/features-strings.js @@ -0,0 +1,19 @@ +/* globals console setTimeout document */ + +//templated string, string literals, multiline strings + +let fullname = "John Doe"; + +console.log(fullname.startsWith("John")); +console.log(fullname.indexOf("John") === 0); + +console.log(fullname.endsWith("John")); +console.log(fullname.endsWith("Doe")); +console.log(fullname.indexOf("Doe") === (fullname.length - "Doe".length)); + +console.log("-".repeat(20)); +console.log(fullname.includes("John")); +console.log(fullname.includes("john")); +console.log(fullname.includes("n D")); +console.log(fullname.includes("Doe")); +console.log(fullname.indexOf("Doe") >= 0); \ No newline at end of file diff --git a/Topics/08. ES6-Features/demos/live-demos/index.html b/Topics/08. ES6-Features/demos/live-demos/index.html new file mode 100644 index 0000000..f117990 --- /dev/null +++ b/Topics/08. ES6-Features/demos/live-demos/index.html @@ -0,0 +1,16 @@ + + + + + + Document + + + + Click + Click +
+ + + + \ No newline at end of file diff --git a/Topics/08. ES6-Features/slides/README.md b/Topics/08. ES6-Features/slides/README.md deleted file mode 100644 index c7ee572..0000000 --- a/Topics/08. ES6-Features/slides/README.md +++ /dev/null @@ -1,528 +0,0 @@ - - - - - - - -# Table of Contents -- JavaScript History - - The ECMAScript standard -- Using JavaScript.Next - - Running JavaScript.Next in the browsers - - Chrome Harmony, Firefox Nightly - - Compiling to JS5 – Traceur, Babel -- ECMAScript 6 features - - Variables: var, let, const - - OOP: classes, inheritance, super, get/set - - Functions: generators, iterators, arrow functions, comprehensions, for-of - - - - -- ECMAScript 6 features: - - Data Structures: set/weakset, map/weakmap - - Async operations: built-in promises - - Modules: imports, exports, compitability - - Objects: computed properties, shorthand properties, Object.is(), Object.assign(), proxies - - Others: templates, Math and Number extensions - - - - - - - - -# JavaScript History -- **JavaScript** is a front-end scripting language developed by Netscape for dynamic content - - Lightweight, but with limited capabilities - - Can be used as object-oriented language - - Embedded in your HTML page - - Interpreted by the Web browser -- **Client-side**, **mobile**and**desktop** technology -- Simple and flexible -- Powerful to manipulate the DOM - - - - - - - - -# Using JavaScript.next -- There are a few ways to use JavaScript.next today - - Enable tags in Chrome and Firefox - - Compile to JavaScript 5 using Traceur or Babel -- A compatibility table for ES6 support can be found at https://kangax.github.io/compat-table/es6/ - - - - - - - - -# ES6 Variables -- ES6 introduces new ways to declare variables: - - let – creates a scope variable - - Accessible only in its scope - -```javascript -for(let number of [1, 2, 3, 4]){ - console.log(number); -} -//accessing number here throws exception -``` - - - const – creates a constant variable - - Its value is read-only and cannot be changed - -```javascript -const MAX_VALUE = 16; -MAX_VALUE = 15; // throws exception -``` - - - - - - - - - - - -# For-of loop - -```javascript -The for-of loop iterates over the values - Of an array -``` - -- function* generator(maxValue){ -- for(let i = 0; i < maxValue; i+=1) { -- yield i; -- } -- } -- let iter = generator(10); -- for(let val of iter()){ -- console.log(val); -- } - -```javascript -let sum = 0; -for(let number of [1, 2, 3]) { - sum += number; } -``` - - -```javascript - Of An iteratable object -``` - - - - - - - - - - - -# Templated Strings in ES6 -- ES6 supports templated strings - - i.e. strings with placeholders: - -```javascript -let people = [new Person('Doncho', 'Minkov'), … ]; -for (let person of people){ - log(`Fullname: ${person.fname} ${person.lname}`); -} -``` - - - Templates escape the strings - - They do not call eval - - - - - - - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - - - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - -
Constructor of the class
- - - -# Classes and Inheritance in ES6 -- ES6 introduces classes and a way to create classical OOP - -```javascript -class Person extends Mammal { - constructor(fname, lname, age) { - super(age); - this._fname = fname; - this._lname = lname; - } - get fullname() { - //getter property of fullname - } - set fullname(newfullname) { - //setter property of fullname - } - // more class members… -} -``` - -
Getters and setters
-
Constructor of the class
- - - - - - - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - -```javascript -numbers.sort((a, b) => b – a); -``` - - -```javascript -Becomes -``` - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - -```javascript -numbers.sort((a, b) => b – a); -``` - - -```javascript -var fullnames = - people.filter(function (person) { - return person.age >= 18; - }).map(function (person) { - return person.fullname; - }); -``` - - -```javascript -Becomes -``` - - - - -# Arrow Functions -- Arrow functions easify the creation of functions: - -```javascript -numbers.sort(function(a, b){ - return b – a; -}); -``` - - -```javascript -numbers.sort((a, b) => b – a); -``` - - -```javascript -var fullnames = - people.filter(function (person) { - return person.age >= 18; - }).map(function (person) { - return person.fullname; - }); -``` - - -```javascript -var fullnames2 = - people.filter(p => p.age >= 18) - .map(p => p.fullname); -``` - - -```javascript -Becomes -``` - - -```javascript -Becomes -``` - - - - - - - - - - - -# Object Literals -- ES6 adds a new feature (rule) to the way of defining properties: - - Instead of - -```javascript -let name = 'Doncho Minkov', - age = 25; -let person = { - name: name, - age: age -}; -``` - - - We can do just: - -```javascript -let name = 'Doncho Minkov'; -let person = { - name, - age -}; -``` - - - - - - - - - - - -# Destructuring Assignments -- Destructuring assignments allow to set values to objects in an easier way: - - Destructuring assignments with arrays: - -```javascript -var [a,b] = [1,2]; //a = 1, b = 2 -var [x, , y] = [1, 2, 3] // x = 1, y = 3 -var [first, second, ...rest] = people; -``` - - - Swap values: - -```javascript -[x, y] = [y, x] -``` - - - Result of method: - -```javascript -function get(){ return [1, 2, 3]; } -var [x, y] = get(); -``` - - - - -# Destructuring Assignments -- Destructuring assignments allow to set values to objects in an easier way: - - Destructuring assignments with objects: - -```javascript -var person = { - name: 'Doncho Minkov', - address: { - city: 'Sofia', - street: 'Aleksander Malinov' - } -}; - -var {name, address: {city}} = person; -``` - - - - - - - - - - - -# Maps and Sets -- ES6 supports maps and sets natively - - They do pretty much the same as associative arrays, but in cleaner way: - -```javascript -let names = new Set(); -names.add('Doncho'); -names.add('Nikolay'); -names.add('Ivaylo'); -names.add('Evlogi'); -names.add('Doncho'); // won't be added -``` - - - - - - - - - - - -# ES6 Modules -- ES6 supports modules - - A way to write JavaScript in different files - - Each file has its own scope (not the global) - - Each file decides what to export from its module - - Export the objects you want from a module: - -```javascript -module.exports = { - Person: Person, - Mammal: Mammal -} -``` - - - To use the module in another file: - -```javascript -import classes from './persons' -import {Mammal, Person} form '.persons' -``` - - -```javascript -persons.js -``` - - - - - - - - - - - -# Extenden parameter handling -- Simple and intuitive default values for function parameters -- Aggregation of remaining arguments into single parameter of variadic functions -- Spreading of elements of an interable collection -- function f (x, y = 7, z = 42) { return x + y + z } -- f(1) // 50 - -```javascript -function f (x, y, ...a) { return (x + y) * a.length } -f(1, 2, "hello", true, 7) // 9 -``` - - -```javascript -var params = [ "hello", true, 7 ] -var other = [ 1, 2, ...params ] -f(1, 2, ...params) // 9 -``` - - - - -# JavaScript.next -- http://academy.telerik.com - - - - diff --git a/Topics/08. ES6-Features/slides/index.html b/Topics/08. ES6-Features/slides/index.html deleted file mode 100644 index 543e5c3..0000000 --- a/Topics/08. ES6-Features/slides/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - - - - - - - diff --git a/Topics/09. Modules-and-Patterns/README.md b/Topics/09. Modules-and-Patterns/README.md index b7ee93c..0ae8159 100644 --- a/Topics/09. Modules-and-Patterns/README.md +++ b/Topics/09. Modules-and-Patterns/README.md @@ -9,16 +9,17 @@

JavaScript OOP

Telerik Software Academy

- http://academy.telerik.com + http://academy.telerik.com
# Table of Contents -- Public/Private fields in JavaScript - Module pattern - Revealing module pattern -- Singleton pattern - +- Augmenting Modules +- Modules in EcmaScript2015 + + @@ -214,7 +215,7 @@ import * as myModule from "my-module"; ```js import {myMember} from "my-module"; ``` -Import multiple members of a module. This inserts both foo and bar into the current scope. +- Import multiple members of a module. This inserts both foo and bar into the current scope. ```js import {foo, bar} from "my-module"; diff --git a/Topics/09. Modules-and-Patterns/demos/1. module.js b/Topics/09. Modules-and-Patterns/demos/1. module.js index 935edf3..116499c 100644 --- a/Topics/09. Modules-and-Patterns/demos/1. module.js +++ b/Topics/09. Modules-and-Patterns/demos/1. module.js @@ -27,7 +27,7 @@ }; }; - return { getCalculator: (name) => new Calculator(name }; + return { getCalculator: (name) => new Calculator(name) }; } ()); var calc = controls.getCalculator('First');