防抖节流

// 防抖函数 n秒内只触发一次
function debounce(fn: Object, wait: number) {
let timer;
return function () {
let _this = this
let args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(()=> {
fn.apply(_this, args)
}, wait)
}
}

// use
window.onresize = debounce(function () {
console.log("resize")
}, 500)

// 节流函数

function thorttle (fn: Object, wait: number) {
let timer
return function () {
let _this = this
let args = arguments
if (!timer) {
timer = setTimeout(()=> {
timer = null;
fn.apply(_this, args)
}, wait)
}
}
}
// use
window.onresize = thorttle(function () {
console.log('s')
},500)

手写Promise

// 1、基本架构:
// 状态
// then
// 执行器函数 executor

// 2、executor、resolve、reject
// 3、then 同步下调用
// 4、then 异步下调用
// 5、then 链式调用
// 返回 Promise
// then 函数递归返回常量结果,供下个 then 使用
// 考虑 then 成功的回调为 null 的情况

class Promise {
static PENDING = "pending";
static RESOLVED = "resolved";
static REJECTED = "rejected";

static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}

static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}

constructor(executor) {
this.state = Promise.PENDING;
this.value = undefined;
this.reason = undefined;

this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];

const resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject);
}

if (this.state === Promise.PENDING) {
this.state = Promise.RESOLVED;
this.value = value;

this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
};

try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}

then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};

let promise = new Promise((resolve, reject) => {
if (this.state === Promise.PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}

if (this.state === Promise.RESOLVED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}

if (this.state === Promise.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
});

return promise;
}

catch(onRejected) {
return this.then(null, onRejected);
}

all(arr) {
let count = 0;
let result = [];

return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i])
.then((res) => {
result[i] = res;
if (++count === arr.length) {
resolve(res);
}
})
.catch((error) => {
reject(error);
});
}
});
}

race(arr) {
return new Promise((resolve, reject) => {
arr.forEach((item) => Promise.resolve(item).then(resolve, reject));
});
}

finally(callback) {
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => value);
},
(reason) => {
return Promise.resolve(callback()).then(() => {
throw reason;
});
}
);
}

allSettled(arr) {
let count = 0;
let result = [];

return new Promise((resolve, reject) => {
const fn = (i, data) => {
if (count === arr.length) {
resolve(result);
}

result[i] = data;
count++;
};

for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i])
.then((res) => {
fn(i, { status: "fulfilled", value: res });
})
.catch((error) => {
fn(i, { status: "rejected", reason: error });
});
}
});
}

// from Node Util.promisify
promisify(f) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
}

args.push(callback);

f.call(this, ...args);
});
};
}

// from Node Util.promisifyAll
promisifyAll(obj) {
for (let key in obj) {
if (typeof obj[key] === "function") {
obj[key] = this.promisify(obj[key]);
}
}
}
}

function resolvePromise(promise, x, resolve, reject) {
// let promise = new Promise((resolve) => {
// resolve(1);
// }).then((res) => {
// return promise;
// });

if (x === promise) {
throw TypeError("循环引用");
}

if ((typeof x === "object" && x !== null) || typeof x === "function") {
let called;

try {
let then = x.then;

if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// x: { then: {} }
if (called) return;
called = true;
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 返回了常量,直接 resolve
resolve(x);
}
}

const p = new Promise((resolve, reject) => {
reject(1);
});

p.catch((error) => {
console.log("error + ", error);
return error;
}).then((res) => {
console.log(res);
});

Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};

module.exports = Promise;

实现一个js bind()

Function.prototype.fakeBind = function (obj, ...args) {
return (...rest) => this.call(obj, ...args, ...rest);
};

实现一个loadsh.get()

function getV(context, path, defaultValue) {
if (Object.prototype.toString.call(context) !=="[object object]" && Object.prototype.toString.call(context) !== "[object Array]") {
return context
}
let paths = []
if (Array.isArray(path)) {
paths = [...path]
} else if (Object.prototype.toString.call(path) === "[object String]") {
paths = path.replace(/\[/g, ".").replace(/\]/g, "").split(".").filter(Boolean)
} else {
paths = [String(path)]
}
let result = undefined
for (let i = 0; i<paths.length; i++) {
const key = paths[i]
result = result ? result[key] : context[key]
if (result !== null && typeof result !== "undefined") {
continue
}
return defaultValue || undefined
}
return result
}

实现一个浅克隆

