JavaScript

Материал из Artem Aleksashkin's Wiki
Перейти к навигации Перейти к поиску

var, let, const

    • var - объявляет или глобально или в теле функции
    • let - объявляет в блоке кода {}
    • const - как let, только нельзя менять ссылку на объект
  • Блокировка изменения объекта
let obj = {};
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: false,// можно ли изменять
  enumerable: true,// можно ли перечислять в циклах
  configurable: false// можно ли изменять другие дескрипторы, удалять или менять тип свойства
});
Object.preventExtensions(obj); //запретит добавлять новые свойства
Object.seal(obj);//запретит добавлять, удалять, но позволяет менять изменять свойства
Object.freeze(obj);// замораживает объект

Клонирование объектов

    • Поверхностная копия Shallow
      • Object.assign({}, objectToCopy)
      • {...objectToCopy} - спред оператор
    • Глубока Deep
function deepClone(value) {
    if (typeof value !== 'object' || value === null) {
        return value;
    }
    if (Array.isArray(value)) {
        return value.map((item) => deepClone(item));
    }
    return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, deepClone(value)]));
}

Типы данных

    • Number - целое или плав.точка или NaN typeof(value) === 'number'
    • String - "Test", 'Test', `Test, ${test}` [1] typeof(value) === 'string'
    • Boolean - true/false, typeof(value) === 'boolean'
    • null - пустое значение, typeof(value) === 'object'
    • undefined - неопределенное значение, typeof(value) === 'undefined'
    • Symbol - уникальный тип данных, typeof(value) === 'symbol'
    • BigInt - произвольная точность или через конструктор или 12345n, typeof(value) === 'bigint'
    • Object - или через конструктор или {}, typeof(value) === 'object'
    • Array - или через конструктор или [], typeof(value) === 'object', Array.isArray(value) === true
    • Function - или прямое объявление или через анонимную typeof(value) === 'function'
    • Date - new Date(), typeof(value) === 'object'
    • RegExp - или через new RegExp('^[a-z]*$', flags) или /^[a-z]*$/flags
    • Map - карта - new Map(), map.set(key, value), typeof(map) === 'object'
    • Set - набор уникальных значений - new Set(array) или set.add(value), typeof(set) === 'object' set.has(val) -> O(1)
    • WeakMap, WeakSet - Garbage Collector чистит значения, если исходные были удалены.

Garbage Collector

    • Mark(пометка недостижимых объектов) & Sweap(удаление)

Function Declaration и Function Expression

    • Function Declaration
console.log(factorial(5));// функция всплывает

function factorial(n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}
    • Function Expression
const factorial = function factorial(n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
};

console.log(factorial(5));// функция не всплывает

Function Generator

[2]

function* generatorFunction() {
    yield 1;
    yield 2;
    return 3;
}

const gen = generatorFunction();
console.log(gen.next());// {value: 1, done: false}

Arrow Function

() => expression
param => expression
(param) => expression
(param1, paramN) => expression
() => {
  statements
}
param => {
  statements
}
(param1, paramN) => {
  statements
}

this

Привязка контекста к функции

let user = {
  firstName: "Вася",
  sayHi() {
    alert(`Привет, ${this.firstName}!`);
  }
};

let sayHi = user.sayHi.bind(user); // (*)

sayHi(); // Привет, Вася!

setTimeout(sayHi, 1000); // Привет, Вася!
    • call, apply - вызывает функцию с контекстом
func.call(context, arg1, arg2);
// идентичен вызову
func.apply(context, [arg1, arg2]);
    • arguments
    • у new Function() this - {} - пустой объект
    • у ArrowFunction контекст берется из окружения, изменить его нельзя. arguments не передается
    • у global - this глобальный объект
    • у module - this undefined

dot нотация

У JavaScript есть 2 способа обращаться к свойствам объекта.

Через точку Obj.theProperty и через строчку Obj["theProperty"] - это идентично.

Функции объекта вызываются аналогично

Obj.theFunc(); Obj["theFunc"]();

modules

Позволяет разделить код на модули

export const name = "square";

export function draw(ctx, length, x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x, y, length, length);

  return { length, x, y, color };
}

и потом их импортировать

import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";

экспорт можно прописать отдельной инструкцией

export { name, draw, reportArea, reportPerimeter };

модули можно хранить в *.mjs или *.js файлах

Сравнение

Также есть

  • Object.is() [3]
  • Number.isNaN() [4]
  • Array.isArray() [5]

Замыкание(Closure)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Это функция, которая сохраняет доступ к переменным из своего внешнего окружения, даже после того как это окружение перестало существовать.

Я никогда не понимал почему это назвали замыканием. Я бы назвал - ближайшее окружение.

Асинхронность

[6]

Метод "в лоб"

function doSomethingAsync(callback) {
    setTimeout(() => {
        callback();
    });
}
doSomethingAsync(() => {
    console.log('async code');
});
  • Promise
    • Pending
    • Fulfilled
    • Rejected
  • Promise.all() - принимает массив, возвращает промис с выполненными промисами или одну ошибку
  • Promise.allSettled() - принимает массив, возвращает промис с результатами
  • Promise.any() - принимает массив, возвращает результат любого успешного промиса или ошибку в случае безуспешного результата
  • Promise.race() - принимает массив, возвращает результат первого промиса или ошибку
  • Promise.reject()
  • Promise.resolve()
  • Promise.try()
  • Promise.withResolvers()
