手撕代码

425 阅读2分钟

call、bind、apply的区别

  • call、bind、apply都是函数原型上的方法,作用都是改变函数调用的this指向
  • call和apply传的参数类型不同,apply接收的是个参数数组,call可以接收多个参数,call的执行效率更高
  • call和apply调用后会立即执行,bind会绑定函数的this指向,不会立即执行,只对第一次绑定的生效

Apply

  • call和apply的实现原理类似,
    • 给传入的context对象上添加fn属性,指向当前调用的方法this
    • 调用当前方法,并传入参数
    • 删除context对象上添加的fn属性,返回执行结果
Function.prototype.apply = function (context = window, args) {
    context.fn = this;
    const result = context.fn(...args);
    delete context.fn
    return result;
}

Call

Function.prototype.call = function (context = window, ...args) {
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn
  return result;
}

bind

Function.prototype.bind = function(target, ...args){
    
    return (...args2) => {
        this.call(target,...args,...args2)
    }
}

deepClone

  • 遍历对象的属性,如果属性值是基本数据类型或null,则直接返回
  • 如果属性值是引用数据类型,则递归调用
function clone(obj){
    if(obj === null || typeof obj !== 'object') return
    const newObj = new obj.constructor();
    for(let key in Object.getOwnPropertyDescriptors(obj)){
        newObj[key] = clone(obj[key]);
    }
    return newObj;
}

深度比较两个对象

/**
 * 深度比较两个对象
 * @param {*} a, b 要比较的两个对象
 */
function deepCompare(a, b) {
  if ([a, b].includes(null) || [typeof a, typeof b].includes("object"))
    return a === b;
  const propsA = Object.getOwnPropertyDescriptors(a);
  const propsB = Object.getOwnPropertyDescriptors(b);
  if (Object.keys(propsA).length !== Object.keys(propsB).length) return false;

  return Object.keys(propsA).every((key) => deepCompare(a[key], b[key]));
}

函数柯理化

  • 判断函数参数的个数,如果达到,就返回执行结果,否则继续返回curry函数
function curry(fn, ...args){
    return args.length < fn.length ? 
    	(...extraArgs) => curry(fn, ...args, ...extraArgs)
    	: fn(...args);
}

function addFn(a, b, c, d, e){
    return a + b + c + d + e;
}

const add = curry(addFn);

console.log(add(1, 2, 3, 4, 5))
console.log(add(1, 2)(3, 4)(5))
console.log(add(1)(2)(3)(4)(5))

深度优先遍历

  • 递归实现
/**
 * 数据结构
 *        0
 *    1   2    3
 *   / \  |    |
 *  4   5 6    7
 * 深度优先: 0, 1, 4, 5, 2, 6, 3, 7
 * 广度优先: 0, 1, 2, 3, 4, 5, 6, 7
 */
const rootNode = {
  id: 0,
  children: [
    {
      children: [
        { id: 4, title: "node" },
        { id: 5, title: "node" },
      ],
      id: 1,
      title: "node",
    },
    {
      children: [{ id: 6, title: "node" }],
      id: 2,
      title: "node",
    },
    {
      children: [{ id: 7, title: "node" }],
      id: 3,
      title: "node",
    },
  ],
};

/**
 * 深度优先
 * @param node
 * @param ids
 */
function deepSearch(node: any, ids: number[] = []) {
  ids.push(node.id);
  if (node.children && node.children.length) {
    node.children.forEach((n) => {
      deepSearch(n, ids);
    });
  }
}

const deepRes = [];
deepSearch(rootNode, deepRes);
console.log("深度优先", deepRes);

广度优先遍历

  • 栈型结构实现
/**
 * 广度优先
 * @param root
 */
function wideSearch(root: any) {
  const ids: number[] = [];
  const stack = [root];

  while (stack.length) {
    const node = stack.shift();
    ids.push(node.id);
    if (node.children && node.children.length) {
      stack.push(...node.children);
    }
  }
  return ids;
}

console.log("广度优先", wideSearch(rootNode));

构造斐波那契数列

function getFbnq(length: number) {
  let a = 1;
  let b = 2;
  let arr = [a, b];

  while (arr.length < length) {
    const temp = a;
    a = b;
    b = temp + b;
    arr.push(b);
  }
  return arr;
}

console.log(getFbnq(10));

未完待续

防抖、节流、手写promise、koa原理...