В последнее время я обнаружил, что много раз во время интервью мне всегда задавали какие-то вопросы о рукописной реализации, а потом я также скучно искал, что это за вопросы.
Результаты оказались на удивление похожими, поэтому я решил составить рейтинг для этих «старых друзей».
Ни для чего другого, просто для того, чтобы все по-настоящему поняли, почему возникают связанные проблемы, будучи знакомы с кодом, и восприняли это как возможность усердно работать и стремиться стать сильнее, хахаха.
Итак, давайте замолчим и начнем сегодняшний топ-10! ! !
10-й
рукописное печенье
Индекс внешнего вида: ★☆☆☆☆ Индекс осмотра: ★☆☆☆☆ Комплексная оценка: ★☆☆☆☆ Общий рейтинг: 1,0
Когда дело доходит до куки-файлов, интерфейсные друзья не должны быть слишком незнакомы.Они отправляются сервером в браузер пользователя испастиместныйнебольшая часть данных, данные, которые будут переданы, когда браузер отправит запрос на тот же сервер в следующий раз
маленький, но мощный:
- Управление состоянием сеанса (например, статус входа пользователя, корзина покупок, счет игры или другая информация, которую необходимо записать)
- Настройки персонализации (такие как пользовательские настройки, темы и т. д.)
- Отслеживание поведения в браузере (например, отслеживание и анализ поведения пользователей и т. д.)
Теперь, когда мы знаем, для чего нужен файл cookie, давайте посмотрим, как выглядит формат файла cookie.name=jay_chou; age=41; uid=19790118175;
,пройти черезkey=value
добавлять;
формат для записи
Поскольку во многих ситуациях вам необходимо получить и установить файлы cookie на работе, вам необходимо знать, как реализовать такой метод.
const Cookie = {
getCookie(key) {
let match = document.cookie.match(new RegExp('(^| )' + key + '=([^;]*)(;|$)'));
if (match && match.length) {
return decodeURIComponent(match[2]);
}
return null;
},
setCookie(key, value, opts = {}) {
let arr = [];
if (opts.httpOnly) {
arr.push('httpOnly=true');
}
if (opts.maxAge) {
arr.push(`max-age=${opts.maxAge}`);
}
if (opts.domain) {
arr.push(`domain=${opts.domain}`);
}
if (opts.path) {
arr.push(`path=${opts.path}`);
}
// secure|sameSite等就省略了
document.cookie = `${key}=${encodeURIComponent(value)}; ${arr.join('; ')}`;
}
};
Простой анализ:
getCookie
-
document.cookie
Вы можете получить значение соответствующего файла cookie (неhttpOnly:true
может быть получен) - соответствие регулярному
- Например, age=41, ему может предшествовать пробел, или это может быть символ, начинающийся с age, соответствующий
(^| )
- За ключом следует
=
номер,=
с последующим числом, кроме;
любой символ кроме знака([^;]*)
-
([^;]*)
из*
Указывает от 0 до много раз, потому что будут случаи, когда ключ не имеет значения - в конце концов
;
В случае конца или окончания значения, соответствующего(;|$)
- Например, age=41, ему может предшествовать пробел, или это может быть символ, начинающийся с age, соответствующий
- Возвращает вторую группу, совпавшую после декодирования, которая является значением, соответствующим ключу
- вернуть ноль, если не найдено
setCookie
- Установите массив arr для использованияхранитьВсе параметры должны быть установлены
-
httpOnly
Установите значение true черезdocument.cookie
Не удается получить файл cookie, который устанавливает соответствующий ключ -
max-age
Используется для установки времени истечения срока действия файла cookie в секундах. -
domain
Установите доменное имя, указанное доменное имя может принимать файлы cookie, включая субдомены (например, настройки .baidu.com, следующие субдомены могут принимать файлы cookie) -
path
Укажите путь для принятия файлов cookie (например, path=/web, затем также могут быть приняты подпути /web/fe, /web/login) -
secure
Указывает, что файлы cookie могут приниматься только по протоколу https. -
sameSite
Не удается отправить файлы cookie при междоменном -
document.cookie
Вы можете напрямую установить соответствующий ключ, значение и параметры конфигурации.
испытательный центр: Этот вопрос исследует знакомство с файлами cookie и какие файлы cookie можно установить.параметрИ знать, для чего используется каждый параметр
9-й
Рукописное сведение массива
Индекс внешнего вида: ★★☆☆☆ Индекс осмотра: ★☆☆☆☆ Комплексная оценка: ★☆☆☆☆ Общий рейтинг: 2,0
[1,2,[3,[4,[5,[6,[7,[8,[9]]]]]]]]
Также пьянит встреча с такой ненормальной структурой многомерного массива.Хотя это редко встречается в работе, это действительно тестовая площадка.
Ничего лишнего, просто посмотрите на этопервая реализацияпуть, все вместе, сплющенный в одномерный массив
const flatten1 = arr => {
return arr.reduce((res, cur) => {
// 如果当前项cur为数组,就继续递归展平
if (Array.isArray(cur)) {
// 返回新数组包括展开的原数组元素和新展平的数组元素
return [...res, ...flatten1(cur)];
} else {
// 返回新数组包括展开的原数组元素和当前项
return [...res, cur];
}
}, []);
};
Приведенный выше код будет напрямую сглаживать многомерный массив в одномерный массив, фактически работа по сглаживанию массива завершена.
Но иногда вам нужно сгладить определенную глубину, вместо того, чтобы свести все в одно измерение, вам нужно добавить определенные параметры, чтобы справиться с этим.
посмотривторая реализация
const flatten2 = (arr, depth = 1, res = []) => {
let i = -1, len = arr.length;
while (++i < len) {
// 选择当前项来进行判断
let cur = arr[i];
// 如果depth大于0并且当前项cur是数组
if (depth > 0 && Array.isArray(cur)) {
// 并且depth深度大于1的时候,继续递归处理,深度-1
if (depth > 1) {
flatten2(cur, depth - 1, res);
} else {
res.push(cur);
}
} else {
// 当前项cur不是数组,就直接加到res的后面
res[res.length] = cur;
}
}
// 返回结果数组
return res;
};
испытательный центр: Сведение массива, по сути, исследует вопрос о том, следует ли вызывать рекурсию для продолжения сглаживания ситуации с типом объекта (массивом).
рукописный массив уменьшить
Индекс внешнего вида: ★☆☆☆☆ Индекс осмотра: ★★☆☆☆ Комплексная оценка: ★☆☆☆☆ Общий рейтинг: 2,0
Первая реализация выравнивания массива в приведенном выше вопросе использует метод сокращения, Мы все знаем, что сокращение содержит два параметра, первый параметр — это переданная функция fn, а второй параметр — инициализированное значение val
Многие знают, что в fn есть два часто используемых параметра: 1 — это значение total, возвращаемое аккумулятором, 2 — это элемент cur, который обрабатывает текущий массив.
Кроме того, есть еще два параметра: 3 — значение индекса текущего элемента массива, а 4 — сам массив.
Ладно, вступление окончено, так что давайте его реализовывать
Array.prototype.reduce = function(fn, val) {
// 很好理解,就判断val是否有传入值
for (let i = 0; i < this.length; i++) {
// 没有传入值
if (typeof val === 'undefined') {
val = this[i];
} else { // 有传入val值
// total就是初始值val,之后的依次传入对应
val = fn(val, this[i], i, this);
}
}
return val;
};
Совет: функции нельзя сокращать как箭头函数(fn, val) => {}
, иначе не найдетсяthis
испытательный центр: Это для проверки мастерства сокращения и понимания параметров и их значений, содержащихся в первом параметре fn.
рукописный сон
Индекс внешнего вида: ★★☆☆☆ Индекс осмотра: ★☆☆☆☆ Комплексная оценка: ★☆☆☆☆ Общий рейтинг: 2,0
Поскольку в js нет синтаксиса сна, ему нужно иметь дело с необходимостью что-то делать через определенный период времени.
Это можно понять прямо по коду
function sleep(fn, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn);
}, time);
});
}
// 测试用例
let index = 0;
function fn() {
console.log('我要吃饭了', index++);
}
async function play() {
let a = await sleep(fn, 1000);
a();
let b = await sleep(fn, 2000);
b()
let c = await sleep(fn, 3000);
c()
}
play();
испытательный центр: этот вопрос исследуетPromise
а такжеasync/await
сценарии использования и применения
8-й
рукописное наследство
Индекс внешнего вида: ★☆☆☆☆ Индекс осмотра: ★★☆☆☆ Комплексная оценка: ★★☆☆☆ Общий рейтинг: 3,0
В настоящее время наследование по-прежнему используется в качестве тестовой площадки для проверки, и существует множество способов наследования.
- Наследование цепочки прототипов
- Заемный конструктор
- наследование композиции
- прототипное наследование
- Наследование паразитарного состава
Существуют также различные способы наследования, даже комбинированное наследование имеет определенные недостатки (конструктор родительского класса будет вызываться дважды, один раз в новом родительском классе при создании прототипа подкласса и один раз в конструкторе подкласса). некоторые возможности для улучшения
Далее речь пойдет непосредственно о случае паразитарного наследования состава.
- Чтобы устранить недостаток комбинированного наследования, конструктор родительского класса будет вызываться дважды.
- Создайте копию прототипа родительского класса напрямую, а затем назначьте ее прототипу подкласса (этот шаг считается паразитным наследованием).
- Составление или вызов конструктора родительского класса в конструкторе дочернего класса
// 寄生组合继承
function inherit(subs, supers) {
let proto = Object.create(supers.prototype); // 父类原型副本
proto.constructor = subs; // constructor指向子类
subs.prototype = proto; // 赋给子类的prototype原型
}
// 父类
function Super(name) {
this.name = name;
this.colors = ['爱,很简单', '寂寞的季节'];
}
Super.prototype.sayName = function() {
console.log(this.name);
};
// 子类
function Sub(name, age) {
// 组合还是Super.call(this, ...args)构造函数继承实例上的属性和方法
Super.call(this, name);
this.age = age;
}
// 继承
inherit(Sub, Super);
Sub.prototype.sayAge = function() {
console.log(this.age);
}
// 测试用例
let sub = new Sub('陶喆', 18);
sub.colors.push('就是爱你');
sub.sayName();
sub.sayAge();
console.log(sub.colors);
let super1 = new Super('JJ');
console.log(super1.colors);
испытательный центр: Тестовый сайт о наследовании в основном вращается вокруг знакомства с различными методами наследования и соответствующими недостатками, и получается относительно полный метод наследования.
7-й
рукописная каррированная функция
Индекс внешнего вида: ★★☆☆☆ Индекс осмотра: ★★☆☆☆ Комплексная оценка: ★★☆☆☆ Общий рейтинг: 4,0
Каррирование означает принятиенесколько параметровФункция трансформируется в приемодин параметрфункция и возвращаетостальные параметрыМетоды, которые возвращают результаты
Короче говоря, вы можете сделать это в функции填充
несколько参数
,после этого返回
Один新函数
,最后
провести求值
Как бы это ни было просто, это не так понятно, как демонстрация нескольких строчек кода.
function sum(a, b, c, d) {
return a + b + c + d;
}
// 柯里化函数
function curry(fn, ...args) {
// 如果传递的参数还没有达到要执行的函数fn的个数
// 就继续返回新的函数(高阶函数)
// 并且返回curry函数传递剩下的参数
if (args.length < fn.length) {
return (...newArgs) => curry(fn, ...args, ...newArgs);
} else {
return fn(...args);
}
}
// 测试用例
let add = curry(sum);
console.log(add(1)(2)(3)(4));
console.log(add(1, 2, 3)(4));
console.log(add(1, 2)(3, 4));
console.log(add(1)(2, 3)(4));
испытательный центр: Основная инспекционная параФункции высшего порядкапонимания, и что каррирование — это процесс частичной оценки
6-й
EventEmitter
Индекс внешнего вида: ★★☆☆☆ Индекс осмотра: ★★☆☆☆ Комплексная оценка: ★★☆☆☆ Общий рейтинг: 5,0
Когда-то кто-то спросил вас, знаете ли вы о публикации и подписке, и когда вы ответили «да» и хрипло объяснили, человек улыбнулся и написал мне EventEmitter
Не паникуйте, простоon, emit, off, once
Это способ
Если спросишь, напиши
// 我们以ES6类的形式写出来
class EventEmitter {
constructor() {
// 事件对象,存储订阅的type类型
this.events = Object.create(null);
}
on(type, cb) {
let events = this.events;
// 如果该type类型存在,就继续向数组中添加回调cb
if (events[type]) {
events[type].push(cb);
} else {
// type类型第一次存入的话,就创建一个数组空间并存入回调cb
event[type] = [cb];
}
},
emit(type, ...args) {
// 遍历对应type订阅的数组,全部执行
if (this.events[type]) {
this.events[type].forEach(listener => {
listener.call(this, ...args);
});
}
}
off(type, cb) {
let events = this.events;
if (events[type]) {
events[type] = events[type].filter(listener => {
// 过滤用不到的回调cb
return listener !== cb && listener.listen !== cb;
});
}
}
once(type, cb) {
function wrap() {
cb(...arguments);
this.off(type, wrap);
}
// 先绑定,调用后删除
wrap.listen = cb;
// 直接调用on方法
this.on(type, wrap);
}
}
испытательный центр: Проверить есть ли реальное понимание публикации и подписки, суть проста (создать数组
, в массив添加回调
, пройтись по массиву при выполнении依次执行
Просто), не паникуйте при встрече с таким экзаменационным вопросом, подумайте о принципах и идеях
5-й
Рукописное обещание.все и раса
Индекс внешнего вида: ★★★★☆ Индекс осмотра: ★★☆☆☆ Комплексная оценка: ★★★☆☆ Общий рейтинг: 6,0
Если упоминается Обещание, в большинстве случаев два брата будут неразлучны, когда они появятся.
Promise.all
Передайте набор массивов с промисами в качестве экземпляров, все методы будут выполняться в том порядке, в котором они были переданы, до тех пор, пока не будет выполнен тот, который занимает больше всего времени.resolveВозвращение будет засчитано как успех. Если появляется одно из промежуточных звеньевrejectпросто перебей
Promise.race
Как следует из названия, передаваемые параметры такие же, как и все, это означает, что тот, кто разрешит первым, завершит выполнение, и будет возвращен тот, кто преуспел первым.
Сценарии применения:
- all
- Параллельные запросы выполняются последовательно, несколько асинхронных результатов объединяются.
- race
- Определить время ожидания интерфейса
// Promise.all和Promise.race本质都是返回一个新的Promise实例
Promise.all = function(arr) {
return new Promise((resolve, reject) => {
// 用结果数组记录,每个promise实例传递的数据
let res = [];
// 索引记录是否全部完成
let index = 0;
for (let i = 0; i < arr.length; i++) {
// p为每一个promise实例,调用then方法
let p = arr[i];
p.then(data => {
// 把data数据赋给结果数组对应的索引位置
res[i] = data;
if (++index === arr.length) {
// 全部遍历完毕后,再把结果数组统一返回
resolve(res);
}
}, reject);
}
});
};
Promise.race = function(arr) {
return new Promise(() => {
for (let i = 0; i < arr.length; i++) {
arr[i].then(resolve, reject);
}
});
};
// 测试用例
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000);
});
let p2 = new Promise((resolve) => {
resolve(2);
});
let p3 = new Promise((resolve) => {
setTimeout(() => {
resolve(3);
});
});
Promise.all([p3, p1, p2]).then(data => {
// 按传入数组的顺序打印
console.log(data); // [3, 1, 2]
});
Promise.race([p1, p2, p3]).then(data => {
// 谁快就是谁
console.log(data); // 2
})
испытательный центр: Изучите возможности API-интерфейса Promise, ознакомьтесь с разницей между всеми и расой и узнайте, в каких сценариях они используются.
4 место
рукописный instanceOf
Индекс внешнего вида: ★★★☆☆ Индекс осмотра: ★★★☆☆ Комплексная оценка: ★★★☆☆ Общий рейтинг: 7,0
instanceOf используется для определения принадлежности экземпляра к определенному классу.Это должно быть понимание прототипа и цепочки прототипов, поэтому я не буду много говорить, просто посмотрите на код напрямую
function instanceOf(A, B) {
B = B.prototype;
A = A.__proto__;
while(true) {
if (A === null) {
return false
}
if (A === B) {
return true;
}
A = A.__proto__;
}
}
испытательный центр: Этот вопрос в основном исследует отношения между прототипом и цепочкой прототипов. Цепочка прототипов экземпляра является прототипом класса, к которому он принадлежит. Такого рода отношения легче понять.
рукописный
Индекс внешнего вида: ★★★☆☆ Индекс осмотра: ★★★☆☆ Комплексная оценка: ★★★☆☆ Общий рейтинг: 7,0
Когда дело доходит до междоменных ситуаций, наиболее известным является jsonp. Хотя CORS сейчас очень удобен для решения, большая часть его настраивается студентами в фоновом режиме. Когда интерфейс сталкивается с междоменными ситуациями, также используется jsonp. более.
Думаю многие студенты знают принцип реализации jsonp.На самом деле это легко решается опираясь на атрибут src скрипта.Конечно, через нас пропускают back-end студенты.callback
Параметры для обертывания данных и окончательного возврата их нам, просто взгляните, как это выглядит
- Данные jsonp, возвращаемые серверной частью
app.get('/jsonp', (req, res) => {
const {callback, wd, from} = req.query;
let data = {
msg: '这是jsonp数据',
word: wd,
referer: from,
data: [1, 2, 3]
};
data = JSON.stringify(data);
// 约定好的callback函数,然后把数据填充到里面返回给前端
res.end(callback + '(' + data + ')');
});
Увидев это, вы должны понимать, как jsonp обрабатывается и возвращается на серверной части, поэтому теперь мы переходим внутрь и начинаем реализовывать его вручную.
const jsonp = (opts = {}) => {
// 通过一个callback参数所对应的函数名来把数据进行写入
opts.url = `${opts.url}?callback=${opts.callback}`;
// 在你需要传递其他参数时,需要遍历后拼接到url上
for (let key in opts.data) {
if (opts.data.hasOwnProperty(key)) {
opts.url += `&${key}=${opts.data[key]}`;
}
}
// 主要是依靠script的src属性加载内容没有跨域情况
const script = document.createElement('script');
script.src = opts.url;
// 在script脚本执行完毕后,再删除此脚本
script.onload = () => {
document.body.removeChild(script);
}
// 把创建好的script脚本添加到body中
document.body.appendChild(script);
};
// 测试用例
jsonp({
url: 'http://localhost:8888/cors',
data: {
wd: 'nba',
from: 'home'
},
// 接收数据的函数
callback: 'getData'
});
function getData(data) {
// 通过jsonp拿到的真实数据
console.log(data);
}
испытательный центр:Для jsonp мы все знаем, что он загружается через атрибут src скрипта скрипта и нет междоменной ситуации, поэтому данные получаются путем создания скрипта и загрузки src.Конечно, нам тоже нужно быть ясными о процессе того, как бэкэнд заполняет данные, чтобы мы могли узнать, что есть, а также узнать, почему
3 место
рукописная глубокая копия
Индекс внешнего вида: ★★★★☆ Индекс осмотра: ★★★☆☆ Комплексная оценка: ★★★★☆ Общий рейтинг: 8,0
Глубокое копирование также можно назвать глубоким клонированием, как бы оно ни называлось, не важно, важно то, что эта проблема действительно является классикой среди классиков, а также для предотвращения проблемы путаницы данных, вызванной адресами разделяемой памяти, так что не t идти по углам и открыть все сразу.
Подождите, потому что большинство типов данных, используемых в работе, являются объектами и массивами, поэтому они не будут обрабатываться для регуляров и дат.Если вам интересно, вы можете изучить это в частном порядке.
let o = {a: 1, b: 2, c: [1,2,3], d:{age:18}};
o.o = o; // 循环引用的情况
const deepClone = (obj, map = new Map()) => {
// 说白了,只有对象类型({}, [])才需要处理深拷贝
if (typeof obj === 'object' && obj !== null) {
// 处理循环引用的情况,如果之前已经有了该数据了
// 就直接返回,没必要再重新处理了
if (map.has(obj)) {
return map.get(obj);
}
// 主要就是两种情况,数组和对象
// 创建一个新的对象or数组
let data = Array.isArray(obj) ? [] : {};
// 设置数据,防止循环引用
map.set(obj, data);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 通过对obj下的所有数据类型进行递归处理
data[key] = deepClone(obj[key], map);
}
}
// 返回data,由于是新的对象or数组
// 所以会开辟新的内存空间,不会和老的obj共用空间
// 就不会导致数据错乱干扰的情况了
return data;
}
// 基础类型之间返回即可
return obj;
}
// 测试用例
let o2 = deepClone(o);
o2.c.pop();
o2.a = 110;
o2.d.name = '小白';
console.log(o, o2);
испытательный центр: Во-первых, вам нужно знать, почему существует глубокая копия, во-вторых, вам нужно знать, какие типы данных нужно глубоко копировать, и понимать, что делает рекурсия.
рукописный новый
Индекс внешнего вида: ★★★☆☆ Индекс осмотра: ★★★★☆ Комплексная оценка: ★★★★☆ Общий рейтинг: 8,0
Для того, как создать экземпляр, мы слишком ясно, нового достаточно, например, если нет объекта, мы создадим новый объект, ха-ха, шучу
Давайте рассмотримновый процесс
- создать новый объект
- Направьте прототип нового объекта на прототип конструктора
- это указывает на созданный экземпляр
- Если никакой другой объект не возвращается, возвращается этот новый объект
Иногда текст всегда бледный, поэтому просто используйте код, чтобы объяснить причину.
function Dog(name) {
this.name = name;
// 返回对象
// return {
// name,
// age: 5
// }
// 返回函数
// return function() {
// return name;
// }
}
Dog.prototype.say = function() {
console.log(this.name);
return this.name;
};
// 手写new
function New(Super, ...args) {
// 创建一个继承构造函数原型的对象
let obj = Object.create(Super.prototype);
// res是表示构造函数返回的结果
let res = Super.call(obj, ...args);
// 第一个条件是返回对象
// 第二个条件是返回函数
if ((res !== null && typeof res === 'object') || typeof res === 'function') {
return res;
}
// 返回新对象
return obj;
}
// 测试用例
let dog = New(Dog, '小白');
dog.say();
// console.log(dog.name, dog.age);
// console.log(dog());
испытательный центр: Многие люди знают, что при создании нового экземпляра соответствующий класс будет иметь множество свойств и методов по этому поводу, но иногда они будут изучать, что происходит, если объект или функция возвращаются непосредственно в конструкторе класса. И нам также нужно лучше продумать новый процесс, что именно он делает?
Статья 2
Рукописный звонок и подача заявки
Индекс внешнего вида: ★★★★☆ Индекс осмотра: ★★★★☆ Комплексная оценка: ★★★★☆ Общий рейтинг: 9,0
Вопрос про call и apply больше о разнице между ними.Кроме того изредка кто-то будет спрашивать какой из двух лучше по производительности.Может у вас возникнут какие-то мысли,но это не беда,через следующий код,Вы можно легко обнаружить, что вызов выполняется быстрее, чем применение.
- звони и оставляй заявку
// call实现
Function.prototype.call = function(context, ...args) {
// 执行上下文都保证是对象类型,如果不是就是window
context = Object(context) || window;
// 创建一个额外的变量当做context的属性
const fn = Symbol();
// 给这个fn属性赋值为当前的函数
context[fn] = this;
// 执行函数把...args传入
const result = context[fn](...args);
// 删除使用过的fn属性
delete context[fn];
// 返回函数执行结果
return result;
};
// 测试用例 - call
let o = { a: 1 };
function fn(b) {
console.log(this.a, b);
}
fn.call(o, '你好');
// apply实现
Function.prototype.apply = function(context, arrArgs) {
context = Object(context) || window;
const fn = Symbol();
context[fn] = this;
// 需要把传入apply的数组进行展开运算
// 所以在这里性能会有些消耗相比call来讲
const result = context[fn](...arrArgs);
delete context[fn];
return result;
}
// 测试用例 - apply
let o = { a: 1 };
function fn(...b) {console.log(this.a, ...b)}
fn.apply(o, [2, 3, 4]);
испытательный центр: Этот вопрос сначала проверяет, знакомы ли вы с использованием двух методов. Разница заключается в форме второго переданного параметра. Apply должен быть массивом, а call может быть любого типа и может передавать несколько параметров.
Кроме того, установка приватных свойств для контекста выполнения и получение результата выполнения функции, окончательное удаление используемых приватных свойств и возврат результата также является местом для исследования.
рукописный переплет
Индекс внешнего вида: ★★★★☆ Индекс осмотра: ★★★★☆ Комплексная оценка: ★★★★☆ Общий рейтинг: 9,0
По сути, метод привязки можно понимать как практическое применение функций высшего порядка.Разница между привязкой и вызовом и применением заключается в том, что он не выполняет функцию немедленно, а имеет отложенную операцию выполнения, которая представляет собой так называемую генерация новой функции
Итак, давайте посмотрим на код, чтобы его было легче понять.
Function.prototype.bind = function(context, ...args) {
// context为要改变的执行上下文
// ...args为传入bind函数的其余参数
return (...newArgs) => {
// 这里返回一个新的函数
// 通过调用call方法改变this指向并且把老参和新参一并传入
return this.call(context, ...args, ...newArgs);
}
};
// 测试用例
let o = {name: 'chd'};
function f(...args) {
console.log(this.name, ...args);
}
let o2 = f.bind(o, 1, '222', null, [1,2,3], {age: 5});
o2();
испытательный центр: изучить знакомство и понимание привязки, а также понять, понимаете ли вы функции более высокого порядка, а также знаете разницу между вызовом и применением.
1-е место
Ручной троттлинг и стабилизация
Индекс внешнего вида: ★★★★★ Индекс осмотра: ★★★★★ Комплексная оценка: ★★★★★ Общий рейтинг: 10,0
Существует метод оптимизации, называемый оптимизацией опыта, в котором чаще используются дросселирование и защита от сотрясений.
Дросселирование — это выполнение функции один раз в течение определенного периода времени, в основном используемое для событий прокрутки.
Anti-shake — это функция, которая выполняется в последний раз в течение определенного периода времени, которая в основном используется при вводе операций ввода.
Теперь давайте посмотрим, как пишется наш Top1.
// 节流
function throttle(fn, time) {
// 设置初始时间
let pre = 0;
// 返回一个新的函数
return (...args) => {
// 记录当前时间
let now = Date.now();
// 通过时间差来进行节流
if (now - pre > time) {
// 执行fn函数
fn.call(this, ...args);
// 更新pre的时间
pre = now;
}
}
}
// 防抖
function debounce(fn, time, isNow) {
// 设置定时器变量
let timer;
return (...args) => {
// 默认首次是立即触发的,不应该一上来就延迟执行fn
if (isNow) {
fn.call(this, ...args);
isNow = false;
return;
}
// 如果上一个定时器还在执行,就直接返回
if (timer) return;
// 设置定时器
timer = setTimeout(() => {
fn.call(this, ...args);
// 清除定时器
clearTimeout(timer);
timer = null;
}, time);
}
}
испытательный центр: Дросселирование и защита от сотрясений — определенно распространенные темы. С помощью этих двух методов оптимизации мы можем значительно улучшить взаимодействие с пользователем. пройти черезsetTimeout
Чтобы установить время, выполните функцию fn в течение указанного времени и узнайте время первого срабатывания антивибратора.
Заканчивать
Большое спасибо за вашу усердную работу, чтобы увидеть здесь.Хотя это Топ-10, точки переднего плана, которые необходимо изучить в процессе собеседования, также очень велики.Очень важно обеспечить систематическое обучение и подумать о том, почему
Что ж, на этот раз напишем здесь 886.