function checkMail() {
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.5) {
      resolve('Mail has arrived');
    } else {
      reject(new Error('Failed to arrive'));
    }
  });
}

checkMail()
  .then((mail) => {
    console.log(mail);
  })
  .catch((err) => {
    console.error(err);
  })
  .finally(() => {
    console.log('Experiment completed');
  });

Async Await

function resolveAfter2Seconds() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds().catch((value) => {console.error(value);}).finally(() => {console.log('finally');});
  console.log(result);
  // Expected output: "resolved"
}

asyncCall();
  • Observable RxJS [7]
function checkMail() {
  return new Observable((subscriber: Subscriber<string | null>) => {
    if (Math.random() > 0.5) {
          subscriber.next('next');
    } else {
          subscriber.complete();
          // subscriber.error(new Error('12345'));
          // subscriber.unsubscribe();
    }
  });
}
checkMail()
      .subscribe({
        next: (results: string | null): void => {
          console.log(results);
        },
        error: (err) => console.error('something wrong occurred: ', err),
        complete: () => {
          console.log('complete');
        },
      });

Prototype

Прототипы позволяют организовать прототипное наследование

function Person(name) {
    this.name = name;
}

Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
}

const john = new Person('John');

john.greet();
// Hello, my name is John

Вытащить прототип объекта (getPrototypeOf или __proto__(устар))

const obj = {};
console.log(obj.toString());// [object Object]

console.log(Object.getPrototypeOf(obj) === Object.prototype);// true
console.log(Object.getPrototypeOf(obj.prototype) === null);// true

Установить прототип

const proto = { greet: function() { console.log('Hello!'); } };
const obj = {};
Object.setPrototypeOf(obj, proto);
obj.greet(); // Hello!
Js-prototype.png

Метод Object.hasOwn проверяет наличие собственного свойства у объекта.

Метод Object.hasOwnProperty проверяет, является ли свойство собственным свойством объекта. Он не учитывает свойства, унаследованные по цепочке прототипов.

Debounce, throttle, curry

function debounce(func, wait) {
    let timeout;
    return function(...args) {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(context, args), wait);
    };
}

// Пример использования
window.addEventListener('resize', debounce(() => {
    console.log('Resize event');
}, 200));
function throttle(func, limit) {
    let lastFunc;
    let lastRan;
    return function(...args) {
        const context = this;
        if (!lastRan) {
            func.apply(context, args);
            lastRan = Date.now();
        } else {
            clearTimeout(lastFunc);
            lastFunc = setTimeout(function() {
                if ((Date.now() - lastRan) >= limit) {
                    func.apply(context, args);
                    lastRan = Date.now();
                }
            }, limit - (Date.now() - lastRan));
        }
    };
}

// Пример использования
window.addEventListener('scroll', throttle(() => {
    console.log('Scroll event');
}, 200));
function curry(func) {
    return function curried(...args) {
        if (args.length >= func.length) {
            return func.apply(this, args);
        } else {
            return function(...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
}

// Пример использования
function sum(a, b, c) {
    return a + b + c;
}

const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1, 2, 3)); // 6
function partial(func, ...fixedArgs) {
    return function(...args) {
        return func.apply(this, fixedArgs.concat(args));
    };
}

const add = (a, b, c) => a + b + c;
const add5 = partial(add, 5);
console.log(add5(10, 15)); // 30

Event Loop

  • Стек вызовов (Call stack) - структура данных, где хранятся текущие выполняющиеся функции. Когда функция вызывается, она добавляется в стек вызовов, когда её выполнение завершается, она удаляется из стека вызовов.
  • Web API - браузер предоставляет различные API для создания веб-приложений (DOM API, Storage API, XMLHttpRequest)
  • Очередь тасок (Task queue) - очередь, куда попадают задачи от Web API
  • Очередь микротасок (Microtask queue) - очередь, куда попадают задачи от промисов, MutationObserver, IntersectionObserver, queueMicrotask(),

Алгоритм работы

  • Выполнить все синхронные задачи, задачи с Web API отдать на выполнение браузеру.
  • Выполнить все задачи из очереди микротасок (выполняются, пока очередь не опустеет)
  • Выполнить одну макротаску из очереди макротасок
  • Произвести рендеринг
  • Начать с 1 шага
const myFn = async() => {
    console.log("1");// -> Call stack
    setTimeout(() => console.log("6"));// -> Task queue
    Promise.resolve().then(() => console.log("4"));// -> Microtask queue
    console.log("2");// -> Call stack
    await Promise.resolve();// -> Microtask queue
    console.log("5");// -> Microtask queue
}

myFn();
console.log("3");// -> Call stack

// Call stack
// Microtask queue
// Task queue

1
2
3
4
5
6
  • requestAnimationFrame
  • requestIdleCallback