Недавно я разобрался с высокочастотными вопросами фронтенд-интервью и поделился ими со всеми, чтобы учиться вместе. Если у вас есть какие-либо вопросы, пожалуйста, поправьте меня!
Статьи из серии вопросов для фронтенд-интервью:
【1】HTML-глава "2021" с высокочастотными вопросами для фронтенд-интервью
【2】Сводка вопросов по высокочастотным фронтенд-интервью «2021» CSS статьи
【3】Глава "2021" о JavaScript, посвященная высокочастотным вопросам для фронтенд-интервью (часть 1)
【4】Резюме статьи JavaScript "2021" о часто задаваемых вопросах на собеседовании (часть 2)
【5】Глава Vue "2021" о часто задаваемых вопросах на собеседовании (часть 1)
【6】«2021», «Краткое изложение высокочастотных лиц переднего торца Vue статей (под)
【7】Сводка вопросов по высокочастотным фронтенд-интервью «2021» React (часть 1)
【8】Сводка вопросов по высокочастотным фронтенд-интервью «2021» React (часть 2)
【9】Резюме вопросов высокочастотного внешнего интервью "2021" компьютерной сети
[10]«2021» высокочастотные вопросы о фронтенд-интервью краткое изложение принципов браузера
【11】Сводка вопросов по оптимизации производительности высокочастотных предварительных интервью «2021»
【12】«2021» Рукописный свод кода высокочастотных вопросов на собеседовании перед интерфейсом
【13】Сводные результаты вывода кода высокочастотных вопросов внешнего интервью «2021»
1. Основы JavaScript
1. Рукописный объект.create
Идея: использовать входящий объект в качестве прототипа
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
2. Рукописный метод instanceof
Оператор instanceof используется для определения того, появляется ли свойство прототипа конструктора где-либо в цепочке прототипов объекта.
Этапы реализации:
- Сначала получите тип прототипа
- затем получить прототип объекта
- Затем продолжайте цикл, чтобы определить, равен ли прототип объекта прототипу типа, пока прототип объекта не будет
null
, потому что цепочка прототипов заканчиваетсяnull
Реализация:
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left), // 获取对象的原型
prototype = right.prototype; // 获取构造函数的 prototype 对象
// 判断构造函数的 prototype 对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
3. Рукописный новый оператор
вызовnew
В процессе произойдут четыре вышеупомянутые вещи:
(1) Сначала создайте новый пустой объект
(2) Установите прототип, установите прототип объекта в прототип объекта функции.
(3) Пусть this функции указывает на этот объект, выполняет код конструктора (добавляет свойства к этому новому объекту)
(4) Определите тип возвращаемого значения функции.Если это тип значения, верните созданный объект. Если это ссылочный тип, возвращается объект этого ссылочного типа.
function objectFactory() {
let newObject = null;
let constructor = Array.prototype.shift.call(arguments);
let result = null;
// 判断参数是否是一个函数
if (typeof constructor !== "function") {
console.error("type error");
return;
}
// 新建一个空对象,对象的原型为构造函数的 prototype 对象
newObject = Object.create(constructor.prototype);
// 将 this 指向新建对象,并执行函数
result = constructor.apply(newObject, arguments);
// 判断返回对象
let flag = result && (typeof result === "object" || typeof result === "function");
// 判断返回结果
return flag ? result : newObject;
}
// 使用方法
objectFactory(构造函数, 初始化参数);
4. Рукописные обещания
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保存初始化状态
var self = this;
// 初始化状态
this.state = PENDING;
// 用于保存 resolve 或者 rejected 传入的值
this.value = null;
// 用于保存 resolve 的回调函数
this.resolvedCallbacks = [];
// 用于保存 reject 的回调函数
this.rejectedCallbacks = [];
// 状态转变为 resolved 方法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变,
if (self.state === PENDING) {
// 修改状态
self.state = RESOLVED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 状态转变为 rejected 方法
function reject(value) {
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变
if (self.state === PENDING) {
// 修改状态
self.state = REJECTED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.rejectedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 将两个方法传入函数执行
try {
fn(resolve, reject);
} catch (e) {
// 遇到错误时,捕获错误,执行 reject 函数
reject(e);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
onResolved =
typeof onResolved === "function"
? onResolved
: function(value) {
return value;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function(error) {
throw error;
};
// 如果是等待状态,则将函数加入对应列表中
if (this.state === PENDING) {
this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果状态已经凝固,则直接执行对应状态的函数
if (this.state === RESOLVED) {
onResolved(this.value);
}
if (this.state === REJECTED) {
onRejected(this.value);
}
};
5. Рукописное обещание.затем
then
метод возвращает новыйpromise
Пример, чтобыpromise
При изменении состояния (resolve
/ reject
при вызове), а затем выполнитьthen
функция, мы используемcallbacks
Массив сначала временно хранит переданную потом функцию, а затем вызывает ее при изменении состояния.
Итак, как гарантировать последнее**then**
Метод в предыдущем**then**
(возможно, асинхронно), а затем выполнить его после окончания?мы можем перейти кthen
функции и новыеpromise
изresolve
Вместеpush
к предыдущемуpromise
изcallbacks
Массив, прошлое и достижение эффекта:
- Преемственность: текущая
promise
Когда закончите, вызовите егоresolve
изменить статус, в этомresolve
будет вызываться по очередиcallbacks
Обратный вызов, это будет выполненоthen
метод в - После окончания: на предыдущем шаге, когда
then
После выполнения метод возвращает результат. Если результатом является простое значение, он напрямую вызывает новый метод.promise
изresolve
, пусть его состояние изменится, что, в свою очередь, вызовет новыйpromise
изcallbacks
Методы в массиве повторяются в цикле. . Если возвращаемый результат являетсяpromise
, вам нужно дождаться его завершения, прежде чем запускать новыйpromise
изresolve
, поэтому его можно найти в его результатеthen
вызовите новыйpromise
изresolve
then(onFulfilled, onReject){
// 保存前一个promise的this
const self = this;
return new MyPromise((resolve, reject) => {
// 封装前一个promise成功时执行的函数
let fulfilled = () => {
try{
const result = onFulfilled(self.value); // 承前
return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
}catch(err){
reject(err)
}
}
// 封装前一个promise失败时执行的函数
let rejected = () => {
try{
const result = onReject(self.reason);
return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
}catch(err){
reject(err)
}
}
switch(self.status){
case PENDING:
self.onFulfilledCallbacks.push(fulfilled);
self.onRejectedCallbacks.push(rejected);
break;
case FULFILLED:
fulfilled();
break;
case REJECT:
rejected();
break;
}
})
}
Уведомление:
- несколько подряд
then
Метод обратного вызова регистрируется синхронно, но регистрируется в другомcallbacks
массив, потому что каждый разthen
Вернуть новыйpromise
Примеры (см. примеры и рисунки выше) - После завершения регистрации выполняются асинхронные события в конструкторе, а асинхронные события вызываются последовательно после асинхронного завершения.
callbacks
Обратные вызовы, зарегистрированные заранее в массиве
6. Рукописное обещание.все
1) Основная идея
- Получает массив экземпляров Promise или объект с интерфейсом Iterator в качестве параметра
- Этот метод возвращает новый объект обещания,
- Пройдите входящие параметры, используйте Promise.resolve() для «упаковки» параметров в объект обещания.
- Все обратные вызовы параметров выполнены успешно, и массив возвращаемых значений находится в том же порядке, что и параметры.
- Если один из параметров в массиве дает сбой, активируется состояние сбоя, и первое сообщение об ошибке Promise с ошибкой используется в качестве сообщения об ошибке Promise.all.
2) Реализовать код
Вообще говоря, Promise.all используется для обработки нескольких одновременных запросов, а также для удобства построения данных страницы, запрашивая данные из разных интерфейсов, используемых страницей вместе.Однако, если один из интерфейсов дает сбой, несколько запросов также не получается, страница может не выйти, это зависит от степени связанности текущей страницы
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if(!Array.isArray(promises)){
throw new TypeError(`argument must be a array`)
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedResult = [];
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promises[i]).then(value=>{
resolvedCounter++;
resolvedResult[i] = value;
if (resolvedCounter == promiseNum) {
return resolve(resolvedResult)
}
},error=>{
return reject(error)
})
}
})
}
// test
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
})
7. Рукописное обещание.гонка
Параметром этого метода является массив экземпляров Promise, а затем, когда состояние Promise в массиве становится выполненным, выполняется метод обратного вызова, зарегистрированный до этого момента.можно изменить только один раз, тогда нам просто нужно внедрить метод разрешения объекта Promise, сгенерированного в Promise.race, в функцию обратного вызова в каждом экземпляре Promise в массиве.
Promise.race = function (args) {
return new Promise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject)
}
})
}
8. Функция защиты от сотрясений, написанная от руки
Функция anti-shake означает, что обратный вызов будет выполнен через n секунд после срабатывания события, и если событие сработает снова в течение этих n секунд, время будет перезапущено. Это можно использовать в некоторых событиях запросов кликов, чтобы избежать отправки нескольких запросов на серверную часть из-за нескольких кликов пользователя.
// 函数防抖的实现
function debounce(fn, wait) {
let timer = null;
return function() {
let context = this,
args = arguments;
// 如果此时存在定时器的话,则取消之前的定时器重新记时
if (timer) {
clearTimeout(timer);
timer = null;
}
// 设置定时器,使事件间隔指定事件后执行
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
9. Ручная функция дросселирования
Функция дросселирования относится к указанию единицы времени. В данном блоке времени выполняется функция обратного вызова, которая запускает событие, может выполняться только один раз. Если событие срабатывает несколько раз в том же единице времени, только один может вступить в силу. Дросселирование может использоваться в прослушивании событий функции прокрутки для уменьшения частоты вызовов событий через дросселирование событий.
// 函数节流的实现;
function throttle(fn, delay) {
let curTime = Date.now();
return function() {
let context = this,
args = arguments,
nowTime = Date.now();
// 如果两次时间间隔超过了指定时间,则执行函数。
if (nowTime - curTime >= delay) {
curTime = Date.now();
return fn.apply(context, args);
}
};
}
10. Функция оценки рукописного типа
function getType(value) {
// 判断数据是 null 的情况
if (value === null) {
return value + "";
}
// 判断数据是引用类型的情况
if (typeof value === "object") {
let valueClass = Object.prototype.toString.call(value),
type = valueClass.split(" ")[1].split("");
type.pop();
return type.join("").toLowerCase();
} else {
// 判断数据是基本数据类型的情况和函数的情况
return typeof value;
}
}
11. Функция рукописного вызова
Этапы реализации функции вызова:
- Чтобы определить, является ли вызывающий объект функцией, даже если мы определим его в прототипе функции, его можно вызвать с помощью call и других методов.
- Определите, существует ли объект входящего контекста, если нет, установите его в window.
- Обработать входящие параметры и перехватить все параметры после первого параметра.
- Как функция свойства объекта контекста.
- Используйте объект контекста, чтобы вызвать этот метод и сохранить возвращенный результат.
- Удалите только что добавленное свойство.
- Вернуть результат.
// call函数实现
Function.prototype.myCall = function(context) {
// 判断调用对象
if (typeof this !== "function") {
console.error("type error");
}
// 获取参数
let args = [...arguments].slice(1),
result = null;
// 判断 context 是否传入,如果未传入则设置为 window
context = context || window;
// 将调用函数设为对象的方法
context.fn = this;
// 调用函数
result = context.fn(...args);
// 将属性删除
delete context.fn;
return result;
};
12. Функция рукописного применения
Шаги реализации функции применения:
- Чтобы определить, является ли вызывающий объект функцией, даже если мы определим его в прототипе функции, его можно вызвать с помощью call и других методов.
- Определите, существует ли объект входящего контекста, если нет, установите его в window.
- Сделайте функцию свойством объекта контекста.
- Определить, передается ли значение параметра в
- Используйте объект контекста, чтобы вызвать этот метод и сохранить возвращенный результат.
- Удалить недавно добавленное свойство
- вернуть результат
// apply 函数实现
Function.prototype.myApply = function(context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
let result = null;
// 判断 context 是否存在,如果未传入则为 window
context = context || window;
// 将函数设为对象的方法
context.fn = this;
// 调用方法
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
// 将属性删除
delete context.fn;
return result;
};
13. Функция рукописного связывания
Этапы реализации функции привязки:
- Чтобы определить, является ли вызывающий объект функцией, даже если мы определим его в прототипе функции, его можно вызвать с помощью call и других методов.
- Сохраните ссылку на текущую функцию и получите остальные значения входящих параметров.
- Создайте функцию, которая возвращает
- Функция использует применение для привязки вызова функции. Необходимо оценить, используется ли функция в качестве конструктора. В это время вам нужно передать this текущей функции в вызов применения, а другие случаи передаются в указанный объект контекста.
// bind 函数实现
Function.prototype.myBind = function(context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
var args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 根据调用方式,传入不同绑定值
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
);
};
};
14. Реализация функции каррирования
Каррирование функций относится к методу преобразования функции, принимающей несколько аргументов, в ряд функций, принимающих один аргумент.
function curry(fn, args) {
// 获取函数需要的参数长度
let length = fn.length;
args = args || [];
return function() {
let subArgs = args.slice(0);
// 拼接得到现有的所有参数
for (let i = 0; i < arguments.length; i++) {
subArgs.push(arguments[i]);
}
// 判断参数的长度是否已经满足函数所需参数的长度
if (subArgs.length >= length) {
// 如果满足,执行函数
return fn.apply(this, subArgs);
} else {
// 如果不满足,递归返回科里化的函数,等待参数的传入
return curry.call(this, fn, subArgs);
}
};
}
// es6 实现
function curry(fn, ...args) {
return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}
15. Реализация запросов AJAX
AJAX — это аббревиатура асинхронного JavaScript и XML, которая относится к асинхронному взаимодействию JavaScript, который получает XML-документы с сервера для извлечения данных, а затем обновляет соответствующую часть текущей веб-страницы без обновления всей веб-страницы.
Шаги для создания запроса AJAX:
- Создайте объект XMLHttpRequest.
- на этом объектеСоздайте HTTP-запрос, используя открытый метод, параметры, требуемые открытым методом, — это метод запроса, адрес запроса, является ли он асинхронным, а также информация об аутентификации пользователя.
- Перед инициированием запроса этот объект может бытьДобавьте некоторую информацию и функции слушателя. Например, вы можете добавить информацию заголовка к запросу с помощью метода setRequestHeader. Вы также можете добавить к этому объекту функцию слушателя состояния. Объект XMLHttpRequest имеет в общей сложности 5 состояний.При изменении его состояния будет инициировано событие onreadystatechange.Вы можете настроить функцию слушателя на обработку результата после успешного выполнения запроса. Когда readyState объекта становится 4, это означает, что данные, возвращенные сервером, получены.В это время можно судить о статусе запроса.Если статус 2xx или 304, это означает, что возврат нормальный. В это время страницу можно обновить с помощью данных в ответе.
- Когда свойства объекта и функция слушателя установлены, последний вызовИспользуйте метод send, чтобы инициировать запрос к серверу, вы можете передать параметры в качестве тела данных для отправки.
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创建 Http 请求
xhr.open("GET", SERVER_URL, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功时
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
// 设置请求失败时的监听函数
xhr.onerror = function() {
console.error(this.statusText);
};
// 设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 请求
xhr.send(null);
16. Используйте Promise для переноса запросов AJAX
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
17. Реализуйте мелкое копирование
Поверхностная копия означает, что новый объект точно копирует значения атрибутов исходного объекта.Если копия является базовым типом данных, копируется значение базового типа данных, а если это ссылочный тип данных, адрес памяти копируется. Если адрес ссылочной памяти одного из объектов изменяется, другой объект также изменяется.
(1) Объект.назначить()
Object.assign()
Является методом копирования объектов в ES6. Первый принимаемый параметр — это целевой объект, а остальные параметры — исходный объект.Object.assign(target, source_1, ···)
, этот метод может реализовать поверхностную или глубокую копию одномерного объекта.
Уведомление:
- Если целевой объект и исходный объект имеют атрибуты с одинаковыми именами или если несколько исходных объектов имеют атрибуты с одинаковыми именами, более поздние атрибуты переопределяют более ранние атрибуты.
- Если функция имеет только один параметр, когда параметр является объектом, объект будет возвращен напрямую; когда параметр не является объектом, параметр будет преобразован в объект, а затем возвращен.
- потому что
null
а такжеundefined
не может быть преобразован в объект, поэтому первый параметр не может бытьnull
илиundefined
, будет сообщено об ошибке.
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
console.log(target); // {a: 1, b: 2, c: 3}
(2) Оператор спреда
Используйте оператор распространения для копирования свойств при создании буквального объекта. грамматика:let cloneObj = { ...obj };
let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
(3) Метод массива реализует мелкую копию массива
1) Массив.прототип.срез
-
slice()
Метод — это метод массива JavaScript, который возвращает выбранный элемент из существующего массива: Использование:array.slice(start, end)
, метод не изменяет исходный массив. - Этот метод имеет два параметра, оба из которых являются необязательными. Если ни один из параметров не указан, можно реализовать поверхностную копию массива.
let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false
2) Массив.прототип.concat
-
concat()
метод используется для объединения двух или более массивов. Этот метод не изменяет существующий массив, а возвращает новый массив. - Этот метод имеет два параметра, оба из которых являются необязательными. Если ни один из параметров не указан, можно реализовать поверхностную копию массива.
let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
console.log(arr.concat() === arr); //false
(4) Мелкая копия от руки
// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}// 浅拷贝的实现;
function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}
18. Внедрите глубокое копирование
- Мелкая копия:Поверхностная копия относится к копированию значения атрибута одного объекта в другой объект.Если значение какого-либо атрибута является ссылочным типом, то адрес ссылки будет скопирован в объект, поэтому два объекта будут иметь один и тот же ссылочный тип.Цитата . Неглубокие копии можно реализовать с помощью Object.assign и оператора распространения.
- Глубокая копия:Глубокая копия относится к поверхностной копии.Если значение свойства является ссылочным типом, оно создает новый ссылочный тип и копирует в него соответствующее значение, поэтому объект получает новый ссылочный тип вместо ссылки исходного типа. Глубокое копирование может быть реализовано с использованием двух функций JSON для некоторых объектов, но поскольку формат объекта JSON более строгий, чем у js, при наличии значения типа функции или символа в значении свойства преобразование завершится ошибкой.
(1) JSON.stringify()
-
JSON.parse(JSON.stringify(obj))
Это один из наиболее часто используемых методов глубокого копирования.JSON.stringify
Будуjs
Сериализация объекта (строка JSON), затем используйтеJSON.parse
десериализовать (восстановить)js
объект. - Этот способ может реализовать глубокое копирование просто и грубо, но проблемы все же есть.Если в копируемом объекте есть функции, неопределенные и символы, то при их использовании
JSON.stringify()
После обработки он исчезнет.
let obj1 = { a: 0,
b: {
c: 0
}
};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}
(2) _.cloneDeep метод библиотеки функций lodash
Библиотека также предоставляет _.cloneDeep для Deep Copy.
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
(3) Функция глубокого рукописного копирования
// 深拷贝的实现
function deepCopy(object) {
if (!object || typeof object !== "object") return;
let newObject = Array.isArray(object) ? [] : {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] =
typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
}
}
return newObject;
}
2. Обработка данных
1. Реализовать функции форматирования даты
войти:
dateFormat(new Date('2020-12-01'), 'yyyy/MM/dd') // 2020/12/01
dateFormat(new Date('2020-04-01'), 'yyyy/MM/dd') // 2020/04/01
dateFormat(new Date('2020-04-01'), 'yyyy年MM月dd日') // 2020年04月01日
const dateFormat = (dateInput, format)=>{
var day = dateInput.getDate()
var month = dateInput.getMonth() + 1
var year = dateInput.getFullYear()
format = format.replace(/yyyy/, year)
format = format.replace(/MM/,month)
format = format.replace(/dd/,day)
return format
}
2. Поменять местами значения a и b без использования временных переменных
Умное использование суммы и разности двух чисел:
a = a + b
b = a - b
a = a - b
3. Реализовать вывод массива не по порядку
Основные идеи реализации:
- Возьмите первый элемент массива, случайным образом сгенерируйте значение индекса и замените первый элемент на элемент, соответствующий этому индексу.
- Во второй раз возьмите второй элемент массива данных, случайным образом сгенерируйте значение индекса, отличное от значения с индексом 1, и замените второй элемент элементом, соответствующим значению индекса.
- Следуйте приведенным выше правилам, пока обход не будет завершен.
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < arr.length; i++) {
const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(arr)
Другой способ - пройти в обратном порядке:
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,
randomIndex,
temp;
while (length) {
randomIndex = Math.floor(Math.random() * length--);
temp = arr[length];
arr[length] = arr[randomIndex];
arr[randomIndex] = temp;
}
console.log(arr)
4. Реализуйте сумму элементов массива
- обр=[1,2,3,4,5,6,7,8,9,10], сумма
let arr=[1,2,3,4,5,6,7,8,9,10]
let sum = arr.reduce( (total,i) => total += i,0);
console.log(sum);
- обр=[1,2,3,[[4,5],6],7,8,9], сумма
var = arr=[1,2,3,[[4,5],6],7,8,9]
let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);
console.log(arr);
Рекурсивная реализация:
let arr = [1, 2, 3, 4, 5, 6]
function add(arr) {
if (arr.length == 1) return arr[0]
return arr[0] + add(arr.slice(1))
}
console.log(add(arr)) // 21
5. Сгладить массив
(1) Рекурсивная реализация
Обычная рекурсивная идея проста для понимания. Это обход по элементам посредством рекурсивной рекурсии. Если каждый элемент по-прежнему является массивом, то продолжайте обход вниз и используйте метод рекурсивной программы для реализации каждого элемента массива. связь:
let arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
}
flatten(arr); // [1, 2, 3, 4,5]
(2) уменьшить итерацию функции
Как видно из обычной рекурсивной функции выше, на самом деле она заключается в обработке каждого элемента массива.На самом деле, сокращение можно использовать и для реализации склейки массивов, тем самым упростив код первого метода. код выглядит следующим образом:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.reduce(function(prev, next){
return prev.concat(Array.isArray(next) ? flatten(next) : next)
}, [])
}
console.log(flatten(arr));// [1, 2, 3, 4,5]
(3) Реализация оператора спреда
Реализация этого метода использует оператор распространения и некоторый метод, которые используются вместе для достижения цели выравнивания массива:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
(4) разделить и toString
Вы можете использовать методы split и toString для выравнивания массива.Поскольку массив будет иметь метод toString по умолчанию, вы можете напрямую преобразовать массив в строку, разделенную запятыми, а затем использовать метод split для повторного преобразования строку в массив. , как показано в следующем коде:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.toString().split(',');
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
С помощью этих двух методов многомерные массивы могут быть напрямую преобразованы в строки, соединенные запятыми, а затем повторно разделены на массивы.
(5) квартира в ES6
Мы также можем напрямую вызвать плоский метод в ES6, чтобы добиться выравнивания массива. Синтаксис плоского метода:arr.flat([depth])
где depth — параметр flat, а depth — глубина расширения массива, который можно передать (по умолчанию не заполняется, значение равно 1), то есть массив расширяется на один слой. Если количество слоев неизвестно, параметр можно передать в Infinity, что означает, что независимо от того, сколько слоев будет развернуто:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.flat(Infinity);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
Видно, что массив с двумя уровнями вложенности добился нужного эффекта, установив для параметра плоского метода значение Infinity. На самом деле его тоже можно поставить на 2, и такого эффекта тоже можно добиться. В процессе программирования, если количество вложенных уровней массива неизвестно, лучше всего использовать Infinity напрямую для достижения выравнивания.(6) Обычные методы и методы JSONМетод ToString использовался в четвертом методе, который по-прежнему использует первый метод преобразования JSON.stringify строки, а затем отфильтровывается с помощью регулярного выражения в массиве строк в квадратных скобках, и, наконец, использование JSON.parse преобразует его в массив:
let arr = [1, [2, [3, [4, 5]]], 6];
function flatten(arr) {
let str = JSON.stringify(arr);
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
return JSON.parse(str);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
6. Реализовать дедупликацию массива
Учитывая неупорядоченный массив, запросите удаление повторяющихся чисел из массива и верните новый массив без дубликатов.
Подход ES6 (с использованием коллекций структур данных):
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
Метод ES5: используйте карту для хранения уникальных номеров
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
uniqueArray(array); // [1, 2, 3, 5, 9, 8]
function uniqueArray(array) {
let map = {};
let res = [];
for(var i = 0; i < array.length; i++) {
if(!map.hasOwnProperty([array[i]])) {
map[array[i]] = 1;
res.push(array[i]);
}
}
return res;
}
7. Реализуйте плоский метод массива
function _flat(arr, depth) {
if(!Array.isArray(arr) || depth <= 0) {
return arr;
}
return arr.reduce((prev, cur) => {
if (Array.isArray(cur)) {
return prev.concat(_flat(cur, depth - 1))
} else {
return prev.concat(cur);
}
}, []);
}
8. Реализуйте метод push массива
let arr = [];
Array.prototype.push = function() {
for( let i = 0 ; i < arguments.length ; i++){
this[this.length] = arguments[i] ;
}
return this.length;
}
9. Реализуйте метод фильтра массива
Array.prototype._filter = function(fn) {
if (typeof fn !== "function") {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0, len = this.length; i < len; i++) {
fn(this[i]) && res.push(this[i]);
}
return res;
}
10. Реализуйте метод массива MAP
Array.prototype._map = function(fn) {
if (typeof fn !== "function") {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0, len = this.length; i < len; i++) {
res.push(fn(this[i]));
}
return res;
}
11. Реализуйте метод повтора строки
Введите строку s и количество раз, которое она повторяется, и выведите повторяющиеся результаты, например, введите abc, 2, выведите abcabc.
function repeat(s, n) {
return (new Array(n + 1)).join(s);
}
Рекурсивный:
function repeat(s, n) {
return (n > 0) ? s.concat(repeat(s, --n)) : "";
}
12. Реализовать переключение строк
Добавьте метод в цепочку прототипов строки для реализации переворота строки:
String.prototype._reverse = function(a){
return a.split("").reverse().join("");
}
var obj = new String();
var res = obj._reverse ('hello');
console.log(res); // olleh
Следует отметить, что определенный метод должен вызываться после создания экземпляра объекта, иначе метод не может быть найден.
13. Разделяйте тысячи цифр запятыми
Числа имеют десятичную версию:
let format = n => {
let num = n.toString() // 转成字符串
let decimals = ''
// 判断是否有小数
num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimals
let len = num.length
if (len <= 3) {
return num
} else {
let temp = ''
let remainder = len % 3
decimals ? temp = '.' + decimals : temp
if (remainder > 0) { // 不是3的整数倍
return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp
} else { // 是3的整数倍
return num.slice(0, len).match(/\d{3}/g).join(',') + temp
}
}
}
format(12323.33) // '12,323.33'
Номер без десятичной версии:
let format = n => {
let num = n.toString()
let len = num.length
if (len <= 3) {
return num
} else {
let remainder = len % 3
if (remainder > 0) { // 不是3的整数倍
return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',')
} else { // 是3的整数倍
return num.slice(0, len).match(/\d{3}/g).join(',')
}
}
}
format(1232323) // '1,232,323'
14. Реализовать добавление неотрицательных больших целых чисел
JavaScript имеет ограничения на диапазон значений, ограничения следующие:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_VALUE // 5e-324
Number.MIN_SAFE_INTEGER // -9007199254740991
Если вы хотите иметь дело с очень большим целым числом (> Number.MAX_SAFE_INTEGER
) Для добавления, но также хочется выводить общую форму, то использование + не достижимо, как только число превыситNumber.MAX_SAFE_INTEGER
Числа будут немедленно преобразованы в экспоненциальное представление, и точность чисел будет отличаться от прежней.
Реализуйте алгоритм для добавления больших чисел:
function sumBigNumber(a, b) {
let res = '';
let temp = 0;
a = a.split('');
b = b.split('');
while (a.length || b.length || temp) {
temp += ~~a.pop() + ~~b.pop();
res = (temp % 10) + res;
temp = temp > 9
}
return res.replace(/^0+/, '');
}
Его основные идеи заключаются в следующем:
- Во-первых, используйте строки для хранения больших чисел, чтобы математическое представление чисел не менялось.
- Инициализируйте res, temp, чтобы сохранить промежуточные результаты вычислений, и преобразуйте две строки в массив для добавления каждого бита.
- Сложите соответствующие биты двух массивов.Результат сложения двух чисел может быть больше 10, поэтому может потребоваться только выполнить операцию остатка на 10 и сохранить результат в текущем бите.
- Определите, больше ли текущий бит 9, то есть будет ли он перенесен, и если да, присвойте temp значение true, потому что в операции сложения true будет автоматически неявно преобразовано в 1 для следующего добавления.
- Повторяйте вышеуказанные операции до конца расчета
13. Реализовать добавление (1) (2) (3)
Концепция каррирования функций. Каррирование — это метод преобразования функции, которая принимает несколько параметров, в функцию, которая принимает один параметр и возвращает новую функцию, которая принимает оставшиеся параметры и возвращает результат.
1) Грубая версия
function add (a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
console.log(add(1)(2)(3)); // 6
2) Карри решение
- Длина параметра фиксирована
var add = function (m) {
var temp = function (n) {
return add(m + n);
}
temp.toString = function () {
return m;
}
return temp;
};
console.log(add(3)(4)(5)); // 12
console.log(add(3)(6)(9)(25)); // 43
Для add(3)(4)(5) процесс выполнения выглядит следующим образом:
- Сначала выполнить add(3), в это время m=3, и вернуть функцию temp;
- Выполните temp(4), выполните add(m+n) в этой функции, n — это значение 4, переданное за это время, а значение m по-прежнему равно 3 на предыдущем шаге, поэтому add(m+n)=add( 3+4) =add(7), в это время m=7, и возвращает временную функцию
- Выполните temp(5), выполните add(m+n) в этой функции, n — это значение 5, переданное за это время, а значение m по-прежнему равно 7 на предыдущем шаге, поэтому add(m+n)=add( 7+5) =add(12), в это время m=12, и вернуть временную функцию
- Поскольку параметры не передаются позже, возвращаемая временная функция не выполняется, а печатается.Друзья, которые знают JS, знают, что toString объекта — это метод модификации объекта для преобразования строки, поэтому функция toString временной функции в коде возвращает значение m, а значение m — это значение m=12, когда функция выполнялась на последнем шаге, поэтому возвращаемое значение равно 12.
- Длина параметра не фиксирована
function add (...args) {
//求和
return args.reduce((a, b) => a + b)
}
function currying (fn) {
let args = []
return function temp (...newArgs) {
if (newArgs.length) {
args = [
...args,
...newArgs
]
return temp
} else {
let val = fn.apply(this, args)
args = [] //保证再次调用时清空
return val
}
}
}
let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4, 5)()) //15
console.log(addCurry(1)(2)(3, 4, 5)()) //15
console.log(addCurry(1)(2, 3, 4, 5)()) //15
14. Преобразовать массив класса в массив
Существует несколько способов преобразования массивов классов в массивы:
- Преобразование реализуется вызовом метода slice массива
Array.prototype.slice.call(arrayLike);
- Преобразование реализуется вызовом метода splice массива
Array.prototype.splice.call(arrayLike, 0);
- Преобразование реализуется вызовом метода concat массива с применением
Array.prototype.concat.apply([], arrayLike);
- Преобразование достигается методом Array.from
Array.from(arrayLike);
15. Суммирование с помощью сокращения
обр = [1,2,3,4,5,6,7,8,9,10], сумма
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.reduce((prev, cur) => { return prev + cur }, 0)
обр = [1,2,3,[[4,5],6],7,8,9], сумма
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.flat(Infinity).reduce((prev, cur) => { return prev + cur }, 0)
arr = [{a:1, b:3}, {a:2, b:3, c:4}, {a:3}], сумма
let arr = [{a:9, b:3, c:4}, {a:1, b:3}, {a:3}]
arr.reduce((prev, cur) => {
return prev + cur["a"];
}, 0)
16. Преобразование объекта js в древовидную структуру
// 转换前:
source = [{
id: 1,
pid: 0,
name: 'body'
}, {
id: 2,
pid: 1,
name: 'title'
}, {
id: 3,
pid: 2,
name: 'div'
}]
// 转换为:
tree = [{
id: 1,
pid: 0,
name: 'body',
children: [{
id: 2,
pid: 1,
name: 'title',
children: [{
id: 3,
pid: 1,
name: 'div'
}]
}
}]
Код:
function jsonToTree(data) {
// 初始化结果数组,并判断输入数据的格式
let result = []
if(!Array.isArray(data)) {
return result
}
// 使用map,将当前对象的id与当前对象对应存储起来
let map = {};
data.forEach(item => {
map[item.id] = item;
});
//
data.forEach(item => {
let parent = map[item.pid];
if(parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
}
17. Сумма аргументов функции с использованием ES5 и ES6
ES5:
function sum() {
let sum = 0
Array.prototype.forEach.call(arguments, function(item) {
sum += item * 1
})
return sum
}
ЭС6:
function sum(...nums) {
let sum = 0
nums.forEach(function(item) {
sum += item * 1
})
return sum
}
18. Разобрать параметры URL на объекты
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',
id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
city: '北京', // 中文需解码
enabled: true, // 未指定值得 key 约定为 true
}
*/
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
let paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else { // 处理没有 value 的参数
paramsObj[param] = true;
}
})
return paramsObj;
}
3. Применение сценария
1. Циклическая печать красного, желтого и зеленого цветов.
Давайте рассмотрим типичную проблему и сравним несколько методов асинхронного программирования для этой задачи:Красный свет горит 3 секунды, зеленый свет горит 1 секунду, а желтый свет горит 2 секунды; как сделать, чтобы три индикатора загорались попеременно и многократно?
Три функции освещения:
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
Сложность этого вопроса в том, чтоТребуется включить индикатор "альтернативный повтор", а не "на один раз" и более.
(1) Реализовано с обратным вызовом
const task = (timer, light, callback) => {
setTimeout(() => {
if (light === 'red') {
red()
}
else if (light === 'green') {
green()
}
else if (light === 'yellow') {
yellow()
}
callback()
}, timer)
}
task(3000, 'red', () => {
task(2000, 'green', () => {
task(1000, 'yellow', Function.prototype)
})
})
Здесь есть ошибка: код завершает процесс только один раз, а красный, желтый и зеленый свет загораются только один раз после выполнения. Как сделать так, чтобы это повторялось поочередно?
Как упоминалось выше, рекурсия может рекурсивно зажечь цикл:
const step = () => {
task(3000, 'red', () => {
task(2000, 'green', () => {
task(1000, 'yellow', step)
})
})
}
step()
Обратите внимание на желтый свет в обратном вызове снова под названием STEP-методДля завершения цикла света.
(2) Реализовать с обещанием
const task = (timer, light) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (light === 'red') {
red()
}
else if (light === 'green') {
green()
}
else if (light === 'yellow') {
yellow()
}
resolve()
}, timer)
})
const step = () => {
task(3000, 'red')
.then(() => task(2000, 'green'))
.then(() => task(2100, 'yellow'))
.then(step)
}
step()
Обратный вызов здесь удален, и после того, как свет закончится, разрешите текущее обещание и по-прежнему используйте рекурсию.
(3) Реализовано с помощью async/await
const taskRunner = async () => {
await task(3000, 'red')
await task(2000, 'green')
await task(2100, 'yellow')
taskRunner()
}
taskRunner()
2. Печатайте 1,2,3,4 каждую секунду
// 使用闭包实现
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
// 使用 let 块级作用域
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
3. Детская задача на счет
В кругу 30 детей, пронумерованных от 1 до 30, в кругу считать согласно этому, ребенок, который считает 1, 2, 3 до 3, выходит из круга, а затем следующий ребенок снова считает 1, 2, 3, спросите Что это номер последнего оставшегося ребенка?
function childNum(num, count){
let allplayer = [];
for(let i = 0; i < num; i++){
allplayer[i] = i + 1;
}
let exitCount = 0; // 离开人数
let counter = 0; // 记录报数
let curIndex = 0; // 当前下标
while(exitCount < num - 1){
if(allplayer[curIndex] !== 0) counter++;
if(counter == count){
allplayer[curIndex] = 0;
counter = 0;
exitCount++;
}
curIndex++;
if(curIndex == num){
curIndex = 0
};
}
for(i = 0; i < num; i++){
if(allplayer[i] !== 0){
return allplayer[i]
}
}
}
childNum(30, 3)
4. Используйте Promise для асинхронной загрузки изображений
let imageAsync=(url)=>{
return new Promise((resolve,reject)=>{
let img = new Image();
img.src = url;
img.οnlοad=()=>{
console.log(`图片请求成功,此处进行通用操作`);
resolve(image);
}
img.οnerrοr=(err)=>{
console.log(`失败,此处进行失败的通用操作`);
reject(err);
}
})
}
imageAsync("url").then(()=>{
console.log("加载成功");
}).catch((error)=>{
console.log("加载失败");
})
5. Внедрите модель публикации-подписки
class EventCenter{
// 1. 定义事件容器,用来装事件数组
let handlers = {}
// 2. 添加事件方法,参数:事件名 事件方法
addEventListener(type, handler) {
// 创建新数组容器
if (!this.handlers[type]) {
this.handlers[type] = []
}
// 存入事件
this.handlers[type].push(handler)
}
// 3. 触发事件,参数:事件名 事件参数
dispatchEvent(type, params) {
// 若没有注册该事件则抛出错误
if (!this.handlers[type]) {
return new Error('该事件未注册')
}
// 触发事件
this.handlers[type].forEach(handler => {
handler(...params)
})
}
// 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
removeEventListener(type, handler) {
if (!this.handlers[type]) {
return new Error('事件无效')
}
if (!handler) {
// 移除事件
delete this.handlers[type]
} else {
const index = this.handlers[type].findIndex(el => el === handler)
if (index === -1) {
return new Error('无该绑定事件')
}
// 移除事件
this.handlers[type].splice(index, 1)
if (this.handlers[type].length === 0) {
delete this.handlers[type]
}
}
}
}
6. Найдите самые частотные слова в статье
function findMostWord(article) {
// 合法性判断
if (!article) return;
// 参数处理
article = article.trim().toLowerCase();
let wordList = article.match(/[a-z]+/g),
visited = [],
maxNum = 0,
maxWord = "";
article = " " + wordList.join(" ") + " ";
// 遍历判断单词出现次数
wordList.forEach(function(item) {
if (visited.indexOf(item) < 0) {
// 加入 visited
visited.push(item);
let word = new RegExp(" " + item + " ", "g"),
num = article.match(word).length;
if (num > maxNum) {
maxNum = num;
maxWord = item;
}
}
});
return maxWord + " " + maxNum;
}
7. Инкапсулируйте асинхронную выборку и используйте асинхронное ожидание
(async () => {
class HttpRequestUtil {
async get(url) {
const res = await fetch(url);
const data = await res.json();
return data;
}
async post(url, data) {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await res.json();
return result;
}
async put(url, data) {
const res = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify(data)
});
const result = await res.json();
return result;
}
async delete(url, data) {
const res = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify(data)
});
const result = await res.json();
return result;
}
}
const httpRequestUtil = new HttpRequestUtil();
const res = await httpRequestUtil.get('http://golderbrother.cn/');
console.log(res);
})();
8. Реализуйте наследование прототипов
Так называемое наследование цепочки прототипов состоит в том, чтобы сделать прототип нового экземпляра равным экземпляру родительского класса:
//父方法
function SupperFunction(flag1){
this.flag1 = flag1;
}
//子方法
function SubFunction(flag2){
this.flag2 = flag2;
}
//父实例
var superInstance = new SupperFunction(true);
//子继承父
SubFunction.prototype = superInstance;
//子实例
var subInstance = new SubFunction(false);
//子调用自己和父的属性
subInstance.flag1; // true
subInstance.flag2; // false
9. Реализуйте двустороннюю привязку данных
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
console.log('获取数据了')
},
set(newVal) {
console.log('数据更新了')
input.value = newVal
span.innerHTML = newVal
}
})
// 输入监听
input.addEventListener('keyup', function(e) {
obj.text = e.target.value
})
10. Реализировать простую маршрутизацию
// hash路由
class Route{
constructor(){
// 路由存储对象
this.routes = {}
// 当前hash
this.currentHash = ''
// 绑定this,避免监听时this指向改变
this.freshRoute = this.freshRoute.bind(this)
// 监听
window.addEventListener('load', this.freshRoute, false)
window.addEventListener('hashchange', this.freshRoute, false)
}
// 存储
storeRoute (path, cb) {
this.routes[path] = cb || function () {}
}
// 更新
freshRoute () {
this.currentHash = location.hash.slice(1) || '/'
this.routes[this.currentHash]()
}
}
11. Реализуйте последовательность Фибоначчи
// 递归
function fn (n){
if(n==0) return 0
if(n==1) return 1
return fn(n-2)+fn(n-1)
}
// 优化
function fibonacci2(n) {
const arr = [1, 1, 2];
const arrLen = arr.length;
if (n <= arrLen) {
return arr[n];
}
for (let i = arrLen; i < n; i++) {
arr.push(arr[i - 1] + arr[ i - 2]);
}
return arr[arr.length - 1];
}
// 非递归
function fn(n) {
let pre1 = 1;
let pre2 = 1;
let current = 2;
if (n <= 2) {
return current;
}
for (let i = 2; i < n; i++) {
pre1 = pre2;
pre2 = current;
current = pre1 + pre2;
}
return current;
}
12. Самая длинная неповторяющаяся длина строки
Используйте скользящее окно для установки символов без повторения и пронумеруйте символы, чтобы записать максимальное значение. Используйте карту для ведения индекса символов, и если вы встретите одного и того же персонажа, просто переместите левую границу. Запишите максимальную длину во время движения:
var lengthOfLongestSubstring = function (s) {
let map = new Map();
let i = -1
let res = 0
let n = s.length
for (let j = 0; j < n; j++) {
if (map.has(s[j])) {
i = Math.max(i, map.get(s[j]))
}
res = Math.max(res, j - i)
map.set(s[j], j)
}
return res
};
13. Реализуйте setInterval с помощью setTimeout
Роль setInterval заключается в выполнении функции через определенные промежутки времени, но на самом деле это выполнение выполняется не сразу, когда приходит время, а в добавлении событий в очередь событий через равные промежутки времени, только когда текущий стек выполнения пуст. Только после этого выполнение события может быть выведено из очереди событий. Поэтому может случиться так, что текущий стек выполнения занимает много времени для выполнения, что приводит к накоплению событий, добавленных несколькими таймерами в очередь событий.Когда стек выполнения заканчивается, эти события будут выполняться последовательно, поэтому интервал не может быть достигнутым Эффект времени выполнения.
Из-за этого недостатка setInterval мы можем использовать рекурсивные вызовы setTimeout для имитации setInterval, чтобы гарантировать, что завершится только одно событие, мы вызовем следующее событие таймера, что решает проблему setInterval.
Идея реализации состоит в том, чтобы использовать рекурсивные функции для непрерывного выполнения setTimeout для достижения эффекта setInterval.
function mySetInterval(fn, timeout) {
// 控制器,控制定时器是否继续执行
var timer = {
flag: true
};
// 设置递归函数,模拟定时器执行。
function interval() {
if (timer.flag) {
fn();
setTimeout(interval, timeout);
}
}
// 启动定时器
setTimeout(interval, timeout);
// 返回控制器
return timer;
}
14. Реализовать jsonp
// 动态的加载js文件
function addScript(src) {
const script = document.createElement('script');
script.src = src;
script.type = "text/javascript";
document.body.appendChild(script);
}
addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) {
console.log(res);
}
// 接口返回的数据格式
handleRes({a: 1, b: 2});
15. Определите, есть ли циклическая ссылка на объект
С объектами циклических ссылок проблем нет, но при сериализации могут возникнуть проблемы, например вызовJSON.stringify()
Сериализация объекта этого класса сообщит об ошибке:Converting circular structure to JSON.
Чтобы определить, существует ли циклическая ссылка в объекте, можно использовать следующие методы:
const isCycleObject = (obj,parent) => {
const parentArr = parent || [obj];
for(let i in obj) {
if(typeof obj[i] === 'object') {
let flag = false;
parentArr.forEach((pObj) => {
if(pObj === obj[i]){
flag = true;
}
})
if(flag) return true;
flag = isCycleObject(obj[i],[...parentArr,obj[i]]);
if(flag) return true;
}
}
return false;
}
const a = 1;
const b = {a};
const c = {b};
const o = {d:{a:3},c}
o.c.b.aa = a;
console.log(isCycleObject(o)
Найдите целевое значение упорядоченного двумерного массива:
var findNumberIn2DArray = function(matrix, target) {
if (matrix == null || matrix.length == 0) {
return false;
}
let row = 0;
let column = matrix[0].length - 1;
while (row < matrix.length && column >= 0) {
if (matrix[row][column] == target) {
return true;
} else if (matrix[row][column] > target) {
column--;
} else {
row++;
}
}
return false;
};
Двумерные массивы напечатаны наклонно:
function printMatrix(arr){
let m = arr.length, n = arr[0].length
let res = []
// 左上角,从0 到 n - 1 列进行打印
for (let k = 0; k < n; k++) {
for (let i = 0, j = k; i < m && j >= 0; i++, j--) {
res.push(arr[i][j]);
}
}
// 右下角,从1 到 n - 1 行进行打印
for (let k = 1; k < m; k++) {
for (let i = k, j = n - 1; i < m && j >= 0; i++, j--) {
res.push(arr[i][j]);
}
}
return res
}