Appearance
手写代码题
浅拷贝
js
// 方法一
const _shallowClone = (target) => {
if (typeof target === "object" && target !== null) {
let cloneObj = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneObj[key] = target[key];
}
}
return cloneObj;
} else {
return target;
}
};
// 方法二
let ans = Object.assign({}, target);
// 方法三
let ans = { ...target };
深拷贝
javascript
// 方法一
const _deepClone = (target) => {
let cloneObj = {};
for (let key in target) {
if (typeof target[key] === "object") {
cloneObj[key] = _deepClone(target[key]);
} else {
cloneObj[key] = target[key];
}
}
return cloneObj;
};
// 方法二
function deepClone(val, map = new WeakMap()) {
if (val === null || typeof val !== "object") return val;
//循环引用
if (map.has(val)) return map.get(val);
let clone = Array.isArray(val) ? [] : {};
map.set(val, clone);
// 获取对象中所有的属性名(包含Symbol值)
let keys = Reflect.ownKeys(val);
//(可换为:Object.keys(val).concat(Object.ownPropertySymbols(val)))
let len = keys.length;
while (len--) {
clone[keys[len]] = deepClone(val[keys[len]], map);
}
return clone;
}
防抖
js
function debounce(fn, wait) {
var timer = null;
return function () {
var context = this,
args = [...arguments];
// 如果此时存在定时器的话,则取消之前的定时器重新记时
if (timer) {
clearTimeout(timer);
timer = null;
}
// 设置定时器,使事件间隔指定事件后执行
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
// 立即执行版本
function debounce(fn, wait) {
var timer = null;
return function () {
var context = this,
args = [...arguments];
// 如果此时存在定时器的话,则取消之前的定时器重新记时
if (timer) clearTimeout(timer);
//定义wait时间后把timer变为null
//即在wait时间之后事件才会有效
// timer为null,那么执行func函数
if (!timer) fn.apply(context, args);
timer = setTimeout(() => {
timer = null;
}, wait);
};
}
节流
js
// 时间戳版
function throttle(fn, delay) {
var preTime = Date.now();
return function () {
var context = this,
args = [...arguments],
nowTime = Date.now();
// 如果两次时间间隔超过了指定时间,则执行函数。
if (nowTime - preTime >= delay) {
preTime = Date.now();
return fn.apply(context, args);
}
};
}
// 定时器版
function throttle(fun, wait) {
let timeout = null;
return function () {
let context = this;
let args = [...arguments];
if (!timeout) {
timeout = setTimeout(() => {
fun.apply(context, args);
timeout = null;
}, wait);
}
};
}
Promise
js
class MyPromise {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.state = MyPromise.PENDING;
this.value = null;
this.reason = null;
this.resolvedCallback = [];
this.rejectedCallback = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
resolve(value) {
setTimeout(() => {
if (this.state === MyPromise.PENDING) {
this.state = MyPromise.FULFILLED;
this.value = value;
this.resolvedCallback.forEach((callback) => {
callback(value);
});
}
});
}
reject(reason) {
setTimeout(() => {
if (this.state === MyPromise.PENDING) {
this.state = MyPromise.REJECTED;
this.reason = reason;
this.resolvedCallback.forEach((callback) => {
callback(reason);
});
}
});
}
then(onfulfilled, onrejected) {
return new MyPromise((resolve, reject) => {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : () => {};
onrejected = typeof onrejected === "function" ? onrejected : () => {};
if (this.state === MyPromise.PENDING) {
this.resolvedCallback.push(onfulfilled);
this.rejectedCallback.push(onrejected);
}
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
onfulfilled(this.value);
});
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
onrejected(this.reason);
});
}
});
}
}
Promise.all
js
function promiseAll(promises) {
return new Promise((resolve, reject) => {
let result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i])
.then((res) => {
result[i] = res;
count++;
if (count == promiseNum) {
return resolve(result);
}
})
.catch((err) => {
return reject(err);
});
}
});
}
// 手写一个Promise.all方法,但是存在最大并发数量限制
const promiseAll = (promiseLists = [], limit = Infinity) => {
return new Promise((resolve, reject) => {
let n = promiseLists.length;
let result = [];
let count = 0;
let queue = [...promiseLists];
function run() {
if (queue.length > 0) {
let time = queue.shift();
return time()
.then((res) => {
result.push(res);
count++;
return run();
})
.catch((err) => reject(err))
.finally(() => {
if (count === n) {
resolve(result);
}
});
}
}
for (let i = 0; i < Math.min(limit, n); i++) {
Promise.resolve().then(run());
}
});
};
Promise.race
js
Promise.race = function (args) {
return new Promise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject);
}
});
};
Array.prototype.reduce
js
Array.prototype._reduce = function (fn, prev) {
for (let i = 0; i < this.length; i++) {
if (prev === undefined) {
prev = fn(this[i], this[i + 1], i + 1, this);
++i;
} else {
prev = fn(prev, this[i], i, this);
}
}
return prev;
};
Array.prototype.filter
js
Array.prototype._filter = function (Fn) {
if (typeof Fn !== "function") return;
const array = this;
const newArray = [];
for (let i = 0; i < array.length; i++) {
const result = Fn.call(arguments[1], array[i], i, array);
result && newArray.push(array[i]);
}
return newArray;
};
Array.prototype.map
js
Array.prototype._map = function (Fn) {
if (typeof Fn !== "function") return;
const array = this;
const newArray = new Array(array.length);
for (let i = 0; i < array.length; i++) {
let result = Fn.call(arguments[1], array[i], i, array);
newArray[i] = result;
}
return newArray;
};
Array.prototype.flat
js
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);
}
}, []);
}
new
js
const _new = function (constructor, ...args) {
// new关键字做了4件事
// 1. 创建一个新对象
const obj = {};
// 2. 为新对象添加属性__proto__,将该属性链接至构造函数的原型对象
obj.__proto__ = constructor.prototype;
// 3. 执行构造函数,this被绑定在新对象上
const res = constructor.apply(obj, args);
// 4. 确保返回一个对象
return res instanceof Object ? res : obj;
};
const _new = function () {
const object1 = {};
const Fn = [...arguments].shift();
object1.__proto__ = Fn.prototype;
const object2 = Fn.apply(object1, arguments);
return object2 instanceof Object ? object2 : object1;
};
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(构造函数, 初始化参数);
instanceof
js
const myInstanceof = (left, right) => {
//基本数据类型的判断
if (target === null || typeof target !== "object") {
return false;
}
let leftProto = Object.getPrototypeOf(left);
let rightProto = right.prototype;
while (true) {
if (leftProto === null) return false;
if (leftProto === rightProto) return true;
leftProto = Object.getPrototypeOf(leftProto);
}
};
bind
js
Function.prototype._bind = function (context, ...args) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
return function (..._args) {
args = args.concat(_args);
context[fnSymbol](...args);
delete context[fnSymbol];
};
};
apply
js
Function.prototype._apply = function (context, argsArr) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
context[fnSymbol](...argsArr);
delete context[fnSymbol];
};
call
js
Function.prototype._call = function (context, ...args) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
context[fnSymbol](...args);
delete context[fnSymbol];
};
Object.create()
js
const _objectCreate = (proto) => {
if (typeof proto !== "object" || proto === null) return;
const fn = function () {};
fn.prototype = proto;
return new fn();
};
数组去重
js
// 1. Set
Array.from(new Set(array));
[...new Set(array)];
// 2. for
let arr = [];
for (let i = 0; i < array.length; i++) {
// or forEach,map
if (arr.indexOf(array[i]) === -1) {
// 如果数组元素有多个NaN,可以使用includes
arr.push(array[i]);
}
}
// 3. reduce + includes
array.reduce((pre, cur) => {
!pre.includes(cur) && pre.push(cue);
return pre;
}, []);
// 4. filter + includes
let newArr = [];
array.filter((item) => {
return !newArr.includes(item) && (newArr[newArr.length] = item);
});
数组扁平化
js
// 方法一
Array.prototype.myFlat = (arr) => {
return [].concat(
...arr.map((item) => {
return Array.isArray(item) ? myFlat(item) : item;
})
);
};
// 方法二
Array.prototype.myFlat = (arr) => {
return arr.reduce((a, b) => {
return a.concat(Array.isArray(b) ? myFlat(b) : b);
}, []);
};
function getFlatArr(list, deepNum = 1) {
return deepNum > 0
? list.reduce((pre, item) => {
return pre.concat(Array.isArray(item) ? getFlatArr(item, deepNum - 1) : item);
}, [])
: list.slice();
}
sleep()
js
// 方法一
const sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, time);
});
};
sleep(1000).then(() => {
console.log(1);
});
// 方法二
function sleep(callback, time) {
if (typeof callback === "function") setTimeout(callback, time);
}
function output() {
console.log(1);
}
sleep(output, 1000);
List To Tree
js
const convert = (list) => {
let res = [];
let map = list.reduce((pre, cur) => {
pre[cur.id] = cur;
return pre;
}, {});
for (const item of list) {
if (item.parentId === 0) {
res.push(item);
} else {
if (item.parentId in map) {
const parent = map[item.parentId];
parent.children = parent.children || [];
parent.children.push(item);
}
}
}
return res;
};
// 原始 list 如下
let list = [
{ id: 1, name: "部门A", parentId: 0 },
{ id: 2, name: "部门B", parentId: 0 },
{ id: 3, name: "部门C", parentId: 1 },
{ id: 4, name: "部门D", parentId: 1 },
{ id: 5, name: "部门E", parentId: 2 },
{ id: 6, name: "部门F", parentId: 3 },
{ id: 7, name: "部门G", parentId: 2 },
{ id: 8, name: "部门H", parentId: 4 },
];
const result = convert(list);
console.log(result);
compose 函数
js
const add10 = (x) => x + 10;
const mul10 = (x) => x * 10;
const add100 = (x) => x + 100;
// (10 + 100) * 10 + 10 = 1110
compose(add10, mul10, add100)(10);
function compose(...fns) {
return fns.reduce((a, b) => (...args) => {
return a(b(...args));
});
}
const composeAsync = (...fns) => {
return (x) => {
return fns.reduceRight((prev, cur) => {
return prev.then(cur);
}, Promise.resolve(x));
};
};
composeAsync(
add10,
mul10,
add100
)(10).then((res) => {
console.log(res);
});
(5).add(3).minus(2) = 6
js
Number.prototype.add = function (x) {
return this + x;
};
Number.prototype.minus = function (x) {
return this - x;
};
console.log((5).add(3).minus(2)); // 6
控制并发数
javascript
function imageLoad(urls, fetchImage, limit) {
//先创建长度为limit的池子
let pool = urls.splice(0, limit).map((item, index) => {
return fetchImage(item).then((res) => {
return index; //将当前加载完图片的 index返回
});
});
let p = Promise.race(pool); //将第一个执行成功的 先返回
for (let i = 0; i < urls.length; i++) {
p = p.then((index) => {
//形成链式调用.then .then
pool[index] = fetchImage(urls[i]).then(() => {
//将已经加载完成的图片重新替换成新的 未加载的图片
return index;
});
return Promise.race(pool);
});
}
}
// 执行
imageLoad(imageUrls, fetchImage, 3);
function imageLoad1(urls, limit) {
function run() {
if (urls.length > 0) {
const url = urls.shift();
return fetchImage(url).then((res) => {
return run(); //当图片请求成功之后继续递归调用run
});
}
}
// 当imageUrls.length < limit的时候,我们也没有必要去创建多余的Promise
const promiseList = Array(Math.min(limit, urls.length))
.fill(Promise.resolve())
.map((promise) => promise.then(run));
Promise.all(promiseList).then(() => {
//全部加载完成后的操作
});
}
imageLoad1(imageUrls, 3);
发布订阅模式
js
class EmitEvent {
constructor() {
this.eventMap = {};
}
on(event, fn) {
const listeners = this.eventMap[event] || (this.eventMap[event] = []);
listeners.push(fn);
}
emit(event, ...args) {
this.eventMap[event]?.forEach((fn) => {
fn.call(null, args);
});
}
off(event, fn) {
if (!this.eventMap[event]) return;
this.eventMap[event] = this.eventMap[event].filter((cn) => fn !== cb);
}
once(event, fn) {
this.on(event, (...args) => {
fn.call(null, args);
this.off(event, fn);
});
}
}
const event = new EmitEvent();
event.on("click", () => {
console.log("click");
});
event.emit("click");
EventBus
js
class EventBus {
events = new Map();
constructor() {}
on(type, fn) {
const callbacks = this.events.get(type);
if (callbacks) {
callbacks.push(fn);
} else {
this.events.set(type, [fn]);
}
}
emit(type, ...args) {
const context = this;
const callbacks = this.events.get(type);
if (!callbacks) return;
callbacks.forEach((fn) => {
fn.apply(context, [...args]);
});
}
}
mergePromise
js
const timeout = (ms) =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});
const ajax1 = () =>
timeout(2000).then(() => {
console.log("1");
return 1;
});
const ajax2 = () =>
timeout(1000).then(() => {
console.log("2");
return 2;
});
const ajax3 = () =>
timeout(2000).then(() => {
console.log("3");
return 3;
});
// --------- 1 --------
const mergePromise = (ajaxArray) => {
// 1,2,3 done [1,2,3] 此处写代码 请写出ES6、ES3 2中解法
var data = [];
var sequence = Promise.resolve();
ajaxArray.forEach((item) => {
sequence = sequence.then(item).then((res) => {
data.push(res);
return data;
});
});
return sequence;
};
// ---------- 2 -------
const mergePromise = (ajaxArray) => {
let data = [];
return ajaxArray.reduce((prev, curr) => {
return prev.then(curr).then((res) => {
data.push(res);
return data;
});
}, Promise.resolve());
};
mergePromise([ajax1, ajax2, ajax3]).then((data) => {
console.log("done");
console.log(data); // data 为[1,2,3]
});
// 执行结果为:1 2 3 done [1,2,3]
EventEmitter
typescript
type FnCallback = (...args) => any;
class EventEmitter {
eventMap: { [key in string]?: FnCallback[] } = {};
constructor() {}
on(event: string, fn: FnCallback) {
const listeners = this.eventMap[event] || (this.eventMap[event] = []);
listeners.push(fn);
}
emit(event: string, ...args: any) {
this.eventMap[event]?.forEach((fn) => {
fn.call(null, args);
});
}
off(event: string, fn: FnCallback): void {
if (!this.eventMap[event]) return;
this.eventMap[event] = this.eventMap[event].filter((cn) => fn !== cb);
}
once(event, fn: FnCallback) {
this.on(event, (...args: any) => {
fn.call(null, args);
this.off(event, fn);
});
}
}