Browse Source

HW_11 done

ostapenkonataliia 1 year ago
parent
commit
f80a258d3c
10 changed files with 563 additions and 107 deletions
  1. 14 8
      .idea/workspace.xml
  2. 25 25
      Js_08/js.js
  3. 11 12
      Js_10/js.js
  4. 3 1
      Js _11/index.html
  5. 60 59
      Js _11/js.js
  6. 12 0
      Js_12/index.html
  7. 139 0
      Js_12/js.js
  8. 45 0
      Js_12/style.css
  9. 130 2
      js.js
  10. 124 0
      rgb.js

+ 14 - 8
.idea/workspace.xml

@@ -2,12 +2,16 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="c45bf7d2-992f-400a-8194-6f236ee5f805" name="Changes" comment="">
-      <change afterPath="$PROJECT_DIR$/Js _11/index.html" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/Js _11/js.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/js.js" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/Js_12/index.html" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/Js_12/js.js" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/Js_12/style.css" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/rgb.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/Js_10/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/Js_10/index.html" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/Js _11/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/Js _11/index.html" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/Js _11/js.js" beforeDir="false" afterPath="$PROJECT_DIR$/Js _11/js.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/Js_08/js.js" beforeDir="false" afterPath="$PROJECT_DIR$/Js_08/js.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/Js_10/js.js" beforeDir="false" afterPath="$PROJECT_DIR$/Js_10/js.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/js.js" beforeDir="false" afterPath="$PROJECT_DIR$/js.js" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -17,9 +21,9 @@
   <component name="FileTemplateManagerImpl">
     <option name="RECENT_TEMPLATES">
       <list>
-        <option value="CSS File" />
-        <option value="HTML File" />
         <option value="JavaScript File" />
+        <option value="HTML File" />
+        <option value="CSS File" />
       </list>
     </option>
   </component>
@@ -41,7 +45,7 @@
     "RunOnceActivity.OpenProjectViewOnStart": "true",
     "RunOnceActivity.ShowReadmeOnStart": "true",
     "WebServerToolWindowFactoryState": "false",
-    "last_opened_file_path": "C:/js10",
+    "last_opened_file_path": "C:/вика/js11",
     "list.type.of.created.stylesheet": "CSS",
     "nodejs_package_manager_path": "npm",
     "settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings",
@@ -87,7 +91,9 @@
       <workItem from="1669725693806" duration="34828000" />
       <workItem from="1669843943175" duration="5002000" />
       <workItem from="1669966012431" duration="34799000" />
-      <workItem from="1670226413389" duration="27634000" />
+      <workItem from="1670226413389" duration="30307000" />
+      <workItem from="1670585530619" duration="66786000" />
+      <workItem from="1670959529015" duration="10774000" />
     </task>
     <servers />
   </component>

+ 25 - 25
Js_08/js.js

