Навыки внешнего принуждения 108 формула (3) - Инженер вызовов API без эмоций

внешний интерфейс JavaScript
Навыки внешнего принуждения 108 формула (3) - Инженер вызовов API без эмоций

Всю ночь стучаться в код, проливать две строчки старых слез, пользоваться тремя языками, страдают только четыре конечности, ждать, пока в пятую стражу заквакает петух, и отдыхать от проливного дождя, потом идти к двери и стоять в длинном павильоне на десять миль; скудная помощь, желающая найти себе хорошую пару; как пара птиц, воображающая облако; в любовной сети, занимающаяся ползком; для подключения Гуйжи, анализ данных; мышление на тысячу миль, помогающее обрамить мир; , как найти?

Краткое содержание серии опубликованных статей:

В соответствии со стилем статьи часть справочного материала будет помечена в конце соответствующего подраздела.

Тридцать седьмой стиль: тупо глядел в глаза, невежественный, как во сне——"123​4".length === 5? В этот момент я почувствовал предательство и обиду в глазах

  • копироватьСледующий код в консоль браузера:
console.log('123​4'.length === 5); // true

12345

  Ха-ха, ты чувствуешь себя преданным своими глазами? На самом деле это так называемаяпространство нулевой ширины(Пробел нулевой ширины, называемый «ZWSP»), символы нулевой ширины — это невидимые непечатаемые символы, которые используются для прерывания длинных английских слов или длинных арабских цифр для облегчения отображения новой строки, в противном случае — длинных английских слов и длинных арабских. цифры будут пересекать границы блочной модели, обычно используемой в редакторах форматированного текста, для форматирования разделителей.

  • Исследуйте тайну следующего кода выше:
const common = '1234';
const special = '123​4';
console.log(common.length); // 4
console.log(special.length); // 5
console.log(encodeURIComponent(common)); // 1234
console.log(encodeURIComponent(special)); // 123%E2%80%8B4
// 把上面中间特殊字符部分进行解码
console.log(decodeURIComponent('%E2%80%8B')); // (空)

const otherSpecial = '123\u200b4'; // 或者"123\u{200b}4"
console.log(otherSpecial); // 1234
console.log(otherSpecial.length, common === special, special === otherSpecial); // 5 false true
  • Используйте пробелы нулевой ширины в HTML (в HTML пробелы нулевой ширины аналогичны<wbr>эквивалент):
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <!-- &#8203; 和 <wbr /> 是零宽空格在html中的两种表示 -->
    <div>abc&#8203;def</div>
    <div>abc<wbr />def</div>
  </body>
</html>

ESLint имеетЗапретить неправильные пробелы (no-irregular-whitespace)правила для предотвращения ошибочного копирования некоторых пробелов, таких как пробелы нулевой ширины, в коде, чтобы не вводить в заблуждение.

Расширение: то, что мы часто используем в html&nbsp;полное имяNo-Break SPace, то есть неразрывные пробелы, когда HTML имеет несколько последовательныхнормальное пространство, браузер будет отображать пробел только при отображении, и, используя этот неразрывный пробел, вы можете запретить браузеру объединять пробелы. Он часто используется в редакторах форматированного текста.Когда мы вводим несколько пробелов подряд в редакторе форматированного текста, окончательный вывод будет содержать много неразрывных пробелов.

Использованная литература:Список общих пробелов - Ли Иньчэн | Что такое пространство нулевой ширины | Википедия - пробелы

Тип 38: Как запретить копирование и вставку с веб-страниц

   Возможно, вам знаком запрет на копирование и вставку веб-страниц. Некоторые веб-страницы прямо запрещено копировать и вставлять; некоторые веб-страницы требуют входа в систему, прежде чем их можно будет скопировать и вставить; а некоторые веб-сайты при копировании и вставке будут содержать соответствующую информацию об источнике, идентифицирующую веб-сайт.

  • Как отключить копирование и вставку с веб-страниц
const html = document.querySelector('html');
html.oncopy = () => {
  alert('牛逼你复制我呀');
  return false;
};
html.onpaste = () => false;
  • Сделайте что-нибудь еще при копировании, например, перейдите на целевую страницу.
const html = document.querySelector('html');
html.oncopy = (e) => {
  console.log(e);
  // 比如指向百度或者登陆页
  // window.location.href='http://www.baidu.com';
};
html.onpaste = (e) => {
  console.log(e);
};
  • Как установить/получить содержимое буфера обмена с помощью js
//设置剪切板内容
document.addEventListener('copy', () => {
  const clipboardData =
    event.clipboardData || event.originalEvent?.clipboardData;
  clipboardData?.setData('text/plain', '不管复制什么,都是我!');
  event.preventDefault();
});

//获取剪切板的内容
document.addEventListener('paste', () => {
  const clipboardData =
    event.clipboardData || event.originalEvent?.clipboardData;
  const text = clipboardData?.getData('text');
  console.log(text);
  event.preventDefault();
});
  • Какая польза

    • Для сцен, которые требуют регистрации для ввода пароля дважды введите одно и то же контент, должно быть необходимость запретить пасту, на этот раз он может запрещать соответствующую копию ввода копирования и вставить действие.
    • Войдите, чтобы скопировать. Содержимое страниц на многих веб-сайтах запрещено копировать, что может помешать пользователям или программам злонамеренно очищать данные страниц.

Использованная литература:Clipboard API and events | Document.execCommand()

Тридцать девятая форма:function.lengthОтносится к чему? - Каррирование, перегрузка функций JS

   В функциональном программировании есть несколько важных понятий: композиция функций, каррирование и функторы. Каррирование относится к методу преобразования функции, которая принимает несколько параметров, в функцию, которая принимает один параметр (первый параметр исходной функции) и возвращает новую функцию, которая принимает остальные параметры и возвращает результат. Этот метод был назван Кристофером Стрейчи в честь логика Хаскелла Карри, но он был изобретен Моисеем Шнфинкелем и Готтлобом Фреге.

  лодаш реализует_.curryфункция,_.curryФункция принимает функцию в качестве аргумента и возвращает новую каррированную функцию. При вызове новой каррированной функции, когда количество переданных параметров меньше, чем параметры, требуемые каррированной функцией, возвращается функция, которая получает оставшиеся параметры, а когда переданные параметры соответствуют требованиям каррированной функции, результат вернулся. Так,_.curryКак функция определяет, соответствуют ли переданные параметры требованиям? Давайте посмотрим на следующий пример:

function func(a, b, c) {
  console.log(func.length, arguments.length);
}
func(1); // 3  1
  • Ознакомьтесь с объяснением MDN:

    • lengthэто значение атрибута функционального объекта, которое указывает, сколько функций имеет функция.Параметры, которые необходимо передать,ТеПараметры с определенными значениями по умолчанию не учитываются, например, длина функции (x = 0) равна 0. То есть количество формальных параметров включает только количество параметров до первого со значением по умолчанию.
    • Напротив,arguments.lengthфактическое количество параметров, переданных при вызове функции.
  • Реализовать функцию карри lodash

// 模拟实现 lodash 中的 curry 方法
function curry(func) {
  return function curriedFn(...args) {
    // 判断实参和形参的个数
    if (args.length < func.length) {
      return function () {
        return curriedFn(...args.concat(Array.from(arguments)));
      };
    }
    return func(...args);
  };
}

function getSum(a, b, c) {
  return a + b + c;
}

const curried = curry(getSum);

console.log(curried(1, 2, 3));
console.log(curried(1)(2, 3));
console.log(curried(1, 2)(3));
  • Перегрузка JS-функции

  Перегрузка функций означает, что имя функции одинаковое, но разрешены разные входные данные.В зависимости от входных данных вызываются разные функции и возвращаются разные результаты. По умолчанию в JS нет перегрузки функций, но естьFunction.lengthсвойства иarguments.length, мы можем просто пройтиif…elseилиswitchДля завершения перегрузки функции JS.

function overLoading() {
  // 根据arguments.length,对不同的值进行不同的操作
  switch (arguments.length) {
    case 0 /*操作1的代码写在这里*/:
      break;
    case 1 /*操作2的代码写在这里*/:
      break;
    case 2: /*操作3的代码写在这里*/
  }
}

  Для получения дополнительной информации о перегрузке функций обратитесь к Джону Резигу, отцу jQuery.JavaScript Method Overloading, В этой статье автор ловко использует замыкания для реализации перегрузки JS-функций.

Использованная литература:Говоря о перегрузке функций JavaScript | JavaScript Method Overloading | Перегрузка функций JavaScript - Fundebug | Function.length | Введение в функциональное программирование - Руан Ифэн

Сороковая форма:["1","7","11"].map(parseInt)Почему он возвращает [1,NaN,3]?

  • карта возвращает 3 параметра, элемент, индекс, массив, поэтому[1,7,11].map(console.log)Распечатать:

parseInt

  • parseInt принимает два параметра: строка, основание счисления, где основание по умолчанию равно 10;
  • Тогда каждый вызов parseInt эквивалентен:parseInt(item,index,Array), третий параметр Массив, переданный картой, будет проигнорирован. Когда индекс равен 0,parseInt(1,0), основание принимает значение по умолчанию 10;parseInt(7,1), 7 не существует в базе 1, поэтому возвращается "NaN";parseInt(11,2), 11 в двоичном формате равно 3 в десятичном.

Ссылаться на:Почему ['1', '7', '11'].map(parseInt) возвращает [1, NaN, 3] в JS

Тип 41: Передача данных между iframes, postMessage может быть вашим выбором

  В ходе обычной разработки мы можем столкнуться с ситуациями, когда нам необходимо передавать данные между сайтами разного происхождения и фреймами. В настоящее время мы можем использовать postMessage для завершения передачи данных. Метод   window.postMessage() может безопасно реализовать связь между источниками. Как правило, сценарии для двух разных страниц будут выполняться только в том случае, если страницы, на которых они выполняются, используют один и тот же протокол (обычно https), номер порта (443 по умолчанию для https) и хост (модуль Document.domain для обоих страницы) установлены на одно и то же значение), два сценария могут взаимодействовать друг с другом (т. е. имеют один и тот же источник). Метод window.postMessage() предоставляет контролируемый механизм для обхода этого ограничения и безопасен при правильном использовании.

// 页面1 触发事件,发送数据
top.postMessage(data, '*');
// window  当前所在iframe
// parent  上一层iframe
// top     最外层iframe

//页面2 监听message事件
useEffect(() => {
  const listener = (ev) => {
    console.log(ev, ev.data);
  };
  window.addEventListener('message', listener);
  return () => {
    window.removeEventListener('message', listener);
  };
}, []);

Уведомление:

  • postMessageВторой параметр targetOrigin используется для указания того, какие окна могут получать событие сообщения, и его значением может быть строка «*» (что означает неограниченное количество) или URI.
  • Если вы точно знаете, в какое окно должно быть отправлено сообщение, всегда указывайте для targetOrigin точное значение вместо *.
  • Отсутствие точной цели приведет к утечке данных на любой вредоносный сайт, заинтересованный в данных.

Использованная литература:window.postMessage

Форма 42: X Шредингера - Интересноlet x = x

  Кот Шрёдингера (английское название: Erwin Schrödinger’s Cat) — мысленный эксперимент, предложенный известным австрийским физиком Шрёдингером, который заключается в содержании кошки в герметичном контейнере с небольшим количеством радия и цианида. Распад радия имеет вероятность: если радий распадется, сработает механизм, разбивающий бутылку с цианидом, и кот погибнет, если радий не распадется, кот выживет. Согласно теории квантовой механики,Поскольку радиоактивный радий находится в суперпозиции двух состояний распада и отсутствия распада, кот должен находиться в суперпозиции мертвого кота и живого кота.. Этот кот, который одновременно мертв и жив, является так называемым «котом Шредингера».

После того, как в   JS были введены let и const, также появилось интересное явление:

<!-- 可以拷贝下面的代码,放的一个html文件中,然后使用浏览器打开,查看控制台 -->
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      let x = x;
    </script>
    <script>
      x = 2;
      console.log(x);
    </script>
  </body>
</html>

