JavaScript手撕代码合集

总结一下面试常考的手撕代码

Posted by BY Bamboo on February 18, 2020

深拷贝

JSON.parse(JSON.stringify(object)) 有局限性,会忽略 undefined 和 symbol

function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}

function deepCopy(obj) {
  let result;
  if (isArray(obj)) {
    result = [];
  } else {
    result = {};
  }
  for (let attr in obj) {
    if (typeof obj[attr] === "object") {
      result[attr] = arguments.callee(obj[attr]);
    } else {
      result[attr] = obj[attr];
    }
  }
  return result;
}

new

function create(func, args) {
  let obj = {}; //1.创建一个新对象;
  obj.__proto__ = func.prototype; //2.设置新对象的_proto_属性指向构造函数的prototype
  let result = func.call(obj, ...args); //3.让构造函数的this指向新对象,并执行构造函数(为新对象添加属性)
  return result instanceof Object ? result : obj; 4.//确保返回值为对象
}

instanceof

通过判断对象的原型链中是不是能找到类型的 prototype

function myInstanceof(left, right) {
  let prototype = right.prototype;
  left = left.__proto__;
  while (true) {
    if (left === null || left === undefined)
      //直到对象原型为 null,因为原型链最终为 null
      return false;
    if (prototype === left) return true;
    left = left.__proto__;
  }
}

防抖

function antiShake(fn, delay) {
  var timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn();
    }, delay);
  };
}

节流

function throttle(fn, delay) {
  var flag = true;
  return function() {
    if (!flag) {
      return;
    }
    flag = false;
    setTimeout(function() {
      fn();
      flag = true;
    }, delay);
  };
}

bind

Function.prototype.myBind = function(context, ...arr1) {
  if (typeof this !== "function") {
    throw new TypeError("Bind must be called on a function");
  }
  let This = this;
  context = context || window;

  return function(...arr2) {
    let arr = arr1.concat(arr2);
    // 因为返回了一个函数,我们可以 new,new 的优先级高于 bind
    if (this instanceof This) {
      return new This(...arr);
    }
    This.call(context, ...arr);
  };
};

function foo(n1, n2, n3, n4) {
  console.log(this);
  console.log(n1, n2, n3, n4);
}

foo.myBind(document, 3)(4, 5, 6);

call

Function.prototype.call = function(context, ...args) {
  context = context || window;
  context.func = this;

  let res = context.func(...args); //getValue.call(a, 'yck', '24') => a.fn('yck', '24')
  delete context.func;
  return res;
};

apply

Function.prototype.apply = function(context, args) {
  context = context || window;
  context.func = this;

  let res = context.func(...args);
  delete context.func;
  return res;
};

jsonP

如果服务端收到请求,返回的是 bar({message:’hello’}),这样的话在服务端就可以把数据通过函数执行传参的方式实现数据传递

function jsonP(url, cbName, cb) {
  let script = document.createElement("script");
  script.src = url;
  window[cbName] = cb;
  document.body.appendChild(script);
}
jsonP("http://www.b.com/20190902/jsonp.php?callback=bar", "bar", function(data) {
  console.log(data);
});

简易版 promise

class MyPromise {
  constructor(fn) {
    this.resolveArr = [];
    this.rejectArr = [];
    this.state = "pending";

    let resolveFn = () => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        setTimeout(() => {
          for (let i = 0; i < this.resolveArr.length; i++) {
            this.resolveArr[i]();
          }
        }, 0);
      }
    };
    let rejectFn = () => {
      if (this.state === "pending") {
        this.state = "rejected";
        setTimeout(() => {
          for (let i = 0; i < this.rejectArr.length; i++) {
            this.rejectArr[i]();
          }
        }, 0);
      }
    };
    fn(resolveFn, rejectFn);
  }

  then(cb) {
    if (this.state === "pending") {
      this.resolveArr.push(cb);
      return this;
    }
  }

  catch(cb) {
    if (this.state === "pending") {
      this.rejectArr.push(cb);
      return this;
    }
  }
}

function foo() {
  return new MyPromise(function(resolve, reject) {
    setTimeout(() => {
      resolve();
      // reject();
    }, 1000);
  });
}

foo()
  .then(function() {
    console.log(1);
  })
  .catch(function() {
    console.log(2);
  })
  .then(function() {
    console.log(3);
  });