深克隆
对于数组和对象做不同的处理
为了解决循环引用的问题,缓存已经访问过的引用类型,如果已经存在,使用缓存值
Object.keys() 遍历的是对象自身的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function deepCopy (obj, cache = [] ) { if (obj === null || typeof obj !== "object" ) { return obj; } const hit = cache.filter((c ) => c.original === obj)[0 ]; if (hit) { return hit.copy; } const copy = Array .isArray(obj) ? [] : {}; cache.push({ original : obj, copy, }); Object .keys(obj).forEach((key ) => { copy[key] = deepCopy(obj[key], cache); }); return copy; } let a = { a : 10 , }; a.self = a; let b = deepCopy(a);console .log(b);
bind
异常处理 如果调用者不是函数,抛出异常
如果是构造调用,则返回构造调用的结果
否则,使用 apply 改变函数绑定的 this
1 2 3 4 5 6 7 8 9 10 11 12 13 Function .prototype.mybind = function (context ) { if (typeof this !== "function" ) { throw new Error ("error" ); } let _this = this ; let arg = [...arguments].slice(1 ); return function F ( ) { if (this instanceof F) { return new _this(...arg, ...arguments); } return _this.apply(context, arg.concat(...arguments)); }; };
call/apply
异常处理 如果调用者不是函数,抛出异常
在 context 上添加 fn 为当前函数,进行调用得到结果,之后需要删除 context 上的 fn
返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Function .prototype.mycall = function (context ) { if (typeof this !== "function" ) { throw new TypeError ("error" ); } context = context || window ; context.fn = this ; let result; let args = [...arguments].slice(1 ); result = context.fn(...args); delete context.fn; return result; }; Function .prototype.myapply = function (context ) { if (typeof this !== "function" ) { throw new TypeError ("error" ); } context = context || window ; context.fn = this ; let result; if (arguments [1 ]) { result = context.fn(...arguments[1 ]); } else { result = context.fn(); } delete context.fn; return result; }; console .log(Array .prototype.concat.mycall([], [1 ], [2 ]));console .log(Array .prototype.concat.myapply([], [[1 ], [2 ]]));
防抖函数
引入 immediate ,immediate 为 true,需要在 timer = null 时执行函数,而 immediate 为 false 时,需要在延时回调函数中执行函数,因此还需要在闭包中保存 context 和 传参信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function debounce(fn, wait = 50, immediate = true) { let timer, context, args; const later = () => setTimeout(() => { timer = null; if (!immediate) { fn.apply(context, args); context = args = null; } }, wait); return function (...params) { if (!timer) { timer = later(); if (immediate) { fn.apply(this, params); } else { context = this; args = params; } } else { clearTimeout(timer); timer = later(); } }; } const dfn = debounce((arg) => console.log(arg), 500, true); dfn(1); dfn(2); dfn(3); setTimeout(() => { dfn("aa"); }, 1000);
节流函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function throttle (fn, gutter ) { let prev = null ; return function (...params ) { let now = Date .now(); if (now - prev > gutter) { fn.apply(this , params); prev = now; } }; } const tfn = throttle((arg ) => console .log(arg), 5000 );setInterval (() => { tfn("a" ); }, 10 );
数组扁平化
1 2 3 4 const flattenDeep = (arr ) => Array .isArray(arr) ? arr.reduce((prev, cur ) => [...prev, ...flattenDeep(cur)], []) : [arr];
实现 new
创建一个对象,其 __proto__
指向构造函数的原型
调用该函数,函数的 this 绑定上一步创建的对象
只要函数的返回值不为对象,返回之前创建的对象,否则返回函数返回的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function A ( ) { this .a = 10 ; } function New (func ) { let res = {}; if (func.prototype !== null ) { res.__proto__ = func.prototype; } let ret = func.apply(res, [...arguments].slice(1 )); if ((typeof ret === "object" || typeof ret === "function" ) && ret !== null ) { return ret; } return res; } console .log(New(A));
instanceOf
1 2 3 4 5 6 7 8 9 10 11 12 13 function instanceOf (left, right ) { let proto = left.__proto__; let prototype = right.prototype; while (true ) { if (proto === null ) { return false ; } if (proto === prototype) { return true ; } proto = proto.__proto__; } }
柯里化 柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?
柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。
接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行 fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let sum = (a, b, c, d ) => a + b + c + d;let curry = (fn, ...arr ) => { let len = fn.length; return (...args ) => { const combineArgs = [...arr, ...args]; if (len <= combineArgs.length) { return fn(...combineArgs); } else { return curry(fn, ...combineArgs); } }; }; let cfn = curry(sum);console .log(cfn(1 )(2 )(3 )(4 ));
eventbus 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class EventBus { all = new Map (); on (type, handler ) { const handlers = this .all.get(type); const added = handlers && handlers.push(handler); if (!added) { this .all.set(type, [handler]); } } off (type, handler ) { const handlers = this .all.get(type); if (handlers) { handlers.splice(handlers.indexOf(handler) >>> 0 , 1 ); } } emit (type, evt ) { (this .all.get(type) || []).slice().map((handler ) => { handler(evt); }); } }