specx

  В приведенном выше коде мы ввели и написали в первом скриптеlet x = x;, что делает невозможным использование переменной x в глобальной области видимости в других скриптах (независимо от того, является ли это присваиванием, значением или объявлением x ). То есть x теперь находится в промежуточном состоянии «как определенного, так и неопределенного».

Этот вопрос показывает: еслиlet xПроцесс инициализации завершается неудачно, тогда:

  • Переменная x всегда будет в созданном состоянии.
  • Вы не можете снова инициализировать x (инициализация имеет только один шанс, и этот шанс у вас не получится).
  • Поскольку x не может быть инициализирован, x всегда находится во временной мертвой зоне (лимбо в Inception)!
  • Некоторые люди думают, что JS пит, как такое может быть, на самом деле это не большая проблема, потому что код уже сообщил об ошибке в это время, и последующий код не имеет шансов выполниться.

Использованная литература:Переменная JS Бан Дафа: X Шредингера

Тип 43. Расскажите об обработке ошибок во внешнем интерфейсе.

Интерфейсная обработка ошибок от React-dnd

   В начале года автор однажды сделалобработка ошибок во внешнем интерфейсеПримечания, это выглядит так:

  Страница, определяемая меню в проекте, введена из-за необходимости перетаскивания.React DnDдля завершения этой работы; с обновлением и итерацией бизнеса некоторые страницы списка ввели функцию настраиваемых столбцов, вы можете сортировать столбцы путем перетаскивания, а позже обнаружил, что на некоторых страницах, пытаясь открыть пользовательский столбец, когда окно выскакивает, страница вылетает на белый экран, а консоль выдает ошибку:'Cannot have two HTML5 backends at the same time.'. При устранении неполадок просмотрите исходный код и найдите:

// ...
value: function setup() {
  if (this.window === undefined) {
    return;
  }
  if (this.window.__isReactDndBackendSetUp) {
    throw new Error('Cannot have two HTML5 backends at the same time.');
  }
  this.window.__isReactDndBackendSetUp = true;
  this.addEventListeners(this.window);
}
// ...

Другими словами,react-dnd-html5-backendпройдет перед созданием нового экземпляраwindow.__isReactDndBackendSetUpГлобальная переменная используется для определения наличия перетаскиваемого компонента. Если есть, об ошибке будет сообщено напрямую. Поскольку соответствующий компонент в проекте не имеет соответствующей логики обработки ошибок, сгенерированное исключение Error загружается на корневой уровень с помощью слой и никогда не был получен. Захватите и обработайте, что в конечном итоге приведет к сбою страницы. На самом деле, в бизнес-сценарии да, эту проблему решить проще, потому что на странице определения меню нет необходимости в настраиваемых столбцах, а настраиваемые столбцы на других страницах отображаются через всплывающее окно, так что не надо. Не забудьте указать всплывающее окно пользовательского столбца. Просто установите свойство destroyOnClose (закрыть уничтожение). Чтобы избежать системного белого экрана, вызванного некоторыми ошибками в проекте, мы должны разумно использовать обработку ошибок в проекте.

Методы фронтальной обработки ошибок

Error Boundaries

  Как сделать компонент React «границами ошибок»? Просто определите новую функцию жизненного цикла в компоненте — componentDidCatch(error, info):

error: это выданная ошибка; информация: это ключ componentStack. Это свойство содержит информацию о стеке компонентов, вызвавшем ошибку.

// ErrorBoundary实现
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

ErrorBoundary использует:

// ErrorBoundary使用
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Erro Boundaries также является компонентом по сути.Он становится новым компонентом, добавляя новую функцию жизненного цикла componentDidCatch.Этот специальный компонент может собирать информацию об ошибках js в своем дереве подкомпонентов, выводить информацию об ошибках или в условиях ошибки отображать страницу ошибки по умолчанию. Обратите внимание, что границы ошибок могут перехватывать только ошибки js в своих дочерних компонентах, но не свои собственные компоненты и ошибки js в не дочерних компонентах.

  Однако Error Boundaries не является панацеей. Давайте рассмотрим ситуации, когда Error Boundaries нельзя использовать для перехвата{} ошибок:

  • Внутренний обработчик событий компонента, поскольку Error Boundaries имеют дело только с ошибками в Render, а Hander Event не возникает в процессе Render.
  • Исключения в асинхронных функциях, границы ошибок, не могут быть перехвачены, например, исключения в таких функциях, как setTimeout или setInterval, requestAnimationFrame и т. д.
  • рендеринг на стороне сервера
  • Ошибки, возникающие в самом компоненте Error Boundaries

Функция жизненного цикла componentDidCatch():

  componentDidCatch – это новая функция жизненного цикла. Когда компонент имеет эту функцию жизненного цикла, он становится границей ошибки.

попробовать/поймать модуль

  Границы ошибок выдают только информацию об ошибках дочерних компонентов и не могут выдавать исключения в обработчиках событий в компонентах. (Поскольку Error Boundaries гарантирует только правильный рендеринг, а обработчик событий не происходит во время процесса рендеринга), нам нужно использовать try/catch для обработки исключений в обработчике событий.

try/catch может перехватывать только синхронные ошибки времени выполнения, но ничего не может сделать с синтаксическими и асинхронными ошибками и не может их перехватывать.

window.onerror

   Когда возникает ошибка времени выполнения JS, окно инициирует событие ошибки интерфейса ErrorEvent и выполняет функцию window.onerror().

На практике onerror в основном используется для перехвата непредвиденных ошибок, а try-catch используется для отслеживания конкретных ошибок в предсказуемых обстоятельствах.Комбинация этих двух методов более эффективна.

/**
 * @param {String}  message    错误信息
 * @param {String}  source    出错文件
 * @param {Number}  lineno    行号
 * @param {Number}  colno    列号
 * @param {Object}  error  Error对象(对象)
 */
window.onerror = function (message, source, lineno, colno, error) {
  console.log('捕获到异常:', { message, source, lineno, colno, error });
  // window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。
  //  return true;
};

window.addEventListener

   в основном используется для захвата исключений при загрузке статических ресурсов.

Promise Catch

