高阶函数实现AOP(面向切面编程)
Function.prototype.before = function (beforefn) {
let _self = this; // 缓存原函数的引用
returnfunction () {
// 代理函数
beforefn.apply(this, arguments); // 执行前置函数
return _self.apply(this, arguments); // 执行原函数
}
}
Function.prototype.after = function (afterfn) {
let _self = this;
returnfunction () {
letset = _self.apply(this, arguments);
afterfn.apply(this, arguments);
returnset;
}
}
let func = () => console.log('func');
func = func.before(() => {
console.log('===before===');
}).after(() => {
console.log('===after===');
});
func();
输出结果:
===before===
func
===after===
当我们 new 一个类的时候 都发生了什么
/** * new2 new关键字的代码实现演示 * @param {function} func 被new的类 (构造函数) */
function new2(func) {
// 创建了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象
let o = Object.create(func.prototype);
// (在构造函数中this指向当前实例)让这个类作为普通函数值行 并且里面this为实例对象
let k = func.call(o);
// 最后再将实例对象返回 如果你在类中显示指定返回值k,
// 注意如果返回的是引用类型则将默认返回的实例对象o替代掉
return typeof k === 'object' ? k : o;
}
// 实验
functionM() {
// 即将被new的类
this.name = 'liwenli';
}
let m = new2(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);
Object.create 兼容实现
let obj1 = {
id: 1};
Object._create = (o) => {
let Fn = function() {
}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}
let obj2 = Object._create(obj1);
console.log(obj2.__proto__ === obj1); // true
console.log(obj2.id); // 1
// 原生的Object.create
let obj3 = Object.create(obj1);
console.log(obj3.__proto__ === obj1); // true
console.log(obj3.id); // 1
currying 函数柯理化
curry 柯理化的实现(递归调用 + valueOf)
知识点:valueOf 浏览器环境下 当我们以log(fn)这种形式取值时,会隐式调用fn自身的valueOf 所以得到的是valueOf的返回值
functionfn() {
};
fn.valueOf = () => console.log('valueof');
console.log(fn); // valueof
const mul = x => {
const result = y => mul(x * y); // 递归调用mul
result.valueOf = () => x;
return result;
}
console.log(mul(2)(3)); // 6
// 在上面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在里面调用mul(x * y)
// 在result函数的valueOf里保存着 由上一次x * y 传进来的结果x, 也就是上一次x*y 会作为这一次的输出 依然叫x
// 第一次mul(2) 此时 x为2 return result
result 为 result = y => mul(2 * y);
// 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的结果作为mul参数
// 最后mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6
// log(mul(2)(3)) 相当于log的最后返回的result 隐式调用valueOf 返回 6
curry 将多参数函数转换为接收单一参数的函数
function fe(a, b, c) {
return a + b + c;
}
function curry(fe) {
let args = []; // 参数集合
let len = args.length;
returnfunctionbar() {
args = [...args, ...arguments]; // 收集参数
if (args.length >= fe.length) {
return fe.apply(this, args);
}
return bar;
}
}
console.log(curry(fe)(1)(2)(3)); // 6
currying 部分求值
// currying 函数柯理化
let currying = function(fn) {
let args = [];
returnfunctionfe() {
if (arguments.length === 0) {
return fn.apply(this, args);
}
[].push.apply(args, arguments);
return fe;
}
}
let count = currying(function (...rest) {
return rest.reduce((prev, cur) => prev + cur, 0);
});
console.log(count(100)(200)(10)()); // 310
收集参数 延迟执行 到达指定次数才执行
// 参数收集 指定次数后执行
function fn(...rest) {
console.log(rest);};
function after(fn, time = 1) {
let params = [];
returnfunction(...rest) {
params = [...params, ...rest];
if (--time === 0) {
fn.apply(this, params);
}
}
}
let newFn = after(fn, 3); // 执行3次 内部fn才会执行
newFn(2);
newFn(3);
newFn(4);
函数节流
throttle 策略的电梯。保证如果电梯第一个人进来后,50毫秒后准时运送一次,不等待。如果没有人,则待机。
let throttle = (fn, delay = 50) => {
// 节流 控制执行间隔时间 防止频繁触发 scroll resize mousemove
let stattime = 0;
returnfunction (...args) {
let curTime = new Date();
if (curTime - stattime >= delay) {
fn.apply(this, args);
stattime = curTime;
}
}
}
防抖动
debounce 策略的电梯。如果电梯里有人进来,等待50毫秒。如果又人进来,50毫秒等待重新计时,直到50毫秒超时,开始运送。参考 前端手写面试题详细解答
let debounce = (fn, time = 50) => {
// 防抖动 控制空闲时间 用户输入频繁
let timer;
returnfunction (...args) {
let that = this;
clearTimeout(timer);
timer = setTimeout(fn.bind(that, ...args), time);
}
}
深度拷贝兼容写法(不包括原型属性)
function deepCopy(obj) {
if (typeof obj !== 'object') return obj;
if (typeof window !== 'undefined' && window.JSON) {
// 浏览器环境下 并支持window.JSON 则使用 JSON
return JSON.parse(JSON.stringify(obj))