@@ -201,31 +201,31 @@ else {
 //     Для чтения текущего значения select используйте свойство value: from.value или to.value
 //Создайте HTML файл с :
 //
-// fetch('https://open.er-api.com/v6/latest/USD').then(res => res.json())
-//     .then(data => {
-//         const from = document.getElementById('from');
-//         const to = document.getElementById('to');
-//         const exchangeRate = document.getElementById('rate')
-//         const amount = document.getElementById('amount');
-//         const result = document.getElementById('result');
-//
-//         for (let [rate, course] of Object.entries(data.rates)) {
-//
-//             const optionFrom = document.createElement('option')
-//             const optionTo = document.createElement('option')
-//
-//             optionFrom.innerHTML = optionTo.innerHTML = rate
-//             optionTo.value = optionFrom.value = rate
-//
-//             from.append(optionFrom);
-//             to.append(optionTo);
-//         }
-//
-//         from.onchange = to.onchange = amount.oninput = () => {
-//             exchangeRate.innerHTML = (data.rates[to.value] / data.rates[from.value]).toFixed(3)
-//             result.innerHTML = (data.rates[to.value] / data.rates[from.value] * amount.value).toFixed(2)
-//         }
-//     })
+fetch('https://open.er-api.com/v6/latest/USD').then(res => res.json())
+    .then(data => {
+        const from = document.getElementById('from');
+        const to = document.getElementById('to');
+        const exchangeRate = document.getElementById('rate')
+        const amount = document.getElementById('amount');
+        const result = document.getElementById('result');
+
+        for (let [rate, course] of Object.entries(data.rates)) {
+
+            const optionFrom = document.createElement('option')
+            const optionTo = document.createElement('option')
+
+            optionFrom.innerHTML = optionTo.innerHTML = rate
+            optionTo.value = optionFrom.value = rate
+
+            from.append(optionFrom);
+            to.append(optionTo);
+        }
+
+        from.onchange = to.onchange = amount.oninput = () => {
+            exchangeRate.innerHTML = (data.rates[to.value] / data.rates[from.value]).toFixed(3)
+            result.innerHTML = (data.rates[to.value] / data.rates[from.value] * amount.value).toFixed(2)
+        }
+    })
 
 //8. countries and cities +
 //По аналогии с предыдущем заданием, реализуйте интерфейс выбора страны и города:

+ 11 - 12
Js_10/js.js

@@ -59,18 +59,6 @@
 // тому, что в объекте в последствии добавить ключ fatherName
 
 // {
-//     function createPerson (name, surname) {
-//         return obj = {
-//             name,
-//             surname,
-//             getFullName
-//         }
-//     }
-//
-//     function getFullName () {
-//         return `${this.name} ${this.surname} ${this.fatherName}`
-//     }
-//
 //     const a = createPerson("Вася", "Пупкин");
 //     a.fatherName = 'Иванович'
 //     console.log(a.getFullName())
@@ -80,6 +68,17 @@
 //
 //     const c = createPerson("Елизавета", "Петрова")
 //     console.log(c.getFullName())
+//
+//     function createPerson(name, surname) {
+//         return {
+//             name,
+//             surname,
+//             getFullName() {
+//                 return `${this.name} ${this.fatherName || ""} ${this.surname}`
+//             }
+//         }
+//     }
+//
 // }
 
 

+ 3 - 1
Js _11/index.html

@@ -3,10 +3,12 @@
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
-    <script src="js.js"></script>
 
 </head>
 <body>
 
+
+
+<script src="js.js"></script>
 </body>
 </html>

+ 60 - 59
Js _11/js.js

@@ -1,24 +1,17 @@
-// 1. makeProfileTimer +
+// 1. makeProfileTimer
 // Напишите функцию makeProfileTimer, которая служит для замера времени выполнения другого кода и работает следующим образом:
 
-// {
-//     let time = performance.now();
-//     alert('Замеряем время работы этого alert');
-//     time = performance.now() - time;
-//     alert(`Время выполнения ${time}`)
-// }
-
 // {
 //     function makeProfileTimer () {
 //         let t0 = performance.now();
-//         alert('Замеряем время работы этого alert');
-//         let t1 = performance.now();
 //         return function () {
+//             let t1 = performance.now();
 //             return `Время работы этого alert ${t1-t0}`
 //         }
 //     }
 //
 //     let timer = makeProfileTimer()
+//     alert('Замеряем время работы этого alert');
 //     alert(timer())
 // }
 
@@ -30,42 +23,39 @@
 //     Навсегда сохраняет результат функции. Это актуально, например, для Math.random.
 //     Действует лениво, то есть вызывает Math.random только тогда, когда результат действительно нужен. Если же по
 //     каким-то причинам значение не понадобится, то Math.random даже не будет вызван
-
-// function makeSaver (fn) {
-//     return function bind () {
-//         return fn;
+//
+// function makeSaver(fn) {
+//     let result;
+//     let resultSave = false;
+//     return function () {
+//         if (!resultSave) {
+//             result = fn();
+//             resultSave = true;
+//         }
+//         return result;
 //     }
 // }
+//
+// var saver = makeSaver(Math.random);
+// var value1 = saver();
+// var value2 = saver();
+// value1 === value2;
+//
+// var saver2 = makeSaver(() => console.log('saved function called') || [null, undefined, false, '', 0, Math.random()][Math.ceil(Math.random() * 6)]);
+// var value3 = saver2();
+// var value4 = saver2();
+//
+// value3 === value4;
+//
+// let namePrompt = prompt.bind(window, 'Как тебя зовут?');
+// let nameSaver = makeSaver(namePrompt);
+// alert(`Привет! Prompt еще не было!`);
+// alert(`Привет ${nameSaver()}. Только что запустился prompt, первый и последний раз`);
+// alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt был только один раз`);
 
-//let saver = makeSaver(Math.random)
-
-// let value1 = saver();
-// let value2 = saver();
-// console.log(value1())
-// console.log(value2())
-// console.log(value1 === value2)
-// ----------------------------------------------------
-// let saver2 = makeSaver(() => console.log('saved function called') || [null, undefined, false, '', 0, Math.random()][Math.ceil(Math.random()*6)])
-// let value3 = saver2();
-// let value4 = saver2();
-// console.log(value3())
-// console.log(value4())
-// console.log(value3 === value4)
-// ------------------------------------------------------------
-
-function makeSaver () {
-    return function bind() {
-        return name;
-    }
-}
 
-let namePrompt = prompt.bind(window, 'Как тебя зовут?');
-let nameSaver = makeSaver(namePrompt)
-alert(`Привет! Prompt еще не было!`)
-alert(`Привет ${nameSaver()}. Только что запустился prompt, первый и последний раз`)
-alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt был только один раз`)
 
-//3. myBind +
+//3. myBind +-
 // Изучите встроенную функцию bind, и сделайте свою версию, которая позволит определить "значение по умолчанию" не
 // только для первых параметров, но для любых других, например для степени в Math.pow:
 // {
@@ -92,8 +82,7 @@ alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt б
 //
 //     const bindedJoiner = myBind((...params) => params.join(''), null, [ ,'b', , ,'e','f'])
 //     console.log(bindedJoiner('a','c','d'))
-//}
-
+// }
 
 //4. checkResult
 // Напишите декоратор checkResult, который:
@@ -105,19 +94,31 @@ alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt б
 //     обертка возвращает результат оригинальной функции. Если валидатор возвращает что-то другое, то оригинал
 //     запускается еще, пока валидатор не вернет true.
 
-// function checkResult (original, validator) {
-//     function wrapper(...params){
-//
-//     }
-//     return wrapper
-// }
-//
-//
-// //numberPrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет число
-// const numberPrompt = checkResult(prompt, x => !isNaN(+x))
-// let   number       = +numberPrompt("Введите число", "0")  //параметры передаются насквозь в оригинал. Не забудьте передать this, используя call или apply
-//
-// //gamePrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет одно из слов 'камень', 'ножницы', 'бумага'
-// const gamePrompt   = checkResult(prompt, x => ['камень', 'ножницы', 'бумага'].includes(x.toLowerCase()))
-// const turn         = gamePrompt("Введите что то из списка: 'камень', 'ножницы', 'бумага'")
-//
+function checkResult(original, validator) {
+    function wrapper(...params) {
+        let result;
+        do {
+            result = original.apply(this, params);
+        } while (!validator(result));
+        return result;
+    }
+    return wrapper;
+}
+//numberPrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет число
+const numberPrompt = checkResult(prompt, x => !isNaN(+x))
+let   number       = +numberPrompt("Введите число", "0")  //параметры передаются насквозь в оригинал. Не забудьте передать this, используя call или apply
+console.log(number);
+
+//gamePrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет одно из слов 'камень', 'ножницы', 'бумага'
+const gamePrompt   = checkResult(prompt, x => ['камень', 'ножницы', 'бумага'].includes(x.toLowerCase()))
+const turn         = gamePrompt("Введите что то из списка: 'камень', 'ножницы', 'бумага'")
+console.log(turn);
+
+// Используя checkResult сделайте функции, которые:
+// randomHigh. Возвращает случайное число в диапазоне от 0.5 до 1*
+const randomHigh = checkResult(Math.random, x => x >= 0.5 && x <=1 ? true : false )
+console.log(randomHigh())
+
+//alwaysSayYes. Достает пользователя окном confirm пока он не согласится (не нажмет OK)
+const alwaysSayYes = checkResult(confirm, x => x)
+alwaysSayYes()

+ 12 - 0
Js_12/index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+    <link rel="stylesheet" href="style.css">
+
+</head>
+<body>
+</body>
+<script src="js.js"></script>
+</html>

+ 139 - 0
Js_12/js.js

@@ -0,0 +1,139 @@
+
+function createStore(reducer) {
+    let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
+    let cbs = []                     //массив подписчиков
+
+    const getState = () => state            //функция, возвращающая переменную из замыкания
+    const subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
+        () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
+
+    const dispatch = action => {
+        const newState = reducer(state, action) //пробуем запустить редьюсер
+        if (newState !== state) { //проверяем, смог ли редьюсер обработать action
+            state = newState //если смог, то обновляем state
+            for (let cb of cbs) cb() //и запускаем подписчиков
+        }
+    }
+
+    return {
+        getState, //добавление функции getState в результирующий объект
+        dispatch,
+        subscribe //добавление subscribe в объект
+    }
+}
+
+function reducer(state, {type, productName, productAmount, summ}){
+    if (!state){ //начальная уборка в ларьке:
+        return {
+            пиво: {
+                amount: 100,
+                price: 35,
+            },
+            чипсы: {
+                amount: 100,
+                price: 28,
+            },
+            сиги: {
+                amount: 100,
+                price: 90,
+            },
+            касса: 0
+        }
+    }
+    if (type === 'Купить' && productAmount <= state[productName].amount && summ >= state[productName].price * productAmount){ //если тип action - КУПИТЬ, то:
+        return {
+            ...state, //берем все что было из ассортимента
+            [productName]: {
+                amount: state[productName].amount - productAmount,
+                price: state[productName].price
+            },
+            касса: state.касса + state[productName].price * productAmount
+            //и уменьшаем то, что покупается на количество
+        }
+    }
+    return state //если мы не поняли, что от нас просят в `action` - оставляем все как есть
+}
+
+function actionCreator (type, productName, productAmount, summ){
+    return {
+        type,
+        productName,
+        productAmount,
+        summ
+    }
+}
+
+let store = createStore(reducer);
+myState = store.getState()
+
+const showCase = document.createElement('section')
+document.body.append(showCase)
+showCase.classList.add('showCase')
+
+const productSelect = document.createElement('select')
+document.body.append(productSelect)
+
+const productQuantityOrderTitle = document.createElement('div')
+productQuantityOrderTitle.innerText = 'Количество:'
+document.body.append(productQuantityOrderTitle)
+
+const productQuantityInput = document.createElement('input')
+productQuantityInput.type = 'number'
+productQuantityInput.min = '0'
+document.body.append(productQuantityInput)
+
+const moneyTitle = document.createElement('div')
+moneyTitle.innerText = 'Деньги:'
+document.body.append(moneyTitle)
+
+const  money = document.createElement('input')
+money.type = 'number'
+money.min = '0'
+document.body.append(money)
+
+const buttonBuy = document.createElement('button')
+buttonBuy.value = 'Купить'
+buttonBuy.innerText = 'Купить'
+document.body.append(buttonBuy)
+buttonBuy.onclick = () => {
+    store.dispatch(actionCreator(buttonBuy.value, productSelect.value, +productQuantityInput.value, +money.value))
+}
+
+for (const product in store.getState()){
+    if (product === 'касса') continue
+
+    const productCard = document.createElement('div')
+    productCard.classList.add('productCard')
+
+    const productName = document.createElement('h2')
+    productName.innerText = product
+    productName.classList.add('productName')
+
+    const productPrice = document.createElement('div')
+    productPrice.innerText = store.getState()[product].price + ' грн'
+    productPrice.classList.add('productPrice')
+
+    const productAmount = document.createElement('div')
+    productAmount.innerText = store.getState()[product].amount + ' шт'
+    productAmount.classList.add('productQuantity')
+
+    productCard.append(productName, productAmount, productPrice)
+    showCase.append(productCard)
+
+    const productOption = document.createElement('option')
+    productOption.value = productOption.innerText = product
+    productSelect.append(productOption)
+
+    const productPriceUnsubscribe = store.subscribe( () => {
+        productPrice.innerText = store.getState()[product].price + ' грн'
+    })
+
+    const productQuantityUnsubscribe = store.subscribe( () => {
+        productAmount.innerText = store.getState()[product].amount + ' шт'
+    } )
+}
+
+
+
+
+

+ 45 - 0
Js_12/style.css

@@ -0,0 +1,45 @@
+*,
+*:before,
+*:after {
+    box-sizing: border-box;
+}
+
+body {
+    margin: 0;
+    height: 100vh;
+    background-color: lavender;
+    padding: 20px;
+}
+
+.showCase {
+    display: flex;
+}
+
+.productCard{
+    padding: 10px;
+    margin: 20px 20px 20px 0;
+    width: 150px;
+    height: 150px;
+    background-color: #c083ef;
+    border-radius: 5px;
+}
+
+.productName{
+    text-align: center;
+    background-color: gold;
+    padding: 5px;
+    color: black;
+    margin-top: 0px;
+}
+
+.productPrice{
+    text-align: center;
+    padding: 10px;
+    color: black;
+}
+
+.productQuantity{
+    padding: 10px 0px 10px;
+    text-align: center;
+}
+

+ 130 - 2
js.js

@@ -1,2 +1,130 @@
-p = 1111;
-console.log(p)
+
+function createStore(reducer) {
+    let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
+    let cbs = []                     //массив подписчиков
+
+    const getState = () => state            //функция, возвращающая переменную из замыкания
+    const subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
+        () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
+
+    const dispatch = action => {
+        const newState = reducer(state, action) //пробуем запустить редьюсер
+        if (newState !== state) { //проверяем, смог ли редьюсер обработать action
+            state = newState //если смог, то обновляем state
+            for (let cb of cbs) cb() //и запускаем подписчиков
+        }
+    }
+
+    return {
+        getState, //добавление функции getState в результирующий объект
+        dispatch,
+        subscribe //добавление subscribe в объект
+    }
+}
+
+function reducer(state, {type, ШО, СКОКА, Цена}){ //объект action деструктуризируется на три переменных
+    if (!state){ //начальная уборка в ларьке:
+        return {
+                пиво: {
+                    количество: 100,
+                    цена: 38,
+                },
+                чипсы: {
+                    количество: 100,
+                    цена: 21,
+                },
+                сиги: {
+                    количество: 100,
+                    цена: 55,
+                }
+        }
+    }
+    if (type === 'КУПИТЬ'){ //если тип action - КУПИТЬ, то:
+        return {
+            ...state, //берем все что было из ассортимента
+            [ШО]: state[ШО] - СКОКА //и уменьшаем то, что покупается на количество
+        }
+    }
+    return state //если мы не поняли, что от нас просят в `action` - оставляем все как есть
+}
+
+// return {
+//
+// }
+
+let store = createStore(reducer);
+
+const actionByeBeer = СКОКА => ({type: 'КУПИТЬ', ШО: 'пиво', СКОКА})
+const actionByeСrisps = СКОКА => ({type: 'КУПИТЬ', ШО: 'чипсы', СКОКА})
+const actionByeSigarets = СКОКА => ({type: 'КУПИТЬ', ШО: 'сиги', СКОКА})
+
+console.log(store.getState().пиво)
+console.log(store.getState().сиги)
+
+document.body.append(productCard)
+
+for (let elementProduct in store.getState()) {
+    const productCard = document.getElementById('card')
+
+
+    const product = document.getElementById('product')
+    product.innerText = card
+
+    const amount = document.getElementById('amount')
+    amount.innerText = store.getState()[card].количество + `шт`
+
+    const price = document.getElementById('price')
+    price.innerText = store.getState()[card].цена + `грн`
+
+    productCard.append(product)
+    productCard.append(amount)
+    productCard.append(price)
+
+    console.log(card)
+}
+
+
+
+const list = document.createElement("select")
+document.body.append(list)
+
+const choicePrice = document.createElement('div')
+document.body.append(choicePrice)
+
+for (let name of Object.keys(store.getState())) {
+    let product = document.createElement("option")
+    product.innerText = name
+    list.append(product)
+}
+
+const inputAmount = document.createElement('input')
+inputAmount.id = "amount"
+inputAmount.type = 'number'
+inputAmount.min = "0"
+document.body.append(inputAmount)
+
+let total = document.createElement('div');
+
+inputAmount.oninput = list.onchange =  () => {
+    total.innerText = 15 * inputAmount.value;
+}
+document.body.append(total)
+
+const buttonBuy = document.createElement('button')
+buttonBuy.innerText = 'Купить'
+document.body.append(buttonBuy)
+
+// buttonBuy.onclick = () => {
+//     const actionByeBeer = СКОКА => ({type: 'КУПИТЬ', ШО: 'пиво', СКОКА})
+// }
+
+
+
+const person = {
+    name: 'Иван',
+    fatherName: 'Иванович',
+    surname: "Иванов"
+}
+
+const {["name"]:nameOrSurname} = person //деструктуризация с использованием ключа, вычисленного с помощью выражения
+console.log(nameOrSurname)

+ 124 - 0
rgb.js

@@ -0,0 +1,124 @@
+{
+    function colorReducer(state, {type, value}) {
+        if (state === undefined) {
+            return {
+                red: 0,
+                green: 0,
+                blue: 0
+            }
+        }
+        if (type === 'COLOR_RED' && typeof value === 'number' && value >= 0 && value <= 255) {
+            return {
+                ...state,
+                red: value
+            }
+        }
+        if (type === 'COLOR_GREEN' && typeof value === 'number' && value >= 0 && value <= 255) {
+            return {
+                ...state,
+                green: value
+            }
+        }
+        if (type === 'COLOR_BLUE' && typeof value === 'number' && value >= 0 && value <= 255) {
+            return {
+                ...state,
+                blue: value
+            }
+        }
+        return state
+    }
+
+    function createStore(reducer) {
+        let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
+        let cbs = []                     //массив подписчиков
+
+        const getState = () => state            //функция, возвращающая переменную из замыкания
+
+        const subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
+            () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
+
+        const dispatch = action => {
+            const newState = reducer(state, action) //пробуем запустить редьюсер
+            if (newState !== state) { //проверяем, смог ли редьюсер обработать action
+                state = newState //если смог, то обновляем state
+                for (let cb of cbs) cb() //и запускаем подписчиков
+            }
+        }
+
+        return {
+            getState, //добавление функции getState в результирующий объект
+            dispatch,
+            subscribe //добавление subscribe в объект
+        }
+    }
+
+    let store = createStore(colorReducer);
+    console.log(store.getState())
+    console.log(store.dispatch("COLOR_RED", 50))
+    console.log(store.getState())
+
+    store.subscribe(() => console.log(store.getState()))
+    store.dispatch("COLOR_RED", 60)
+    store.dispatch("COLOR_BLUE", 600);
+
+    const actionColorRed = value => ({type: "COLOR_RED", value});
+    const actionColorGreen = value => ({type: "COLOR_GREEN", value});
+    const actionColorBlue = value => ({type: "COLOR_BLUE", value});
+    store.dispatch(actionColorBlue(100))
+
+    store.subscribe(() => {
+        const {red, green, blue} = store.getState()
+        document.body.style.backgroundColor = `rgb(${red}, ${green}, ${blue})`
+    })
+    store.dispatch(actionColorGreen(100))
+    store.dispatch(actionColorGreen(200))
+
+
+    const redInput = document.createElement('input')
+    redInput.type = 'number'
+    redInput.min = 0;
+    redInput.max = 255
+    redInput.oninput = () => store.dispatch(actionColorRed(+redInput.value))
+    document.body.append(redInput)
+
+    const greenInput = document.createElement('input')
+    greenInput.type = 'number'
+    greenInput.min = 0;
+    greenInput.max = 255
+    greenInput.oninput = () => store.dispatch(actionColorGreen(+greenInput.value))
+    document.body.append(greenInput)
+
+
+    store.subscribe(() => redInput.value = store.getState().red)
+    store.dispatch(actionColorRed(100))
+
+    const inputColor = document.createElement('input')
+    inputColor.type = 'color'
+
+    inputColor.oninput = () => console.log(inputColor.value)
+    document.body.append(inputColor)
+
+    inputColor.oninput = () => {
+        const hex = inputColor.value
+        const red = parseInt(hex.slice(1, 3), 16)
+        const green = parseInt(hex.slice(3, 5), 16)
+        const blue = parseInt(hex.slice(5, 7), 16)
+
+        store.dispatch(actionColorRed(red))
+        store.dispatch(actionColorGreen(green))
+        store.dispatch(actionColorBlue(blue))
+
+
+        console.log(red, green, blue)
+    }
+
+    store.subscribe(() => {
+        const {red, green, blue} = store.getState()
+        const colorToHex = value => (value < 16 ? '0' : '') + value.toString(16)
+        inputColor.value = `#${colorToHex(red)}${colorToHex(green)}${colorToHex(blue)}`
+    })
+
+    store.dispatch(actionColorRed(0))
+    store.dispatch(actionColorGreen(0))
+    store.dispatch(actionColorBlue(0))
+} */