unhandledrejection:

   Когда промис отклоняется и нет обработчика отклонения, запускается событие unhandledrejection; это может произойти под окном, но также может произойти и в воркере. unhandledrejection наследуется от PromiseRejectionEvent, который, в свою очередь, наследуется от Event. Таким образом, unhandledrejection имеет свойства и методы PromiseRejectionEvent и Event.

Суммировать

   Во внешних компонентах/проектах должен быть соответствующий процесс обработки ошибок, иначе возникает ошибка, слои загружаются, а страница не захватывается, что приводит к зависанию страницы.

Тип 44: не увлекайтесь инструментами — используйте nodejs для автоматического создания файлов в соответствии с конфигурацией.

   В моей работе мне нужно создать проект слоя BFF, чтобы реализовать контроль разрешений для каждого интерфейса и перенаправить его на базовый внутренний интерфейс. Поскольку логика интерфейса уровня BFF меньше, в 70% случаев реализуется только одна переадресация, поэтому сходство каждого файла высокое, но поскольку каждому API необходимо управлять разрешениями отдельно, файл API должен существовать, поэтому используйте nodejs для написания автоматизации API Создайте сценарий, чтобы избежать большого количества ручных операций по созданию, копированию и изменению файлов. Примеры:

  • Напишите скрипт для автоматической генерации файлов:
// auto.js
const fs = require('fs');
const path = require('path');
const config = require('./apiConfig'); // json配置文件,格式见下面注释内容
// config的格式如下:
// [
//     {
//         filename: 'querySupplierInfoForPage.js',
//         url: '/supplier/rest/v1/supplier/querySupplierInfoForPage',
//         comment: '分页查询供应商档案-主信息',
//     },
// ]

// 验证数量是否一致
// 也可以在此做一些其他的验证
function verify() {
  console.log(
    config.length,
    fs.readdirSync(path.join(__dirname, '/server/api')).length
  );
}

// 生成文件
function writeFileAuto(filePath, item) {
  fs.writeFileSync(
    filePath,
    `/**
* ${item.comment}
*/
const { Controller, Joi } = require('ukoa');

module.exports = class ${item.filename.split('.')[0]} extends Controller {
    init() {
        this.schema = {
            Params: Joi.object().default({}).notes('参数'),
            Action: Joi.string().required().notes('Action')
        };
    }

    // 执行函数体
    async main() {
        const { http_supply_chain } = this.ctx.galaxy;
        const [data] = await http_supply_chain("${
          item.url
        }", this.params.Params, { throw: true });
        return this.ok = data.obj;
    }
};
`
  );
}

function exec() {
  config.forEach((item) => {
    var filePath = path.join(__dirname, '/server/api/', item.filename);
    fs.exists(filePath, function (exists) {
      if (exists) {
        // 已存在的文件就不要重复生成了,因为也许你已经对已存在的文件做了特殊逻辑处理
        //(毕竟只有70%左右的API是纯转发,还有30%左右有自己的处理逻辑)
        console.log(`文件${item.filename}已存在`);
      } else {
        console.log(`创建文件:${item.filename}`);
        writeFileAuto(filePath, item);
      }
    });
  });
}

exec();
  • Выполните скрипт для создания файла:node auto.js, содержимое сгенерированного файла выглядит следующим образом:
// querySupplierInfoForPage.js
/**
 * 分页查询供应商档案-主信息
 */
const { Controller, Joi } = require('ukoa');

module.exports = class querySupplierInfoForPage extends Controller {
  init() {
    this.schema = {
      Params: Joi.object().default({}).notes('参数'),
      Action: Joi.string().required().notes('Action'),
    };
  }

  // 执行函数体
  async main() {
    const { http_supply_chain } = this.ctx.galaxy;
    const [data] = await http_supply_chain(
      '/supplier/rest/v1/supplier/querySupplierInfoForPage',
      this.params.Params,
      { throw: true }
    );
    return (this.ok = data.obj);
  }
};

  Это всего лишь руководство.В сочетании с конкретными бизнес-сценариями, возможно, вы найдете больше и лучшие способы использования сценариев nodejs для расширения возможностей внешнего интерфейса.

Сорок пятая форма: Очевидно, что элементы существуют, мойdocument.getElementsByTagName('video')Но не получается?

  • При использовании браузера Chrome для просмотра видео в Интернете некоторые веб-сайты не поддерживают воспроизведение с удвоенной скоростью; некоторые веб-сайты поддерживают только 1,5- и 2-кратную скорость, но я предпочитаю 1,75-кратную; или некоторые веб-сайты требуют от участников воспроизведения с удвоенной скоростью (например, диск), как правило, мы можем решить эту проблему, установив соответствующие плагины для браузера. Если вы не хотите устанавливать плагины, вы также можете использовать что-то вродеdocument.getElementsByTagName('video')[0].playbackRate = 1.75(1,75-кратная скорость) для достижения удвоенной скорости воспроизведения, этот метод эффективен на большинстве веб-сайтов (конечно, если вы знаете идентификатор или класс тега видео, будет удобнее получать элементы по идентификатору и классу). тестирование,playbackRateМаксимальная поддерживаемая скорость в Chrome — 16. При этом датьplaybackRateУстановите значение меньше 1, например 0,3, чтобы имитироватьПризрачные звуковые эффекты.
  • Но на определенном диске этот метод не работает, потому что я не могу получить элемент видео.Элемент проверки выглядит следующим образом:videojs

   При осмотре элемента мы обнаружили#shadow-root (closed)а такжеvideojsПрисутствие. Как вы помните, в шестом классе мы кратко обсуждалиWeb Components, который вводитattachShadow()Метод может открывать Shadow DOM (эта часть DOM по умолчанию изолирована от внешнего DOM, и любой внутренний код не может влиять на внешний), скрывать внутреннюю реализацию пользовательского элемента, и мы не можем получить соответствующий элемент извне, как показано на следующем рисунке (щелкните изображение, чтобы перейти) Пример кода веб-компонентов):

shadow

  Итак, мы можем сделать разумный вывод, что при воспроизведении видео на веб-странице определенного диска также используются аналогичныеElement.attachShadow()метод скрывает элемент, поэтому мы не можем передатьdocument.getElementsByTagName('video')Получите элемент видео. чтениемдокументация по видеоОбнаружено, что пользовательское многоскоростное воспроизведение может быть достигнуто через соответствующий API:

