手写基础原理
手写call
Function.prototype.myCall = function (context) {
if (typeof this !== "function") {
throw new Error("类型错误");
}
const args = [...arguments].slice(1);
const currentContext = context || window;
const fn = Symbol();
currentContext[fn] = this;
const res = currentContext[fn](...args);
Reflect.deleteProperty(currentContext, fn);
return res;
};
手写apply
Function.prototype.myApply = function (context) {
if (typeof this !== "function") {
throw new Error("类型错误");
}
const currentContext = context || window;
const fn = Symbol();
currentContext[fn] = this;
let res = null;
if (arguments[1]) {
res = currentContext[fn](...arguments[1]);
} else {
res = currentContext[fn]();
}
Reflect.deleteProperty(currentContext, fn);
return res;
};
手写bind
Function.prototype.myBind = function (context) {
if (typeof this !== "function") {
throw new Error("类型错误");
}
const self = this;
const args = [].slice.apply(arguments, 1);
const fBind = function () {
const currentArgs = [].slice.apply(arguments);
return self.apply(
this instanceof fBind ? this : context,
arg.concat(currentArgs)
);
};
fBind.prototype = Object.create(this.prototype);
return fBind;
};
柯里化函数实现
function curry(fn) {
if (fn.length <= 1) return fn;
const generator = (...args) => {
if (fn.length === args.length) {
return fn(...args);
} else {
return (...args2) => {
return generator(...args, ...args2);
};
}
};
return generator;
}
简易版防抖函数
function simpleDebounce(func, wait) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
简易版节流函数
function simpleThrottle(func, wait) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
func.apply(this, arguments);
canRun = true;
}, wait);
};
}
事件委托实现
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;
}
手写实现instanceof
function myInstanceof(left, right) {
let prototype = right.prototype;
left = left._proto_;
while (true) {
if (left === null || left === undefined) {
return false;
}
if (prototype === left) {
return true;
}
left = left._proto_;
}
}
简易封装JSONP
function jsonp(url, callback, success) {
let script = document.createElement("script");
script.src = url;
script.async = true;
script.type = "text/javascript";
window[callback] = function(data) {
success && success(data);
};
document.body.appendChild(script);
}
手写new原理
function myNew(fn, ...args) {
if (typeof fn !== "function") {
throw new Error("类型错误");
}
const obj = Object.create(fn.prototype);
const res = fn.apply(obj, args);
return res instanceof Object ? res : obj;
}
手写Object.create
function create(proto){
function Fn(){}
Fn.prototype = proto;
Fn.prototype.constructor = Fn;
return new Fn();
}
简易浅拷贝
function shallowClone(obj) {
function isObject() {
return typeof o === "object";
}
if (!isObject(obj)) return obj;
let target = {};
Reflect.ownKeys(obj).forEach((key) => {
target[key] = obj[key];
});
}
简易深拷贝
function deepClone(obj) {
function isObject(o) {
return typeof o === "object";
}
if (!isObject(obj)) return obj;
let isArray = Array.isArray(obj);
let newObj = isArray ? [] : {};
Reflect.ownKeys(newObj).forEach((key) => {
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
});
return newObj;
}
手写实现filter方法
const myFilter = function(fn, context) {
const arr = [].slice.call(this);
const filterArray = [];
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue;
fn.call(context, arr[i], i, this) && filterArray.push(arr[i]);
}
return filterArray;
};
手写实现map方法
const myMap = function(fn, context) {
const arr = [].slice.call(this);
const resultArray = [];
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue;
resultArray.push(fn.call(context, arr[i], i, this));
}
return resultArray;
};
手写实现reduce
Array.prototype._reduce = function (callback, initVal) {
let i = 0;
let result = initVal;
if (typeof initVal === "undefined") {
result = this[0];
i++;
}
for (i; i < this.length; i++) {
result = callback(result, this[i], i);
}
return result;
};
通过reduce实现filter
const myReduceFilter = function (fn, context) {
const arr = [].slice.call(this);
return arr.reduce((total, cur, index) => {
return fn.call(context, total, cur, index, this)
? [...total, cur]
: [...total];
}, []);
};
通过reduce实现flat
const myFlat = function (deep = 1) {
const arr = [].slice.call(this);
if (deep === 0) return arr;
return arr.reduce((total, cur) => {
if (Array.isArray(cur)) {
return [...total, ...myFlat.call(cur, deep - 1)];
} else {
return [...total, cur];
}
}, []);
};
通过reduce实现flat
const myReduceToMap = function (fn, context) {
const arr = [].slice.call(this);
return arr.reduce((total, cur, index) => {
return [...total, fn.call(context, total, cur, index, this)];
}, []);
};
Promise的简易实现版本
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
const that = this;
that.state = PENDING;
that.value = null;
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED;
that.value = value;
that.resolvedCallbacks.map((cb) => {
cb(value);
});
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED;
that.value = value;
that.rejectedCallbacks.map((cb) => {
cb(value);
});
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const that = this;
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === "function"
? onRejected
: (r) => {
throw r;
};
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled);
that.rejectedCallbacks.push(onRejected);
}
if (that.state === RESOLVED) {
onFulfilled(that.value);
}
if (that.state === REJECTED) {
onRejected(that.value);
}
};
手写进阶实现
实现一个缓存函数
const memorize = function (fn) {
const catchs = {};
return function (...args) {
const _args = JSON.stringify(args);
return catchs[_args] || (catchs[_args] = fn.apply(null, args));
};
};
const add = function (a, b) {
return a + b;
};
const adder = memorize(add);
adder(2, 6);
adder(2, 6);
设计实现一个限制最大运行数量的函数
function maxTask(tasks, n) {
return new Promise((resolve, reject) => {
let start = 0;
let index = 0;
let finish = 0;
const res = [];
function processValue(index, value) {
start -= 1;
finish += 1;
res[index] = value;
runTask();
}
function runTask() {
if (finish === n) {
resolve(res);
return;
}
while (start < n && index < tasks.length) {
start += 1;
let cur = index;
Promise.resolve(tasks[cur]())
.then((value) => {
processValue(cur, value);
})
.catch((err) => {
processValue(cur, err);
});
}
}
runTask();
});
}
实现lodash的【get】方法
function myGet(targetObj, path, defaultValue) {
if (!path || typeof path !== "string") {
return defaultValue;
}
const transPath = path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = targetObj;
for (const p of transPath) {
result = Object(result)[p];
if (result === undefined) {
return defaultValue;
}
}
return result;
}
实现一个重启函数,(请求失败会重启请求,到达次数上限后抛出错误)
function getMyData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.ceil(Math.random() * 20);
if (num <= 5) {
console.log("符合条件", num);
resolve(num);
}
reject(`${num}不符合条件`);
}, 2000);
});
}
function myGetData(fn, times = 5, delay = 1000) {
return new Promise((resolve, reject) => {
function attempt() {
fn().then(resolve).catch((err) => {
console.log(`还有${times}次重启机会`);
if (times === 0) {
reject(err);
} else {
times -= 1;
setTimeout(attempt, delay);
}
});
}
attempt();
});
}
实现以给定前缀的累加函数
const getUniqId = (() => {
const keyMap = new Map();
return function (currentStr) {
if (typeof currentStr !== "string") {
return "";
}
if (keyMap.get(currentStr)) {
const value = keyMap.get(currentStr);
keyMap.set(currentStr, value + 1);
return `${currentStr}_${value + 1}`;
} else {
keyMap.set(currentStr, 1);
return `${currentStr}_1`;
}
};
})();
实现一个检验对象是否是循环引用的函数
const isCyclic = (obj) => {
try {
JSON.stringify(obj);
return false;
} catch {
return true;
}
};
实现一个以给定数组【去重】并输出从大到小的【货币格式】
const nums = [7, 8, 3, 5, 1, 2, 4, 3, 1];
const sortNums = nums.sort((a, b) => a - b);
const targetNums = [...new Set(sortNums)];
const targetStr = targetNums.join("");
const dealNumber = (money) => {
if (!money) {
return "";
}
const temp = money.match(/(\d{1,3})/g);
return temp.join(",").split("").reverse().join("");
};
实现一个按顺序加载img的函数
const loadImg = (url) => {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = url;
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject("error");
};
});
};
const dealWithImgs = (imgs) => {
const imgQueue = [];
for (let i = 0; i < imgs.length; i += 1) {
imgQueue.push(loadImg(imgs[i]));
}
Promise.all(imgQueue)
.then((item) => {})
.catch((err) => {});
};
dealWithImgs(imgs);
订阅-发布模式实现
class EvevtEmitter {
constructor() {
this.handlers = {};
}
on(eventName, cb) {
if (!this.handlers[eventName]) {
this.handlers[eventName] = [];
}
this.handlers[eventName].push(cb);
}
emit(eventName, ...args) {
if (this.handlers[eventName]) {
this.handlers[eventName].forEach((cb) => {
cb(...args);
});
}
}
off(eventName, cb) {
const callbacks = this.handlers[eventName];
const index = callbacks.indexOf(cb);
if (index !== -1) {
this.handlers[eventName].splice(index, 1);
}
}
once(eventName, cb) {
const wrapper = (...args) => {
cb.apply(null, args);
this.off(eventName, wrapper);
};
this.on(eventName, wrapper);
}
}
高频算法题
实现LRU
class LRU {
constructor(max) {
this.max = max;
this.map = new Map();
}
get(key) {
const value = this.map.get(key);
if (value === undefined) {
return -1;
}
this.map.delete(key);
this.map.set(key, value);
return value;
}
set(key, value) {
if (this.map.get(key)) {
this.map.delete(key);
}
this.map.set(key, value);
const keys = this.map.keys();
while (this.map.size > this.max) {
this.map.delete(keys.next().value);
}
}
}
青蛙跳台阶
const numWays = (n) => {
if (n === 1) {
return 1;
}
const dp = [];
dp[0] = 1;
dp[1] = 1;
const max = 1e9 + 7;
for (let i = 2; i <= n; i += 1) {
dp[i] = (dp[i - 1] + dp[i - 2]) % max;
}
return dp[n];
};
二分矩阵搜索
const searchFunc = (matrix, target) => {
if (matrix.length === 0 || matrix[0].length === 0 || matrix[0][0] > target) {
return false;
}
let i = matrix.length - 1;
let j = 0;
while (i >= 0 && j < matrix[0].length) {
if (matrix[i][j] === target) {
return true;
}
if (matrix[i][j] < target) {
j += 1;
} else {
i -= 1;
}
}
return false;
};
反转链表
const reverseList = (head) => {
let pre = null;
let cur = head;
while (cur.next) {
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
};
三数求和
const threeSum = function (sendSums, target = 0) {
const res = [];
if (Array.isArray(sendSums)) {
const nums = sendSums.sort((a, b) => {
return a - b;
});
for (let i = 0; i < nums.length - 2; i += 1) {
let left = i + 1;
let right = nums.length - 1;
if (i > 0 && nums[i] === nums[i - 1]) {
continue;
}
while (left < right) {
if (nums[i] + nums[left] + nums[right] < target) {
left += 1;
while (left < right && nums[left] === nums[left - 1]) {
left += 1;
}
} else if (nums[i] + nums[left] + nums[right] > target) {
right -= 1;
while (left < right && nums[right] === nums[right + 1]) {
right -= 1;
}
} else {
res.push([nums[i], nums[left], nums[right]]);
left += 1;
right -= 1;
while (left < right && nums[left] === nums[left - 1]) {
left += 1;
}
while (left < right && nums[right] === nums[right + 1]) {
right -= 1;
}
}
}
}
}
return res;
};
模拟栈算法实现
class Mystack {
constructor() {
this.stack = [];
this.minStack = []
}
push(x) {
this.stack.push(x);
if (!this.minStack.length || this.minStack[this.minStack.length - 1] >= x) {
this.minStack.push(x);
}
}
pop() {
const popValue = this.stack.pop();
if (popValue === this.minStack[this.minStack.length - 1]) {
this.minStack.pop();
}
}
top() {
return this.stack[this.stack.length - 1];
}
getMin() {
return this.minStack[this.minStack.length - 1];
}
}
数组转树结构
const listToTree = (arr) => {
const map = {};
let node;
const tree = [];
for (let i = 0; i < arr.length; i += 1) {
map[arr[i].id] = arr[i];
arr[i].children = [];
}
for (let i = 0; i < arr.length; i += 1) {
node = arr[i];
if (node.pid !== "0") {
map[node.pid].children.push(node);
} else {
tree.push(node);
}
}
return tree;
};
树转扁平化数组
const treeToList = (tree) => {
let queen = [].concat(tree);
const out = [];
while (queen.length) {
let first = queen.shift();
if (first.children) {
queen = queen.concat(first.children);
Reflect.deleteProperty(first, "children");
}
out.push(first);
}
return out;
};