1.Object.assign()

  • 它不会拷贝对象的继承属性;
  • 它不会拷贝对象的不可枚举的属性;
  • 可以拷贝 Symbol 类型的属性。
let target = {};
let source = { a: { b: 1 } };
Object.assign(target, source);
console.log(target); // { a: { b: 1 } };

2.扩展运算符

/* 对象的拷贝 */
let obj = {a:1,b:{c:1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj) //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2
console.log(obj) //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
/* 数组的拷贝 */
let arr = [1, 2, 3];
let newArr = [...arr]; //跟arr.slice()是一样的效果

3.concat拷贝数组

let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]

4.slice拷贝数组

let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]

5.手动浅拷贝

const shallowClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {}
}
for (let prop in target) { // 循环遍历对象属性复制给克隆对象
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop]
}
}
return cloneTarget
else {
return target
}
}

实现深拷贝

1.JSON.stringify()

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数
  • 无法拷贝不可枚举的属性
  • 无法拷贝对象的原型链
  • 拷贝 RegExp 引用类型会变成空对象
  • 拷贝 Date 引用类型会变成字符串
  • 对象中含有 NaNInfinity 以及 -InfinityJSON 序列化的结果会变成 null
  • 不能解决循环引用的对象,即对象成环 (obj[key] = obj)。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
  1. 手写递归实现深拷贝(基础版)
    • 这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
    • 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
    • 对象的属性里面成环,即循环引用没有解决
let obj1 = {
a: { b: 1},
c:[2, 3]
}
function deepClone(obj) {
let cloneObj = {}
for(let key in obj) {
if (typeof obj[key] === 'object') { // 对象时
cloneObj[key] = deepClone(obj[key]) // 递归
} else {
cloneObj[key] = obj[key] // 基本数据类型
}
}
return cloneObj
}
let obj2 = deepClone(obj1)
obj1.a.b = 2;
console.log(obj2)

3.增强版递归实现

function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj // 普通数据 直接返回
if (hash.get(obj)) return hash.get(obj) // 处理循环引用属性
let cloneObj = new obj.constructor() // 找到obj的原型属性,Array || Object
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 递归拷贝
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
/**
* 深拷贝关注点:
* 1. JavaScript内置对象的复制: Set、Map、Date、Regex等
* 2. 循环引用问题
*/
function deepClone(source, memory) {
const isPrimitive = (v) => {
return /Number|Boolean|String|Null|Undefined|Symbol|Function/.test(
Object.prototype.toString.call(v))
}
let result = null
memory || (memory = new WeakMap())
// 处理原始数据类型和函数 函数返回的是一个新的内存地址
if (isPrimitive(source)) {
result = source
}
// 处理数组
else if (Array.isArray(source)) {
result = source.map((v)=> deepClone(v, memory))
}
// 处理Date、Regex、Set、Map
else if (source instanceof Date) {
result = new Date(source)
} else if (source instanceof RegExp) {
result = new RegExp(source)
} else if (source instanceof Set) {
result = new Set()
for (const f of source) {
result.add(deepClone(f, memory))
}
} else if (source instanceof Map) {
result = new Map()
for (const [k, v] of source.entries()) {
result.set(k, deepClone(v, memory))
}
} else {
// 处理引用类型
if (memory.has(source)) {
result = memory.get(source)
} else {
result = Object.create(null)
memory.set(source, result)
Object.keys(source).forEach((key) => {
const value = source[key]
result[key] = deepClone(value, memory)
})
}
}
return result
}
实现一个函数解析URL
function parse(url) {
// 一、夹杂在 ? 与 # 之前的字符就是 qs,使用 /\?([^/?#:]+)#?/ 正则来抽取
// 使用正则从 URL 中解析出 querystring
// 二、通过 Optional Chain 来避免空值错误
const queryString = url.match(/\?([^/?#:]+)#?/)?.[1];

if (!queryString) {
return {};
}
queryObj = queryString.split("&").reduce((params, block) => {
// 三、如果未赋值,则默认为空字符串
const [_k, _v = ""] = block.split("=");
// 四、通过 decodeURIComponent 来转义字符,切记不可出现在最开头,以防 ?tag=test&title=1%2B1%3D2 出错
const k = decodeURIComponent(_k);
const v = decodeURIComponent(_v);

if (params[k] !== undefined) {
// 处理 key 出现多次的情况,设置为数组
params[k] = [].concat(params[k], v);
} else {
params[k] = v;
}
return params;
}, {});
return queryObj;
}