videojs.getPlayers('video-player').html5player.tech_.setPlaybackRate(1.666);

Использованная литература:Метод воспроизведения видео на сетевом диске Baidu | документация по видео | Element.attachShadow() | Глубокое понимание Shadow DOM v1

Тип 46: SQL тоже может быть if else? - Мои странные знания, которые не очень часто пишут SQL, прибавились

   Я столкнулся с проблемой SQL при очистке leetcode627. Смена пола, требования к предмету:

Дана таблица окладов со значениями m = мужчина и f = женщина. Поменять местами все значения f и m (например, поменять все значения f на m и наоборот). Требуется использовать только один оператор обновления (Update), а промежуточной временной таблицы нет. Обратите внимание, что вы должны написать только один оператор Update, пожалуйста, не пишите никаких операторов Select.

```sql
  UPDATE salary
    SET
      sex = CASE sex
          WHEN 'm' THEN 'f'
          ELSE 'm'
        END;
```

Использованная литература:Подробное объяснение CASE WHEN использования SQL

Сорок седьмой стиль: двор глубок и глубок, ивы окутаны дымом, а занавес не имеет веса — как добиться глубокого копирования?

  Глубокий текст кажется вечной темой на фронтенд-интервью.JSON.stringify()так же какJSON.parse(), но объекты, которые этот метод может правильно обрабатывать, — это только числа, строки, логические значения, массивы, плоские объекты, и их нельзя копировать undefined , function, RegExp и другие типы. Существуют и другие реализации, включая оператор распространения, object.asign, рекурсивное копирование, библиотеку lodash и т. д. В Интернете есть много связанных материалов и реализаций, и это не является предметом нашего обсуждения. На этот раз мы рассмотрим новую реализацию —MessageChannel. Давайте посмотрим непосредственно на код:

// 创建一个obj对象,这个对象中有 undefined 和 循环引用
let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
  f: undefined,
};
obj.c = obj.b;
obj.e = obj.a;
obj.b.c = obj.c;
obj.b.d = obj.b;
obj.b.e = obj.b.c;

// 深拷贝方法封装
function deepCopy(obj) {
  return new Promise((resolve) => {
    const { port1, port2 } = new MessageChannel();
    port1.postMessage(obj);
    port2.onmessage = (e) => resolve(e.data);
  });
}

// 调用
deepCopy(obj).then((copy) => {
  // 请记住`MessageChannel`是异步的这个前提!
  let copyObj = copy;
  console.log(copyObj, obj);
  console.log(copyObj == obj);
});

мы обнаруживаемMessageChannelизpostMessageПередаваемые данные также глубоко копируются, что аналогичноweb workerизpostMessageТакой же. Он также может копировать неопределенные объекты и объекты с циклическими ссылками. просто скажи,MessageChannelСоздается канал связи, этот канал имеет два порта, каждый порт может проходить черезpostMessageОтправьте данные, а порт нужно только привязатьonmessageМетод обратного вызова, вы можете получать данные с другого порта.

Следует отметить следующее:MessageChannelПри копировании объекта с помощью функции все равно будет сообщено об ошибке.

Использованная литература:MessageChannel | Что такое MessageChannel и как его использовать?

Стиль сорок восьмой: Как с помощью VSCode сохранить плагины и конфигурации после смены компьютера?

Может быть, у каждого инженера вызовов API, у которого нет эмоций, есть свои плагины, персонализированная конфигурация и фрагменты кода при использовании VSCode для разработки.Это очень удобно использовать VSCode без входа в систему или регистрации учетной записи, но это также приносит одну проблему: если у вас есть несколько компьютеров, например, один дома и один в компании, все они будут использоваться для разработки или, если вы уйдете и присоединитесь к новой компании. На этом этапе нам нужно снова настроить VSCode с нуля, включая плагины, конфигурации и фрагменты кода.Неоднократно это может действительно привести к сбою. На самом деле VSCode предоставляет плагин синхронизации настроек, который нам очень удобен для синхронизации конфигурации плагина. Конкретное использование заключается в следующем:

  • Найдите Settings Sync в VSCode и установите его;
  • После установки нажмите Ctrl (mac is command) + Shift + P, чтобы открыть панель управления, найдите Sync, выберите Sync: Update/Upload Settings, чтобы загрузить свою конфигурацию, и выберите Sync: Download Settings, чтобы загрузить удаленную конфигурацию;
  • Если вы раньше не использовали Settings Sync, при загрузке конфигурации вы сможете создать код авторизации на Github, что позволит IDE создавать ресурсы в вашем gist; чтобы загрузить удаленную конфигурацию, вы можете напрямую ввести идентификатор суть.
  • После загрузки дождитесь установки, а затем перезапустите.

   Таким образом, мы можем синхронизировать конфигурацию между несколькими устройствами.

Использованная литература:Settings Sync | VSCode сохраняет конфигурацию плагина и управляет сниппетами с помощью gist

Тип 49: чтобы предотвратить подделку объектов, попробуйте Object.seal и Object.freeze.

  Иногда вы можете опасаться, что ваш объект был изменен по ошибке, поэтому вам необходимо его защитить.

  • Object.sealЗапретить добавление и удаление свойств

   Как правило, объект является расширяемым (можно добавлять новые свойства).Object.seal()метод, охватывающий объект, заставляет объект изменятьсяНевозможно добавить новые свойства, и всеСуществующие свойства станут недоступными для настройки. Эффект ненастраиваемых свойств заключается в том, что свойства изменяютсянельзя удалить, и свойство данных не может быть переопределено как свойство доступа или наоборот. Значение текущего свойства можно изменить, если оно изначально было доступно для записи. Попытки удалить свойство запечатанного объекта или преобразовать свойство запечатанного объекта из свойства данных в свойство доступа завершатся автоматически или вызовут ошибку TypeError.

атрибут данныхСодержит расположение значения данных, в котором значения могут быть прочитаны и записаны.свойство доступаНе содержит значений данных. Он содержит пару функций получения и установки. Когда свойство доступа читается, вызывается функция получения и возвращает допустимое значение; когда свойство доступа записывается, вызывается функция установки и передается новое значение, а функция установки отвечает за обработку данных.

