На собеседовании мы часто сталкиваемся с некоторыми вопросами интервью, такими как написанное от руки XXX, поэтому хорошее резюме очень необходимо, чтобы закрепить основу нашего нативного js.
Хотя сводных статей в интернете уже много, есть общая проблема, на мой взгляд, — чрезмерное усложнение принципа. С точки зрения интервьюера, его цель — проверить понимание интервьюируемым языка JS в кратчайшие сроки, но, прочитав множество сводных статей на сайте, я обнаружил, что большая часть кода имеет смысл. , такие как реализация простой защиты от сотрясений, если это основной дисплей процесса, что касается других режимов, нет необходимости копать глубже, и многие «если-иначе» заставляют людей выглядеть ослепительно. запоминать код напрямую.Кроме того, основная логика может быть отображена, и может не быть проблемой реализовать другие подобные ситуации по горизонтали.
В следующем порядке рекомендуется написать его самостоятельно в соответствии с основными моментами, а затем сравнить следующий код, эффект обзора лучше. Цель этой статьи — помочь вам понять внутреннюю работу API с точки зрения первых принципов с помощью наиболее лаконичного кода.Любые крайние случаи, которые не помогают нам понять API, не обрабатываются..
Во-первых, используйте ES5 для реализации метода отображения массивов.
Основные моменты:
1. Каковы параметры callback-функции и как работать с возвращаемым значением.
2. Не изменяйте исходный массив.
Array.prototype.MyMap = function(fn, context){
var arr = Array.prototype.slice.call(this);//由于是ES5所以就不用...展开符了
var mappedArr = [];
for (var i = 0; i < arr.length; i++ ){
mappedArr.push(fn.call(context, arr[i], i, this));
}
return mappedArr;
}
Во-вторых, используйте ES5 для реализации метода уменьшения массива.
Основные моменты:
1. Как быть с не переданным начальным значением
2. Каковы параметры callback-функции и как работать с возвращаемым значением.
Array.prototype.myReduce = function(fn, initialValue) {
var arr = Array.prototype.slice.call(this);
var res, startIndex;
res = initialValue ? initialValue : arr[0];
startIndex = initialValue ? 0 : 1;
for(var i = startIndex; i < arr.length; i++) {
res = fn.call(null, res, arr[i], i, this);
}
return res;
}
3. Реализовать вызов/применить
Идея: используйте функцию контекста this.
//实现apply只要把下一行中的...args换成args即可
Function.prototype.myCall = function(context = window, ...args) {
let func = this;
let fn = Symbol("fn");
context[fn] = func;
let res = context[fn](...args);//重点代码,利用this指向,相当于context.caller(...args)
delete context[fn];
return res;
}
В-четвертых, реализуйте метод Object.create (обычно используется)
function create(proto) {
function F() {};
F.prototype = proto;
F.prototype.constructor = F;
return new F();
}
5. Реализуйте метод привязки
Основные моменты:
1. Для обычных функций привяжите this, чтобы указать на
2. Для конструктора убедитесь, что свойства объекта-прототипа исходной функции не могут быть потеряны.
Function.prototype.bind = function(context, ...args) {
let self = this;//谨记this表示调用bind的函数
let fBound = function() {
//this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)
return self.apply(this instanceof fBound ? this : context || window, args.concat(Array.prototype.slice.call(arguments)));
}
fBound.prototype = Object.create(this.prototype);//保证原函数的原型对象上的属性不丢失
return fBound;
}
Рукописный бинд, о котором все обычно говорят, на самом деле так прост :)
6. Внедрите новое ключевое слово
Основные моменты:
- Создайте новый объект, __proto__ этого объекта должен указывать на объект-прототип конструктора.
- выполнить конструктор
- Возвращаемое значение типа объекта возвращается как возвращаемое значение нового метода, в противном случае возвращается указанный выше новый объект.
function myNew(fn, ...args) {
let instance = Object.create(fn.prototype);
let res = fn.apply(instance, args);
return typeof res === 'object' ? res: instance;
}
7. Осознайте роль instanceof
Суть: восходящий поиск по цепочке прототипов.
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left);
while(true) {
if(proto == null) return false;
if(proto == right.prototype) return true;
proto = Object.getPrototypeof(proto);
}
}
8. Реализуйте одноэлементный шаблон
Основной вывод: перехват с помощью замыканий и свойств прокси
function proxy(func) {
let instance;
let handler = {
construct(target, args) {
if(!instance) {
instance = Reflect.construct(func, args);
}
return instance;
}
}
return new Proxy(func, handler);
}
Девять, осознайте плоскость массива
На самом деле есть много способов. Я уже делал системную сортировку. Есть шесть способов, пожалуйста, обратитесь к:
Краткое описание метода выравнивания (плоского) массива JS
10. Реализуйте функцию защиты от сотрясений
Основные моменты:
Если он снова срабатывает в пределах временных рамок таймера, он повторяется.
const debounce = (fn, delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
11. Реализуйте функцию дросселирования
Основные моменты:
Если он снова срабатывает в пределах временного диапазона таймера, он игнорируется, и следующий таймер может быть запущен после завершения текущего таймера.
const throttle = (fn, delay = 500) => {
let flag = true;
return (...args) => {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, args);
flag = true;
}, delay);
};
};
12. Реализуйте EventEmit с режимом публикации-подписки
Обратитесь к другой моей статье:
Собственный пакет подключаемых модулей JS на основе «публикация-подписка»Рукописный раздел публикации-подписки в формате .
Тринадцать, реализовать глубокую копию
Ниже приведен упрощенный вариант глубокой копии, без учета ситуации с циклическими ссылками и обработкой Buffer, Promise, Set и Map.Если их реализовывать по одному, то это слишком сложно, и нереально напишите их в небольшом интервью.Если вам интересно, вы можете перейти сюда для углубленного внедрения:
Окончательное исследование Deep Copy.
const clone = parent => {
// 判断类型
const isType = (target, type) => `[object ${type}]` === Object.prototype.toString.call(target)
// 处理正则
const getRegExp = re => {
let flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== "object") return parent;
let child, proto;
if (isType(parent, "Array")) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, "RegExp")) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
}
for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
14. Выполнение обещаний
Ключевые моменты сложнее и сложнее.Пожалуйста, обратитесь к другой моей пошаговой статье по разборке:
15. Используйте ES5 для достижения наследования классов
Это тоже ключевое знание. Я уже делал детальную разборку ранее. Есть пять версий. Если каждую версию можно объяснить понятно, это вполне может отражать мое понимание цепочки прототипов. Адрес статьи: