js原生工具方法原理实现
call原理实现
Function.prototype.call = function (ctx, ...params) {
ctx === null ? ctx = window : null;
// 如果上下文不是对象将上下文转化为对象
!/^(object|function)$/.test(typeof ctx) ? ctx = Object(ctx) : null;
const key = Symbol("key");
ctx[key] = this;
const result = ctx[key](...params);
delete ctx[key];
return result;
};
apply原理实现
Function.prototype.apply = function (ctx, params) {
ctx === null ? ctx = window : null;
if (!Array.isArray(params)) {
throw new TypeError("CreateListFromArrayLike called on non-object");
}
// 如果上下文不是对象将上下文转化为对象
!/^(object|function)$/.test(typeof ctx) ? ctx = Object(ctx) : null;
const key = Symbol("key");
ctx[key] = this;
const result = ctx[key](...params);
delete ctx[key];
return result;
};
bind原理实现(真正的bind返回的函数没有原型链)
Function.prototype.Bind = function (target) {
// 放在myBind在Function的原型上,将目标函数指向对象传入
let self = this; // this表示调用的函数
let Temp = function () {};
let args = [].slice.call(arguments, 1); // 将arguments伪数组转化为真数组
function func() { // 返回新的一个函数,this指向新的对象
let _args = [].slice.call(arguments, 0); // 将arguments转化为真数组
self.apply(this instanceof self ? this : target || window, args.concat(_args));
}
Temp.prototype = self.prototype;
func.prototype = new Temp();
return func;
};
es6的Map集合
class Map {
constructor(iterable = []) {
if (typeof iterable[Symbol.iterator] !== "function") {
throw new Error(iterable + "传入不是可迭代对象");
}
this._Entries = [];
for (const item of iterable) {
if (typeof item[Symbol.iterator] !== "function") {
throw new Error(item + "不是可迭代对象");
}
const iterator = item[Symbol.iterator]();
const key = iterator.next().value;
const value = iterator.next().value;
this.set(key, value);
}
}
set(key, value) {
if (this.has(key)) {
this._getObject(key).value = value;
} else {
this._Entries.push({
key,
value
});
}
}
get(key) {
const item = this._getObject(key);
if (item) {
return item.value;
} else {
return false;
}
}
get size() {
return this._Entries.length;
}
_getObject(key) {
for (const item of this._Entries) {
if (this.isEqual(item.key, key)) return item;
}
}
has(key) {
return this._getObject(key) !== undefined;
}
delete(key) {
for (let i = 0; i < this._Entries.length; i++) {
if (this.isEqual(this._Entries[i].key, key)) {
this._Entries.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this._Entries.length = 0;
}
isEqual(value1, value2) {
if (value1 === 0 && value2 === 0) {
return true;
}
return Object.is(value1, value2);
}
*[Symbol.iterator]() {
for (const el of this._Entries) {
yield [el.key, el.value];
}
}
forEach(callback) {
for (const el of this._Entries) {
callback(el.value, el.key, el);
}
}
}
es6的Set集合
class Set {
constructor(iterator = []) {
if (typeof iterator[Symbol.iterator] !== "function") {
throw new TypeError("传入的不是可迭代对象!");
}
this._datas = [];
for (const data of iterator) {
this.add(data);
}
}
add(data) {
if (!this.has(data)) {
this._datas.push(data);
}
}
has(data) {
for (const item of this._datas) {
if (this.isEqual(data, item)) {
return true;
}
}
return false;
}
delete(data) {
for (let i = 0; i < this._datas.length; i++) {
if (this.isEqual(data, this._datas[i])) {
this._datas.splice(i, 1);
return true;
}
return false;
}
}
*[Symbol.iterator]() {
for (const item of this._datas) {
yield item;
}
}
forEach(func) {
for (const item of this._datas) {
func(item, item, this);
}
}
clear() {
this._datas.length = 0;
}
isEqual(value1, value2) {
if (value1 === 0 && value2 === 0) {
return true;
}
return Object.is(value1, value2);
}
get size() {
return this._datas.length;
}
}
数组的高阶函数遍历方法原理实现
Array.prototype.myForEach = function (func, _this) {
const arr = this;//得到调用数组本身(谁调用的这个方法,this就指向谁)
const len = arr.length;//得到数组长度
for (let i = 0; i < len; i++) {
func.apply(_this, [arr[i], i, arr])
// 执行传入的函数,_this为传入的对象,如果没有传入的_this指向,则为undefined,指向window
// 分别将数组每一项值,数组索引,数组本身作为参数传入,执行函数
}
}
Array.prototype.myFilter = function (func, _this) {
const arr = this
const len = arr.length
const newArr = []
for (let i = 0; i < len; i++) {
func.apply(_this, [arr[i], i, arr]) && newArr.push(arr[i])
}
return newArr
}
Array.prototype.myReduce = function (func, initValue) {
var _arr = this, len = this.length
for (var i = 0; i < len; i++) {
if (!initValue) { //如果没有initValue
initValue = _arr[0] //将数组第一个值赋给initValue
i++ //索引从第二个开始
}
initValue = func.apply(initValue, [initValue, _arr[i], i, _arr])
}
return initValue;
}
Array.prototype.myMap = function (func, _this) {
const oldArr = this;
const newArr = [];
for (let i = 0; i < oldArr.length; i++) {
// 处理稀松数组
if (!Object.hasOwnProperty.call(oldArr, i)) continue;
newArr.push(func.apply(_this, [oldArr[i], i, oldArr]));
}
return newArr;
};
Object.is()方法实现
Object.is = function (x, y) {
if (x === y) {
// 处理+0 !== -0
// 如果x不是0,+0,-0直接返回true
// 如果x是0,则两个都是0,根据+Infinite===-Infinite => false进行处理
return x !== 0 || 1 / x === 1 / y;
}
// 处理NaN
return x !== x && y !== y;
};
console.log(Object.is(NaN, NaN));
instanceOf原理实现
function instanceOf(obj, func) {
while (Object.getPrototypeOf(obj)) {
if (obj === func.prototype) return true;
obj = Object.getPrototypeOf(obj);
}
return false;
}
console.log(instanceOf(t, Test));
new的实现
function myNew(func, ...args) {
const obj = Object.create(func.prototype);
const rt = func.apply(obj, args);
return typeof rt === "object" ? rt : obj;
}
常见工具函数手写
jsonp前端简易实现
function jsonp({
url,
params,
success
}) {
let paramsStr = url.split("").indexOf("?") !== -1 ? "&" : "?";
for (const key in params) {
if (Object.hasOwnProperty.call(params, key)) {
paramsStr += key + "=" + params[key] + "&";
}
}
const callback = "callback" + Math.random().toString(36).replace(".", "");
const path = url + paramsStr + "callback=" + callback;
const script = document.createElement("script");
script.src = path;
window[callback] = success;
document.body.appendChild(script);
document.body.removeChild(script);
}
jsonp({
url: "https://developer.duyiedu.com/edu/testJsonp",
params: {
name: "zf",
like: "web"
},
success(data) {
console.log(data);
}
});
珂里化
function curry(func, ...args) {
const arity = func.length;
if (args.length >= arity) {
return func.apply(this, args);
} else {
// 参数不够,返回一个函数,将这个新函数的参数和之前的参数进行组合执行
return (...args1) => {
const total = [...args, ...args1];
return curry(func, ...total);
};
}
}
// 初始化不传参
function curry1(func) {
let arity = [];
return function next(...args) {
arity = [...arity, ...args];
if (arity.length >= func.length) {
func.apply(func, arity);
} else {
return next;
}
};
}
深克隆
function deepCopy(obj) {
if (typeof obj === "object" && obj !== null) {
if (Object.prototype.toString.call(obj) === "[object Date]") {
return new Date(obj);
}
if (Object.prototype.toString.call(obj) === "[object RegExp]") {
return new RegExp(obj);
}
const retObj = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
retObj[key] = deepCopy(obj[key]);
}
}
return retObj;
} else {
return obj;
}
}
判断是否是简单对象(redux)
function isPlainObject(obj) {
if (typeof obj !== "object" || obj === null) return false;
let proto = obj;
while (Object.getPrototypeOf(proto)) {
proto = Object.getPrototypeOf(proto);
}
return proto === Object.getPrototypeOf(obj);
}
jquery数据类型检测封装
const getProto = Object.getPrototypeOf;
const class2type = {};
const toString = class2type.toString;
const hasOwn = class2type.hasOwnProperty;
const fnToString = hasOwn.toString;
const ObjectFunctionString = fnToString.call(Object);
[
"Boolean",
"Number",
"String",
"Symbol",
"Function",
"Array",
"Date",
"RegExp",
"Object",
"Error",
"GeneratorFunction"
].forEach(name => {
class2type[`[object ${name}]`] = name.toLocaleLowerCase();
});
function toType(type) {
// eslint-disable-next-line eqeqeq
if (type == null) {
return type + "";
}
return typeof type === "function" || typeof type === "object" ? class2type[toString.call(type)] || "object" : typeof type;
}
function isFunction(obj) {
// 有一些浏览器dom类型用typeof检测是function
return typeof obj === "function" && typeof obj.nodeType !== "number";
}
// 检测是否为window对象
function isWindow(obj) {
return obj && obj === obj.window;
}
// 检测目标对象是否为数组或者类数组
function isArrayLike(obj) {
const length = obj && "length" in obj && obj.length;
const type = toType(obj);
if (isFunction(obj) || isWindow(obj)) {
return false;
}
return type === "array" ||
length === 0 ||
typeof length === "number" && length > 0 && (length - 1) in obj;
}
function isPlainObject(obj) {
const proto = getProto(obj);
if (!obj || toType(obj) !== "object") return false;
// 没有原型链,可能是通过Object.create(null)创建的对象
if (!proto) return true;
const constructor = hasOwn.call(proto, "constructor") && proto.constructor;
return typeof constructor === "function" && fnToString.call(constructor) === ObjectFunctionString;
}
function isEmptyObject(obj) {
var keys = [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj)
];
return keys.length === 0;
}
merge函数(合并对象)
// 引用上面实现的isPlainObject函数判断是否为简单对象
import isPlainObject from "./isPlainObject";
function merge(obj1, obj2) {
let isPlainObj1 = isPlainObject(obj1);
let isPlainObj2 = isPlainObject(obj2);
if (!isPlainObj1) return obj2;
if (!isPlainObj2) return obj1;
// 都是简单对象
for (const key in obj2) {
if (Object.hasOwnProperty.call(obj2, key)) {
obj1[key] = merge(obj1[key], obj2[key]);
}
}
return obj1;
}
管道函数
function pipe(...func) {
return function (value) {
for (let i = 0; i < func.length; i++) {
value = func[i](value);
}
return value;
};
}
compose函数
- redux
function compose(...middleware) {
if (middleware.length === 0) return (args) => args;
if (middleware.length === 1) return middleware[0];
return middleware.reduce((pre, cur) => {
return (...arg) => {
return pre(cur(...arg));
};
});
}
- koa洋葱模型
function compose(middleware) {
return function () {
return dispatch(0);
function dispatch(i) {
// 取出将要执行的函数
let fn = middleware[i];
// 如果fn为空 直接返回已决状态
if (!fn) return Promise.resolve();
// 将下一个函数作为参数next传给当前函数
return Promise.resolve(fn(function next() {
return dispatch(i + 1);
}));
}
};
}
防抖函数
function debounce(func, wait = 300, immediate = false) {
let timer = null;
return (...params) => {
const nowExec = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
!immediate && func.call(this, ...params);
}, wait);
nowExec && func.call(this, ...params);
};
}
节流函数
function throttle(func, wait = 300) {
let preDate = 0;
let timer = null;
return (...params) => {
const nowDate = Date.now();
const remaining = wait - (nowDate - preDate);
if (remaining <= 0) {
// 时间到了,触发函数
preDate = nowDate;
clearTimeout(timer);
timer = null;
func.call(this, ...params);
} else if (timer === null) {
timer = setTimeout(() => {
preDate = Date.now();
func.call(this, ...params);
timer = null;
}, remaining);
}
};
}
生成器模拟async await
function func(x) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(x + 1);
}, 1000);
});
}
function* generator(x) {
const r1 = yield func(x);
console.log(r1);
const r2 = yield func(r1);
console.log(r2);
const r3 = yield func(r2);
console.log(r3);
}
const gen = generator();
function asyncGenerator(generator, ...params) {
const gen = generator(...params);
function next(args) {
const {
value,
done
} = gen.next(args);
if (done) return;
value.then(x => next(x));
}
next();
}
asyncGenerator(generator, 0);
给对象添加迭代器
Object.prototype[Symbol.iterator] = function () {
let self = this;
let index = 0;
const keys = [...Object.getOwnPropertyNames(self), ...Object.getOwnPropertySymbols(self)];
return {
next() {
return index < keys.length ? {
done: false,
value: self[keys[index++]]
} : {
done: true,
value: undefined
};
}
};
};
Promsie.all原理实现
Promise.all = function (promiseArr) {
return new Promise(function (resolve, reject) {
var index = 0,
ret = [];
// 循环遍历promise实例数组
for (var i = 0; i < promiseArr.length; i++) {
// 创建闭包,保存i
(function (i) {
// 取出每一个promise实例
var proItem = promiseArr[i];
// 如果元素不是promise对象,直接将对应数组位置赋值为当前值,index记录加一
if (!(proItem instanceof Promise)) {
index++;
ret[i] = proItem;
if (index === promiseArr.length) {
// 将最终的结果数组resolve
resolve(ret);
}
return;
}
// 当前proItem是promise实例,调用then方法获取resolve/reject处理结果
proItem.then(function (result) {
index++;
// 将获取的结果赋值给数组对应位置
ret[i] = result;
// 如果index记录等于promise实例数组长度,说明所有promise实例已经处理完毕
if (index === promiseArr.length) {
// 将最终的结果数组resolve
resolve(ret);
}
}).catch(function (err) {
// 只要有一个失败就整体失败
reject(err);
});
}(i));
}
});
};
Promise.race原理实现
Promise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promiseArr.length; i++) {
(function (i) {
var proItem = promiseArr[i];
if (!(proItem instanceof Promise)) {
resolve(proItem);
} else {
// 是promise实例
proItem.then(function (result) {
resolve(result);
}, function (err) {
reject(err);
});
}
}(i));
}
});
};
promise并发
const delay = function (interval) {
return new Promise(function (resolve, reject) {
if (interval === 1000) {
reject(new Error(interval));
}
setTimeout(function () {
resolve(interval);
}, interval);
});
};
const tasks = [
() => delay(1002),
() => delay(1004),
() => delay(100),
() => delay(1302),
() => delay(1092),
() => delay(1024)
];
// 创建请求池 tasks为任务列表,每个元素是一个函数,函数执行返回promise实例,pool是并发数量
function createRequestPool(tasks, pool = 5) {
// 创建并发请求数组
let togetherPool = new Array(pool).fill(0);
// 记录索引,保持result位置和任务位置对应
let index = 0;
// 结果数组
let result = [];
// 返回pool个promise并发执行
togetherPool = togetherPool.map(() => {
return new Promise(function (resolve, reject) {
const run = function () {
// 如果没有请求了,执行resolve
if (index >= tasks.length) {
resolve();
return;
}
// 保留索引,用于result结果保持相对位置
let old_index = index;
// 获取当前索引的task函数
const task = tasks[index++];
task().then(function (res) {
// 将任务结果放入result并运行下一个任务
result[old_index] = res;
run();
}).catch(function (err) {
// 发生异常,直接reject
reject(err);
});
};
// 直接执行run函数
run();
});
});
// 得到结果返回
return Promise.all(togetherPool).then(() => result);
}
// 测试用例
let res = createRequestPool(tasks, 2).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
编程题
列表和树形结构的互相转化
const list = [{
id: 1,
text: "节点一",
parentId: 0
}, {
id: 2,
text: "节点二",
parentId: 1
}];
const tree = [{
id: 1,
text: "节点一",
parentId: 0,
children: [{
id: 2,
text: "节点二",
parentId: 1
}]
}];
function listToTree(list) {
const temp = {};
const ret = [];
for (let i = 0; i < list.length; i++) {
temp[list[i].id] = list[i];
}
for (const i in temp) {
// 不是根节点
if (temp[i].parentId !== 0) {
// 将当前节点添加到父节点的children数组中
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = [];
}
temp[temp[i].parentId].children.push(temp[i]);
} else {
// 是根节点
ret.push(temp[i]);
}
}
return ret;
}
function treeToList(tree) {
let res = [];
const dfs = (tree) => {
tree.forEach(item => {
if (item.children) {
dfs(item.children);
delete item.children;
}
res.push(item);
});
};
dfs(tree);
return res;
}
console.log(JSON.stringify(listToTree(list)));
console.log(treeToList(tree));
大数相加
function add(a, b) {
const maxLen = Math.max(a.length, b.length);
a = a.padStart(maxLen, 0);
b = b.padStart(maxLen, 0);
let sum = "";
let carry = 0;
for (let i = maxLen - 1; i >= 0; i--) {
let curSum = parseInt(a[i]) + parseInt(b[i]) + carry;
carry = Math.floor(curSum / 10);
sum = curSum % 10 + sum;
}
if (carry === 1) {
sum = carry + sum;
}
return sum;
}
console.log(add("34323532432423410", "9432545234324324320"));
斐波那契数列
1 1 2 3 5 8 13 21
- 递归
function fibonacci(n) {
if (n < 1) throw ("不能小于1");
if (n === 1 || n === 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
- 循环
function fibonacci1(n) {
if (n < 0) throw new Error("最小为0!");
if (n === 0) return 0;
if (n <= 2) return 1;
let first = 1;
let second = 1;
let i = 3;
while (i++ <= n) {
// 保存第一个标量
let temp = first;
first = second;
second += temp;
}
return second;
}
连加操作
function add(...args) {
let allArgs = args;
function fn(...newArgs) {
allArgs = [...allArgs, ...newArgs];
return fn;
}
fn.toString = function () {
if (!allArgs.length) return;
return allArgs.reduce((pre, cur) => pre + cur);
};
return fn;
}
console.log((add(3)(5)(8)).toString());
实现动态定时器
// 第 1 题:写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b,...,a+nb 的时间,然后写一个 myClear,停止上面的 mySetInterVal
function MySetInterVal(fn, a, b) {
this.a = a;
this.b = b;
this.n = 0;
this.timer = null;
this.start = () => {
this.timer = setTimeout(() => {
fn();
this.n++;
this.start();
}, this.a + this.n * this.b);
};
this.myClear = () => {
clearTimeout(this.timer);
this.n = 0;
this.timer = null;
};
}
const instance = new MySetInterVal(() => {
console.log("你好");
}, 1000, 2000);
instance.start();
多维数组扁平化处理
function flatten(nums) {
let result = [];
for (let i = 0; i < nums.length; i++) {
if (Array.isArray(nums[i])) {
result = result.concat(flatten(nums[i]));
} else {
result = result.concat(nums[i]);
}
}
return result;
}