const person = {
  name: 'jack',
};
Object.seal(person);
delete person.name;
console.log(person); // {name: "jack"}
  • Object.freezeЗаморозить объекты   Object.freeze()способ заморозить объект. Замороженный объект больше не может бытьне может быть изменен; затем замораживает объектВы не можете добавлять новые свойства к этому объекту, вы не можете удалять существующие свойства, вы не можете изменять перечисляемость, конфигурируемость и возможность записи существующих свойств этого объекта, и вы не можете изменять значения существующих свойств. Кроме того, после заморозки объекта егоПрототипы также не могут быть изменены. замораживание() возвращает тот же объект, который был передан.
const obj = {
  prop: 42,
};
Object.freeze(obj);
obj.prop = 33;
// Throws an error in strict mode
console.log(obj.prop);
// expected output: 42

Советы:Object.freezeданеглубокая заморозка, то есть замораживается только один слой.Чтобы сделать объект неизменяемым, нужно рекурсивно заморозить каждое свойство, тип которого является объектом (морозильник). использоватьObject.freeze()Существующие значения свойств в замороженном объекте неизменяемы. использоватьObject.seal()Запечатанный объект может изменить свои существующие значения свойств. В то же время вы можете использовать Object.isFrozen, Object.isSealed, Object.isExtensible для оценки состояния текущего объекта.

Тип 50: случайные числа, которые не случайны — мы все это знаемMath.randomявляется псевдослучайным, как получить криптографически безопасное случайное число

  Способ генерации случайных чисел в JavaScript заключается в вызове Math.random, эта функция возвращает число между [0, 1), мы передаемMath.randomПакетная обработка может получить все виды случайных значений, которые мы хотим.

  • Как реализовать генератор случайных чисел
// from stackoverflow
// 下面的实现还是很随机的
let seed = 1;
function random() {
  let x = Math.sin(seed++) * 10000;
  return x - Math.floor(x);
}

Для    функции генератора случайных чисел требуется начальное число, которое меняется каждый раз при вызове функции random. потому чтоrandom()Это функция без входных данных. Независимо от того, сколько раз она выполняется, результат ее выполнения один и тот же, поэтому ей нужен постоянно изменяющийся входной параметр. Этот входной параметр называется начальным значением, и начальное значение будет меняться каждый раз. это запущено. Таким образом, мы можем использовать приведенные выше идеи для реализации нашего собственного генератора случайных чисел (возможно, в некоторых случаях нам не нужно заботиться о том, является ли он действительно случайным, или мы просто хотим сделать его не случайным).

  • Почему Math.random небезопасен?

Исходный код   V8 показывает, что возможное количество начальных чисел Math.random равно2 ^ 64, случайный алгоритм относительно прост, просто чтобы обеспечить как можно более случайное распределение. Мы знаем, что игральных карт 52, всего 52! =2 ^ 226комбинаций, если случайное семя имеет только2 ^ 64возможно, то может быть большое количество комбинаций, которые не могут появиться.

   Из логики реализации Math.random в V8 каждый раз будет генерироваться 128 случайных чисел и помещаться в кеш для последующего использования.Когда 128 будут израсходованы, пакет случайных чисел будет регенерирован. Поэтому случайные числа Math.random предсказуемы, а случайные числа, сгенерированные алгоритмом, также называются псевдослучайными числами. Пока определено начальное число и определен случайный алгоритм, мы можем знать, каким будет следующее случайное число. Для получения подробной информации см.История случайных чисел.

  • Crypto.getRandomValues()

  Crypto.getRandomValues()метод позволяет получитьСоответствует криптографическим требованиямбезопасное случайное значение. Массив входящих параметров заполняется случайными значениями (вкриптографически случайный).window.crypto.getRandomValueРеализация использует 1024-битные торренты в браузерах Safari, Chrome и Opera.ARC4потоковый шифр.

var array = new Uint32Array(10);
window.crypto.getRandomValues(array);

console.log('Your lucky numbers:');
for (var i = 0; i < array.length; i++) {
  console.log(array[i]);
}

Использованная литература:История случайных чисел | Crypto.getRandomValues() | Как назвать игральные карты в JavaScript с помощью window.crypto.getrandomvalues?

Форма пятьдесят один:forEachПросто простая обертка вокруг цикла for? Ваше понимание forEach может быть неправильным

   Давайте посмотрим на следующееforEachРеализация:

Array.prototype.forEachCustom = function (fn, context) {
  context = context || arguments[1];
  if (typeof fn !== 'function') {
    throw new TypeError(fn + 'is not a function');
  }

  for (let i = 0; i < this.length; i++) {
    fn.call(context, this[i], i, this);
  }
};

   Мы обнаружили, что приведенная выше реализация кода на самом деле представляет собой простую инкапсуляцию цикла for, что кажется несложным, потому что во многих случаяхforEachметод используется вместо цикла for для завершения обхода массива. На самом деле нет, давайте взглянем на следующий тестовый код:

//  示例1
const items = ['', 'item2', 'item3', , undefined, null, 0];
items.forEach((item) => {
  console.log(item); //  依次打印:'',item2,item3,undefined,null,0
});
items.forEachCustom((item) => {
  console.log(item); // 依次打印:'',item2,item3,undefined,undefined,null,0
});
// 示例2
let arr = new Array(8);
arr.forEach((item) => {
  console.log(item); //  无打印输出
});
arr[1] = 9;
arr[5] = 3;
arr.forEach((item) => {
  console.log(item); //  打印输出:9 3
});
arr.forEachCustom((item) => {
  console.log(item); // 打印输出:undefined 9 undefined*3  3 undefined*2
});

   Мы обнаружили, что результаты выполнения тестового кода forEachCustom и нативного forEach не совпадают. Что касается реализации каждой новой функции, на самом деле мы можемДокументация ECMAНайдите ответ в:

