предисловие
В основном, во время собеседования мы часто сталкиваемся с такими проблемами, как разрыв XXX руками.В этот раз мы собираемся разобраться и обобщить его.Закрепляя основу нашего родного JS, в следующий раз мы хотим рассмотреть вопросы разрыва рук. на собеседовании легко найти и сэкономить время.
Код здесь 👉GitHub
Порядок ухода случайный, а не в порядке сложности.
Реализовать делегат события (подвержен ошибкам)
Здесь не будет объясняться делегирование событий, например привязка события клика к li.
увидеть неправильную версию, (легко жить, см."Уровень интервьюера.")👇
ul.addEventListener('click', function (e) {
console.log(e,e.target)
if (e.target.tagName.toLowerCase() === 'li') {
console.log('打印') // 模拟fn
}
})
"Есть небольшая ошибка, если пользователь нажимает на span в li, то fn не срабатывает, что явно неправильно"👇
<ul id="xxx">下面的内容是子元素1
<li>li内容>>> <span> 这是span内容123</span></li>
下面的内容是子元素2
<li>li内容>>> <span> 这是span内容123</span></li>
下面的内容是子元素3
<li>li内容>>> <span> 这是span内容123</span></li>
</ul>
Такая сцена неправильная, тогда давайте посмотрим на расширенную версию👇
function delegate(element, eventType, selector, fn) {
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)) {
if (element === el) {
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el)
},true)
return element
}
Реализовать перетаскиваемый DIV
Этот вопрос выглядит простым, вы можете попробовать, если вы можете решить его за 30 минут, просто опубликуйте код 👇
<div id="xxx"></div>
var dragging = false
var position = null
xxx.addEventListener('mousedown',function(e){
dragging = true
position = [e.clientX, e.clientY]
})
document.addEventListener('mousemove', function(e){
if(dragging === false) return null
const x = e.clientX
const y = e.clientY
const deltaX = x - position[0]
const deltaY = y - position[1]
const left = parseInt(xxx.style.left || 0)
const top = parseInt(xxx.style.top || 0)
xxx.style.left = left + deltaX + 'px'
xxx.style.top = top + deltaY + 'px'
position = [x, y]
})
document.addEventListener('mouseup', function(e){
dragging = false
})
Написанные от руки функции защиты от сотрясений и дросселирования
"дроссель", который предусматривает, что функция может запускаться только один раз в единицу времени. Если функция запускается несколько раз в течение этого времени, только один из них вступит в силу. сцена👇
- событие прокрутки прокрутки, функция обратного вызова для каждого конкретного описания
- Введите поле ввода, отправьте запрос в определенное время или разверните раскрывающийся список, (также доступен анти-встряска)
Дросселирование фокусируется на блокировке"flag = false"
function throttle(fn, delay) {
let flag = true,
timer = null
return function(...args) {
let context = this
if(!flag) return
flag = false
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(context,args)
flag = true
},delay)
}
}
"защита от дрожания", Выполнить обратный вызов после того, как событие будет запущено в течение n секунд. Если оно будет запущено снова в течение этих n секунд, время будет изменено. сцена👇
- Измените размер окна браузера, чтобы избежать слишком частого
- Кнопки для входа в систему, текстовых сообщений и т. д. позволяют избежать отправки нескольких запросов
- Текстовый редактор сохраняет в реальном времени
Anti-shake фокусируется на очистке"clearTimeout(timer)"
function debounce(fn, delay) {
let timer = null
return function(...args) {
let context = this
if(timer) clearTimeout(timer)
timer = setTimeout(function(){
fn.apply(context,args)
},delay)
}
}
Реализовать дедупликацию массива
Это тестовый пример массива Array👇
var array = [1, 1, '1', '1', null, null,
undefined, undefined,
new String('1'), new String('1'),
/a/, /a/,
NaN, NaN
];
Как сделать дедупликацию массива, чтобы произвести впечатление на интервьюера👇
Использовать набор
let unique_1 = arr => [...new Set(arr)];
использовать фильтр
function unique_2(array) {
var res = array.filter(function (item, index, array) {
return array.indexOf(item) === index;
})
return res;
}
использовать уменьшить
let unique_3 = arr => arr.reduce((pre, cur) => pre.includes(cur) ? pre : [...pre, cur], []);
Используйте значение ключа Object для 🐂🐂, это также лучший эффект 👇
function unique_3(array) {
var obj = {};
return array.filter(function (item, index, array) {
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
использоватьobj[typeof item + item] = true
, причина в том, что对象的键值只能是字符串
, поэтому используйтеtypeof item + item
заменять
Реализовать каррированную функцию
Каррирование означает принятие"несколько параметров"Функция трансформируется в прием"один параметр"функция и возвращает"остальные параметры"Приложение, которое возвращает результаты.
Идеи:
- Определить, достигают ли переданные параметры количества fn для выполнения функции
- Если он не достигнут, продолжайте возвращаться к новой функции и возвращайтесь к функции карри, чтобы передать оставшиеся параметры.
let currying = (fn, ...args) =>
fn.length > args.length ?
(...arguments) => currying(fn, ...args, ...arguments) :
fn(...args)
Тестовый случай👇
let addSum = (a, b, c) => a+b+c
let add = curry(addSum)
console.log(add(1)(2)(3))
console.log(add(1, 2)(3))
console.log(add(1,2,3))
реализовать плоский массив
"Свести многомерный массив к одномерному массиву"
Array.prototype.flat(num)
// num表示的是维度
// 指定要提取嵌套数组的结构深度,默认值为 1
使用 Infinity,可展开任意深度的嵌套数组
Если ты напишешь это интервьюеру, тебя забьют до смерти, проще написать 👇
let flatDeep = (arr) => {
return arr.reduce((res, cur) => {
if(Array.isArray(cur)){
return [...res, ...flatDep(cur)]
}else{
return [...res, cur]
}
},[])
}
"Что вы хотите произвести на интервьюера?", можно так написать, 👇
function flatDeep(arr, d = 1) {
return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val),
[]) :
arr.slice();
};
// var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
// flatDeep(arr1, Infinity);
Вы можете передать параметр, массив сплющен до нескольких размеров, прост и понятен, и выглядит полным 🐂🐂🐂
глубокая копия
Глубокое копирование решает"Повреждение данных, вызванное адресами общей памяти"
Идеи:
-
рекурсия
-
Тип решения
-
Проверка колец (также называемых циклическими ссылками)
-
Прототипы нужно игнорировать
function deepClone(obj, map = new WeakMap()) {
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
if (obj == null || typeof obj != 'object') return obj;
if (map.has(obj)) {
return map.get(obj);
}
let t = new obj.constructor();
map.set(obj, t);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
t[key] = deepClone(obj[key], map);
}
}
return t;
}
//测试用例
let obj = {
a: 1,
b: {
c: 2,
d: 3
},
d: new RegExp(/^\s+|\s$/g)
}
let clone_obj = deepClone(obj)
obj.d = /^\s|[0-9]+$/g
console.log(clone_obj)
console.log(obj)
Реализует функцию объектного типа
Ядро: Object.prototype.toString
let isType = (type) => (obj) => Object.prototype.toString.call(obj) === `[object ${type}]`
// let isArray = isType('Array')
// let isFunction = isType('Function')
// console.log(isArray([1,2,3]),isFunction(Map))
функция isType 👆, также принадлежит"частичная функция", частичная функция фактически возвращает"Параметры предварительной обработки"Новая функция.
Рукописный звонок и подача заявки
Измените этот пункт, разница только в том, что параметры передаются другие👇
// 实现call
Function.prototype.mycall = function () {
let [thisArg, ...args] = [...arguments]
thisArg = Object(thisArg) || window
let fn = Symbol()
thisArg[fn] = this
let result = thisArg[fn](...args)
delete thisArg[fn]
return result
}
// 实现apply
Function.prototype.myapply = function () {
let [thisArg, args] = [...arguments];
thisArg = Object(thisArg)
let fn = Symbol()
thisArg[fn] = this;
let result = thisArg[fn](...args);
delete thisArg.fn;
return result;
}
//测试用例
let cc = {
a: 1
}
function demo(x1, x2) {
console.log(typeof this, this.a, this)
console.log(x1, x2)
}
demo.apply(cc, [2, 3])
demo.myapply(cc, [2, 3])
demo.call(cc,33,44)
demo.mycall(cc,33,44)
рукописный переплет
bind не выполняет функцию сразу, а имеет отложенную операцию выполнения, то есть генерируется новая функция, и вам нужно ее выполнить👇
// 实现bind
Function.prototype.mybind = function(context, ...args){
return (...newArgs) => {
return this.call(context,...args, ...newArgs)
}
}
// 测试用例
let cc = {
name : 'TianTian'
}
function say(something,other){
console.log(`I want to tell ${this.name} ${something}`);
console.log('This is some'+other)
}
let tmp = say.mybind(cc,'happy','you are kute')
let tmp1 = say.bind(cc,'happy','you are kute')
tmp()
tmp1()
Реализовать новую операцию
Ключевые моменты👇
- Создайте новый объект,
__proto__
Объект-прототип, указывающий на конструктор - выполнить конструктор
- Возвращаемое значение типа объекта возвращается как возвращаемое значение нового метода, в противном случае возвращается указанный выше новый объект.
Код выглядит следующим образом👇
function _new() {
let obj = {};
let [constructor, ...args] = [...arguments];
obj.__proto__ = constructor.prototype;
let result = constructor.apply(obj, args);
if (result && typeof result === 'function' || typeof result === 'object') {
return result;
}
return obj;
}
Реализовать instanceof
"instanceof" "оператор"для обнаружения конструкторовprototype
Появляется ли свойство в цепочке прототипов экземпляра объекта.
Грамматика👇
object instanceof constructor
object 某个实例对象
construtor 某个构造函数
Посмотрите цепочку прототипов и найдите вершину прототипа, то есть Object.prototype, код👇
function my_instance_of(leftVaule, rightVaule) {
if(typeof leftVaule !== 'object' || leftVaule === null) return false;
let rightProto = rightVaule.prototype,
leftProto = leftVaule.__proto__;
while (true) {
if (leftProto === null) {
return false;
}
if (leftProto === rightProto) {
return true;
}
leftProto = leftProto.__proto__
}
}
реализовать сон
Чтобы выполнить функцию через определенное время, используйте Promise для инкапсуляции 👇
function sleep(fn, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn);
}, time);
});
}
let saySomething = (name) => console.log(`hello,${name}`)
async function autoPlay() {
let demo = await sleep(saySomething('TianTian'),1000)
let demo2 = await sleep(saySomething('李磊'),1000)
let demo3 = await sleep(saySomething('掘金的好友们'),1000)
}
autoPlay()
Реализовать уменьшение массива
Дополнительные рукописные методы массива см. В моем предыдущем посте 👉«Метод массива» от детальной работы с массивом js до анализа array.js в v8
Дайте упрощенную версию напрямую👇
Array.prototype.myreduce = function(fn, initVal) {
let result = initVal,
i = 0;
if(typeof initVal === 'undefined'){
result = this[i]
i++;
}
while( i < this.length ){
result = fn(result, this[i])
}
return result
}
Реализуйте Promise.all и гонку
Если вы не знаете, как использовать два, асинхронный MDN 👉Promise.race() Promise.all()
// 实现Promise.all 以及 race
Promise.myall = function (arr) {
return new Promise((resolve, reject) => {
if (arr.length === 0) {
return resolve([])
} else {
let res = [],
count = 0
for (let i = 0; i < arr.length; i++) {
// 同时也能处理arr数组中非Promise对象
if (!(arr[i] instanceof Promise)) {
res[i] = arr[i]
if (++count === arr.length)
resolve(res)
} else {
arr[i].then(data => {
res[i] = data
if (++count === arr.length)
resolve(res)
}, err => {
reject(err)
})
}
}
}
})
}
Promise.myrace = function (arr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
// 同时也能处理arr数组中非Promise对象
if (!(arr[i] instanceof Promise)) {
Promise.resolve(arr[i]).then(resolve, reject)
} else {
arr[i].then(resolve, reject)
}
}
})
}
Тестовый случай👇
// 测试用例
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11)
}, 2000);
});
let p2 = new Promise((resolve, reject) => {
reject('asfs')
});
let p3 = new Promise((resolve) => {
setTimeout(() => {
resolve(33);
}, 4);
});
Promise.myall([p3, p1, 3, 4]).then(data => {
// 按传入数组的顺序打印
console.log(data); // [3, 1, 2]
}, err => {
console.log(err)
});
Promise.myrace([p1, p2, p3]).then(data => {
// 谁快就是谁
console.log(data); // 2
}, err => {
console.log('失败跑的最快')
})
рукописное наследство
Способов наследования много, но я не буду здесь слишком углубляться, вы можете прочитать эту статьюЦепочка прототипов JS и наследование: больше не спрашивайте
Главное разобраться, как использовать паразитное комбинированное наследование и наследование классов
"Паразитическая композиционная наследственность"
function inheritPrototype(subType, superType) {
// 创建对象,创建父类原型的一个副本
var prototype = Object.create(superType.prototype);
// 增强对象,弥补因重写原型而失去的默认的constructor 属性
prototype.constructor = subType;
// 指定对象,将新创建的对象赋值给子类的原型
subType.prototype = prototype;
}
Тестовый случай👇
// 父类初始化实例属性和原型属性
function Father(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Father.prototype.sayName = function () {
alert(this.name);
};
// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
// 将父类原型指向子类
inheritPrototype(Son, Father);
// 新增子类原型属性
Son.prototype.sayAge = function () {
alert(this.age);
}
var demo1 = new Son("TianTian", 21);
var demo2 = new Son("TianTianUp", 20);
demo1.colors.push("2"); // ["red", "blue", "green", "2"]
demo2.colors.push("3"); // ["red", "blue", "green", "3"]
Класс реализует наследование👇
class Rectangle {
// constructor
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea()
}
// Method
calcArea() {
return this.height * this.width;
}
}
const rectangle = new Rectangle(40, 20);
console.log(rectangle.area);
// 输出 800
// 继承
class Square extends Rectangle {
constructor(len) {
// 子类没有this,必须先调用super
super(len, len);
// 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
this.name = 'SquareIng';
}
get area() {
return this.height * this.width;
}
}
const square = new Square(20);
console.log(square.area);
// 输出 400
extends
Основной код наследования выглядит следующим образом, по сути, он такой же, как и упомянутый выше паразитический комбинированный метод наследования👇
function _inherits(subType, superType) {
// 创建对象,创建父类原型的一个副本
// 增强对象,弥补因重写原型而失去的默认的constructor 属性
// 指定对象,将新创建的对象赋值给子类的原型
subType.prototype = Object.create(superType && superType.prototype, {
constructor: {
value: subType,
enumerable: false,
writable: true,
configurable: true
}
});
if (superType) {
Object.setPrototypeOf
? Object.setPrototypeOf(subType, superType)
: subType.__proto__ = superType;
}
}
Говоря о принципе реализации с интервьюером, основы этого ребенка в порядке.
AJAX вручную
Я написал краткую версию, а с подробной разбирать не буду, во время интервью обсудите с интервьюером 🐂🐂🐂
var request = new XMLHttpRequest()
request.open('GET', 'index/a/b/c?name=TianTian', true);
request.onreadystatechange = function () {
if(request.readyState === 4 && request.status === 200) {
console.log(request.responseText);
}};
request.send();
Реализовать trim() с регулярными выражениями
Убери первый лишний пробел👇
String.prototype.trim = function(){
return this.replace(/^\s+|\s+$/g, '')
}
//或者
function trim(string){
return string.replace(/^\s+|\s+$/g, '')
}
Реализовать метод Object.create
//实现Object.create方法
function create(proto) {
function Fn() {};
Fn.prototype = proto;
Fn.prototype.constructor = Fn;
return new Fn();
}
let demo = {
c : '123'
}
let cc = Object.create(demo)
Реализовать функцию, позволяющую выполнять до n задач одновременно.
Используйте инкапсуляцию Promise, чтобы получить массив, каждый элемент массива является объектом Promise.
function limitRunTask(tasks, n) {
return new Promise((resolve, reject) => {
let index = 0, finish = 0, start = 0, res = [];
function run() {
if (finish == tasks.length) {
resolve(res);
return;
}
while (start < n && index < tasks.length) {
// 每一阶段的任务数量++
start++;
let cur = index;
tasks[index++]().then(v => {
start--;
finish++;
res[cur] = v;
run();
});
}
}
run();
})
// 大概解释一下:首先如何限制最大数量n
// while 循环start < n,然后就是then的回调
}
десятичное преобразование
Получив десятичное число, преобразуйте его в число базового интервала [2~16], что представляет собой простую симуляцию.
function Conver(number, base = 2) {
let rem, res = '', digits = '0123456789ABCDEF', stack = [];
while (number) {
rem = number % base;
stack.push(rem);
number = Math.floor(number / base);
}
while (stack.length) {
res += digits[stack.pop()].toString();
}
return res;
}
число в строку тысячных
Писать это очень убедительно 🐂🐂🐂
function thousandth(str) {
return str.replace(/\d(?=(?:\d{3})+(?:\.\d+|$))/g, '$&,');
}
❤️ Всем спасибо
Если вы считаете этот контент полезным:
- Поставьте лайк и поддержите его, чтобы больше людей увидело этот контент
- Вы можете поделиться со мной своими мыслями в области комментариев, и вы также можете записать свой мыслительный процесс в области комментариев.
- Если вы считаете, что это хорошо, вы также можете прочитать последние статьи TianTian (спасибо за вашу поддержку и поддержку 🌹🌹🌹):
- «Поиск недостатков и заполнение утечек» отправит вам 18 вопросов браузерного интервью.(450+👍)
- «Поиск недостатков и устранение утечек» дает вам 54 вопроса для собеседования по JavaScript.(500+👍)
- 9 основных операций связанного списка "алгоритмы и структуры данных"(150+👍)
- "Советы" Chrome DevTools советы по отладке для соотечественников, эффективность 🚀🚀🚀(210+👍)
- Коды «Как работает браузер» для девушки — процесс рендеринга (1,1 Вт+ слов)(230+👍)
- «Метод массива» от детальной работы с массивом js до анализа array.js в v8(220+👍)
- Коды «Как работает браузер» для девушки — состав браузера и сетевой запрос (1,2 Вт слов)(240+👍)
В этой статье используетсяmdniceнабор текста