forEach

   Мы можем обнаружить, что 8-й элемент, который фактически выполняет операцию обхода, реализуется через цикл while,Условием завершения цикла является длина полученного ранее массива (то есть изменение длины массива в дальнейшем не повлияет на количество обходов), в цикле while индекс текущего элемента обхода будет сначала преобразуется в строку, и массив будет оцениваться методом HasProperty.Есть ли инициализированный элемент, соответствующий индексу в объекте, если да, то получите соответствующее значение и выполните обратный вызов, если нет, функция обратного вызова будет не будет выполняться, но следующий элемент будет пройден напрямую.

Похоже на то,forEach ничего не делает с неинициализированными значениями (разреженными массивами), поэтому есть разница в значении и количестве значений, напечатанных пользовательским методом в Примере 1 и Примере 2. Затем нам нужно лишь немного изменить предыдущую реализацию, чтобы реализовать наш собственный метод forEach:

Array.prototype.forEachCustom = function (fn, context) {
  context = context || arguments[1];
  if (typeof fn !== 'function') {
    throw new TypeError(fn + 'is not a function');
  }

  let len = this.length;
  let k = 0;
  while (k < len) {
    // 下面是两种实现思路,ECMA文档使用的是HasProperty,在此,使用in应该比hasOwnProperty更确切
    // if (this.hasOwnProperty(k)) {
    //   fn.call(context, this[k], k, this);
    // };
    if (k in this) {
      fn.call(context, this[k], k, this);
    }
    k++;
  }
};

   Снова запустите тестовый столбец Примера 1 и Примера 2 и убедитесь, что выходные данные согласуются с исходным forEach.

   Из документации мы также обнаружили, чтоКоличество циклов while было определено до итерации, и цикл while выполняется, это не означает, что функция обратного вызова будет выполняться, мы пытаемся изменить массив во время итерации:

// 示例3
var words = ['one', 'two', 'three', 'four'];
words.forEach(function (word) {
  console.log(word); // one,two,four(在迭代过程中删除元素,导致three被跳过,因为three的下标已经变成1,而下标为1的已经被遍历了过)
  if (word === 'two') {
    words.shift();
  }
});
words = ['one', 'two', 'three', 'four']; // 重新初始化数组进行forEachCustom测试
words.forEachCustom(function (word) {
  console.log(word); // one,two,four
  if (word === 'two') {
    words.shift();
  }
});
// 示例4
var arr = [1, 2, 3];
arr.forEach((item) => {
  if (item == 2) {
    arr.push(4);
    arr.push(5);
  }
  console.log(item); // 1,2,3(迭代过程中在末尾增加元素,并不会使迭代次数增加)
});
arr = [1, 2, 3];
arr.forEachCustom((item) => {
  if (item == 2) {
    arr.push(4);
    arr.push(5);
  }
  console.log(item); // 1,2,3
});

  Вышеописанный процесс показывает нам, что когда мы сталкиваемся с проблемами на работе, которые отличаются от наших ожиданий, мы можем перейти кОфициальная документация ECMAискать ответы.

Здесь вы можете сослаться на мою предыдущую статью:JavaScript это легко? Так правильно ли вы понимаете forEach?

Введите 52: Имена файлов Git чувствительны к регистру, вы когда-нибудь сажали яму?

Автор столкнулся с ямой при разработке интерфейса для Mac около двух лет назад: код работает нормально локально, но я обнаружил, что после нажатия на git после автоматического развертывания сообщается об ошибке После долгого расследования я наконец обнаружил, что было имя файла, в котором не обращал внимания на регистр, переименовал файл, но git не распознал изменение, поэтому файл не мог быть найден после авторазвертывания. Решение выглядит следующим образом:

  • Проверьте настройки git:git config --get core.ignorecase
  • Git по умолчанию нечувствителен к регистру, поэтому, когда вы меняете регистр имен файлов/папок, git не считает, что вы внесли изменения (статус git не предложит вам внести изменения)
  • Измените настройки, чтобы решить:git config core.ignorecase false

   Таким образом, git может распознавать изменения в регистре имени файла. Рекомендуется, когда мы пишем проекты с использованием React, последняя буква имени файла должна быть заглавной.

Ссылаться на:В Git при изменении имени файла с заглавной буквы

Тип 53: 0,1, которое вы видите, на самом деле не 0,1 - 0,1 + 0,2 !== 0,3, о чем говорил старик, на этот раз мы поговорим о чем-то другом

  0.1 + 0.2 !== 0.3Это проблема с длинным разговором, и вы также поймете корня корня: JS использует версию двойной точности IEEE 754 (64-битную) и до тех пор, пока язык IEEE 754 имеет такие проблемы. Детали могут просматривать статью перед авторомОбоснование 0,1 + 0,2 != 0,3, мы обсуждаем только решение в этом разделе.

  • Поскольку IEEE 754 имеет проблемы с точностью,что x = 0,1 получает 0,1?

Поскольку при хранении чисел с плавающей запятой фиксированная длина мантиссы (мантиссы) составляет 52 бита плюс пропущенный один бит, максимальное число, которое может быть представлено, равно 2^53=9007199254740992, а соответствующая мантисса в экспоненциальном представлении равна 9,007199254740992. , что также является максимальным числом, которое может представлять JS.Точность представления. Его длина равна 16, поэтому вы можете использовать toPrecision(16) для точных операций, а избыточная точность будет автоматически округлена в большую сторону. Итак, есть:

toPrecision

0.10000000000000000555.toPrecision(16)
// 返回 0.1000000000000000,去掉末尾的零后正好为 0.1

// 但你看到的 `0.1` 实际上并不是 `0.1`。不信你可用更高的精度试试:
0.1.toPrecision(21) = 0.100000000000000005551
  • toFixedУстановите точное количество цифр

  toFixed()Метод округляет число до указанного числа знаков после запятой.Синтаксис:NumberObject.toFixed(num).

// 保留两位小数
console.log((0.1 + 0.2).toFixed(2)); // 0.30
  • Number.EPSILON

   У вас должно сложиться впечатление, что в школьном или математическом анализе в колледже и числовой аппроксимации при доказательстве равенства двух значений мы приближаем их разность к сколь угодно малому числу. Тогда естественно подумать о том, чтобы сделать сумму 0,1 + 0,2 минус 0,3 меньше произвольно малого числа, Например, мы можем судить о том, равны ли они, по тому, меньше ли их разность 0,0000000001.

   Фактически, ES6 добавил очень маленькую константу к объекту NumberNumber.EPSILON. Согласно спецификации, он представляет собой разницу между 1 и наименьшим числом с плавающей запятой, большим 1.Number.EPSILONНа самом деле это наименьшая точность, которую может представить JavaScript. Если ошибка меньше этого значения, можно считать, что она не имеет смысла, то есть ошибки нет.

console.log(0.1 + 0.2 - 0.3 < Number.EPSILON); // true
  • Преобразуйте в целое число или строку, а затем выполните операцию суммирования

Чтобы избежать разницы в точности, нам нужно умножить вычисляемое число на n-ю степень 10, преобразовать его в целое число, которое компьютер сможет точно распознать, а затем разделить его на n-ю степень 10. Это как большинство языков программирования справляются с разницей в точности.Да, давайте позаимствуем это, чтобы иметь дело с ошибками точности с плавающей запятой в JS.

Передайте значение n, возведенное в n-ю степень:

formatNum = function (f, digit) {
  var m = Math.pow(10, digit);
  return parseInt(f * m, 10) / m;
};
var num1 = 0.1;
var num2 = 0.2;
console.log(num1 + num2);
console.log(formatNum(num1 + num2, 1));

Автоматически вычислить значение n, возведенное в n-ю степень:

/**
 * 精确加法
 */
function add(num1, num2) {
  const num1Digits = (num1.toString().split('.')[1] || '').length;
  const num2Digits = (num2.toString().split('.')[1] || '').length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}
add(0.1, 0.2); // 0.3
  • Используйте библиотеку классов:

   Обычно такого рода расчеты, требующие высокой точности, должны быть переданы серверной части для расчета и хранения, потому что серверная часть имеет зрелые библиотеки для решения такого рода вычислительных задач. Также есть несколько хороших библиотек для интерфейса:

Использованная литература:Проблемы точности с арифметикой с плавающей запятой в JavaScript | Ловушки с плавающей запятой в JavaScript и решения

Пятьдесят четвертый стиль: все напоминания о выпуске основаны на реве — как реализовать обнаружение страницы, обновление и подсказку в чистом интерфейсе?

В процессе разработки, когда часто встречаются обновления страниц и выпуски версий, пользователям нужно сказать обновить страницу.Некоторые операторы и тестировщики даже думают, что переключение меню, а затем переключение обратно означает обновление ресурсов веб-страницы, а некоторые не могут отличить обычное обновление.В отличие от сильной кисти, реализована функция обнаружения обновления страницы, и обновление страницы автоматически предлагает пользователю обновить страницу.

  Основная идея: добавить хэш к имени файла js при использовании webpack для настройки пакета и компиляции, а затем использовать js для${window.location.origin}/index.htmlОтправьте запрос, разберите хэш имени файла js, представленного в html-файле, и сравните, согласуется ли хэш текущего js с хэшем новой версии.Если они несовместимы, пользователю будет предложено обновить версия.

// uploadUtils.jsx
import React from 'react';
import axios from 'axios';
import { notification, Button } from 'antd';

// 弹窗是否已展示(可以改用闭包、单例模式等实现,看起来会更有逼格一点)
let uploadNotificationShow = false;

// 关闭notification
const close = () => {
  uploadNotificationShow = false;
};

// 刷新页面
const onRefresh = (new_hash) => {
  close();
  // 更新localStorage版本号信息
  window.localStorage.setItem('XXXSystemFrontVesion', new_hash);
  // 刷新页面
  window.location.reload(true);
};

// 展示提示弹窗
const openNotification = (new_hash) => {
  uploadNotificationShow = true;
  const btn = (
    <Button type='primary' size='small' onClick={() => onRefresh(new_hash)}>
      确认更新
    </Button>
  );
  // 这里不自动执行更新的原因是:
  // 考虑到也许此时用户正在使用系统甚至填写一个很长的表单,那你直接刷新了页面,或许会被掐死的,哈哈
  notification.open({
    message: '版本更新提示',
    description: '检测到系统当前版本已更新,请刷新后使用。',
    btn,
    // duration为0时,notification不自动关闭
    duration: 0,
    onClose: close,
  });
};

// 获取hash
export const getHash = () => {
  // 如果提示弹窗已展示,就没必要执行接下来的检查逻辑了
  if (!uploadNotificationShow) {
    // 在 js 中请求首页地址,这样不会刷新界面,也不会跨域
    axios
      .get(`${window.location.origin}/index.html?time=${new Date().getTime()}`)
      .then((res) => {
        // 匹配index.html文件中引入的js文件是否变化(具体正则,视打包时的设置及文件路径而定)
        let new_hash = res.data && res.data.match(/\/static\/js\/main.(.*).js/);
        // console.log(res, new_hash);
        new_hash = new_hash ? new_hash[1] : null;
        // 查看本地版本
        let old_hash = localStorage.getItem('XXXSystemFrontVesion');
        if (!old_hash) {
          // 如果本地没有版本信息(第一次使用系统),则直接执行一次额外的刷新逻辑
          onRefresh(new_hash);
        } else if (new_hash && new_hash != old_hash) {
          // 本地已有版本信息,但是和新版不同:版本更新,弹出提示
          openNotification(new_hash);
        }
      });
  }
};

Пример использования:

import { getHash } from './uploadUtils';

let timer = null;
componentDidMount() {
    getHash();
    timer = setInterval(() => {
      getHash();
      // 10分钟检测一次
    }, 600000)
  }

  componentWillUnmount () {
      // 页面卸载时清除
    clearInterval(timer);
  }

   комбинированныйConsole ImporterПросмотр непосредственно в панели консоли:

uploadpage

Вы также можете использовать описанный выше метод на более высоком уровне.При сборке в каталоге того же уровня, что и index.html, автоматически создается файл json, включая хэш-информацию нового файла.При проверке версии вам нужно только Непосредственно запросите этот JSON-файл для сравнения, чтобы уменьшить избыточность данных.

Использованная литература:Чистая интерфейсная реализация запроса на обновление обнаружения страницы

Эта статья была опубликована вличный блог,Добро пожаловатьКоррекция и звезда.