2021面试手写代码

169 阅读5分钟

深拷贝

递归

function deepClone(obj) {
  let target = Array.isArray(obj) ? [] : {};
  for (i in obj) {
  target[i] = typeof obj[i] === "object"deepClone(obj[i]):obj[i]
  }
  return target;
}

其他方式

JSON.parse(JSON.stringify(obj));
//对象中包含 function 或 RegExp 这些就不能用这种方法了
Object.assign({}, obj);
//对象一级属性是深拷贝,二级属性以后为浅拷贝
$.extend(true, {}, obj1);
//jQuery的extend方法实现深拷贝

数组去重

indexOf

function uniqueByIndexOf(arr) {
  let newArr = [];
  for (let i in arr) {
    if (newArr.indexOf(arr[i] == -1)) {
      newArr.push(arr[i]);
    }
  }
  return newArr;
}

对象属性

function uniqueByObjProp(arr) {
  let obj = {},
    newArr = [];
  for (i in arr) {
    if (!obj[i]) {
      obj[i] = true;
      newArr = obj[i];
    }
  }
}

filter

function uniqueByfilter(arr) {
  let newArr = [];
  return arr.fitler((item) => {
    !newArr.includes(item) && newArr.push(arr[i]);
  });
}

reduce

function uniqueByReduce(arr) {
  return arr.reduce((pre, next) => {
    if (!pre.includes(next)) {
      return pre.concat(next);
    } else {
      return pre;
    }
  }, []);
}

Es6


Array.from(new Set(arr))

[...new Set(arr)]

对象数组去重

function uniqueByObjKey(arr) {
  let obj = {};
  return arr.reduce((pre, next) => {
    obj[next.id] ? "" : (obj[next.id] = true && pre.push(next));
  }, []);
}

数组平铺

ES6

arr.flat();

递归

function flat(arr) {
  let newArr = [];
  for (i in arr) {
    if (Array.isArray(i)) {
      newArr = newArr.concat(flat(i));
    } else {
      newArr.push(arr[i]);
    }
  }
  return newArr;
}

其他方式

arr.join(",").split(",");

节流、防抖

节流函数

应用场景:

  • 滚动事件
  • 防止重复提交
function throttle(handler, wait) {
  let preTime = 0;
  return function () {
    let now = Date.now();
    if (now - preTime >= wait) {
      handler.call(this);
      preTime = now;
    }
  };
}

防抖函数

应用场景:baidu搜索

function debounce(handler, wait) {
  let timer = null;
  return function () {
  let _this = this;
    if(timer){
        clearTimeout(timer);
    }
    timer = setTimeout(
        handler.call(_this),wait
    )
  };
}

call、apply、bind实现

call

思路: 将目标函数的this指向传入的第一个对象(把目标函数作为对象的方法执行,this就指向了目标对象),参数为不定长,且立即执行

  • 改变this指向:可以将目标函数作为这个对象的属性
  • 执行函数,注意arguments参数
  • 删除为了改变this的函数
Function.prototype.call = function(context){
  let ctx = Object(context)||windows
  //给目标函数起一个函数名,作为传入对象的方法
  let fn = 'fn'
  while(ctx.hasOwnProperty(fn)){
  fn = fn+Math.random()
  }
  //把目标函数(this)作为传入对象的方法
  ctx[fn] = this
  //执行函数,第一个参数是目标对象,后面的是执行传入的参数
  let res = ''
  if(arguments.length>1){
      res=ctx[fn](...[...arguments].slice(1))
  }else{
  	res= ctx[fn]()
  }
  //删除目标对象添加的属性
  delete ctx[fn];
  return res
}

apply

原理同call,只是传参方式不同

Function.prototype.apply = function(context,pa){
  let ctx = Object(context)||windows
  //给目标函数起一个函数名,作为传入对象的方法
  let fn = 'fn'
  while(ctx.hasOwnProperty(fn)){
  fn = fn+Math.random()
  }
  //把目标函数(this)作为传入对象的方法
  ctx[fn] = this
  //执行函数,第一个参数是目标对象,后面的是执行传入的参数
  let res = ''
  if(arguments.length>1){
     res=  ctx[fn](...pa)
  }else{
  	res= ctx[fn]()
  }
  //删除目标对象添加的属性
  delete ctx[fn];
  return res
}

bind

改变this指向,但是不立即执行,借用apply返回一个方法

Function.prototype.bind = function(context){
 context = context ? Object(context) : window;
 let _this = this
 //保存argumets
  let mArguments = arguments
 if(arguments.length>1){
 return function (){
  _this.apply(context,[...mArguments.slice(1)])
  }
 }else{
 return function (){
  _this.apply(context,)
  }
 } 
}

object.create

Object.create = function (obj) {
  //创建空构造函数F
  function F() {}
  //F的prottotype指向obj
  F.prtotype = obj;
  //返回F实例
  return new F();
};

new操作符干了什么,实现new

在JavaScript构造函数中:如果return值类型,那么对构造函数没有影响,实例化对象返回空对象;如果return引用类型(数组,函数,对象),那么实例化对象就会返回该引用类型;

function new() {
  //创建空对象
  let obj = {};
  //获取构造函数
  const [Constructor,...args] = [...arguments]
  //绑定原型关系
  obj.__proto__ = Constructor.prototype;
  // 执行构造函数,即绑定 this,并且为这个新对象添加属性
  let result = Constructor.apply(newObj, args);
  if (result && (typeof result === 'object' || typeof result === 'function')) {
    return result
  }
  //返回新对象
  return obj;
}

继承

ES5


function Parent (name, age) {
  this.name = name
  this.age = age
}
Parent.ptototype.sayName = function () {
  console.log(this.name)
}
function Child (city) {
  Parent.call(this)
  this.city = city
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

ES6


class App extends React.Component {
  constructor (props) {
    super(props)
  }
}
export default App

Promise

promise

//判断变量是否是函数
function isFunction(variable) {
  return typeof variable === "function";
}
 
//promise构造函数
function MyPromise(handle) {
  if (!isFunction(handle)) {
    throw new Error("必须传入一个函数");
  }
  this.status = "pending"; //状态,默认等待状态
  this.value = ""; //值
  this.onResolveCallbacks = []; //存放成功回调的数组
  this.onRejectCallbacks = []; //存放失败回调的数组
  var that = this;
  try {
    handle(resolve, reject);
  } catch (err) {
    reject(err); //有报错,状态直接变为rejected
  }
 
  function resolve(val) {
    if (that.status == "pending") {
      that.status = "resolved";
      that.value = val;
      that.onResolveCallbacks.forEach((fn) => {
        fn();
      });
    }
  }
 
  function reject(err) {
    if (that.status == "pending") {
      that.status = "rejected";
      that.value = err;
      that.onRejectCallbacks.forEach((fn) => {
        fn();
      });
    }
  }
}

promise.then

MyPromise.prototype.then = function (sucFun, faildFun) {
  //可选参数
  //如果没有传,只负责把值往后传
  sucFun = isFunction(sucFun) ? sucFun : (value) => value;
  faildFun = isFunction(faildFun) ? faildFun : (value) => value;
  var that = this;
  //先改变了状态
  if (this.status === "resolved") {
    sucFun(this.value);
  }
  if (this.status === "rejected") {
    faildFun(this.value);
  }
  //先指定了回调函数,状态还没改变
  if (this.status === "pending") {
    //存住回调函数,在异步操作完成时调用
    this.onResolveCallbacks.push(() => {
      sucFun(this.value);
    });
    this.onRejectCallbacks.push(() => {
      faildFun(this.value);
    });
  }
 
  return this;
};

promise.all


Promise.allFun = (arr) => {
  return new Promise((resolve, reject) => {
    var resolveArr = [],
      rejectArr = [];
    for (var i = 0; i < arr.length; i++) {
        Promise.resolve(arr[i]).then((res) => {
          resolveArr.push(res);
          if (resolveArr.length == arr.length) {
            resolve(resolveArr);
          }
        })
        .catch((err) => {
          rejectArr.push(err);
          reject(rejectArr);
        });
    }
  });
};

promise.race

myPromise.race = function (arr) {
  return new Promise(function (resolve, reject) {
    for (var i in arr) {
      arr[i].then(resolve, reject);
    }
  });
};

异步并发控制

async function sendRequest(list, max = 4) {
  return new Promise((resolve, reject) => {
    let index = 0; //
    let count = 0; //上传成功个数
    const start = () => {
      while (max > 0 && index < list.length) {
        //剩余位置-1
        max--;
        let chunk = list[index];
        index++;
        request().then((res) => {
          max++; //释放暂用位置
          count++; //上传成功数量+1
          if (count == list.length) {
            resolve("完成了");
          } else {
            start();
          }
        });
      }
    };
 
    start();
  });
}
 
function request() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(1);
      resolve(1);
    }, 2000);
  });
}
 
var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1];
sendRequest(list, 4)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log("上传失败了");
  });

数组方法

push

从后面增加n个元素,返回长度

Array.prototype.push = function(){
 for(i in arguments){
 	this[this.length-1] = arguments[i]
 }
 return this.length
}

pop

从后面删除一个元素,返回删除项

Array.prototype.pop = function(){
  let item
  if(this.length>0){
  item = this[this.length-1]
  }
  this.length = this.length-1
  return item
}

shift

从前面删除一个,返回删除元素

Array.prototype.shift = function(){
  let item
  if(this.length>0){
   item = this[0]
   for(i in this){
    this[i] = this[i+1]
   }
   this.length = this.length-1
  }
  return item
}

unshift

从前面添加n个元素,返回数组长度

Array.prototype.unshift = function(){
let length = arguments.length
if(this.length>0){
//整体后移
 this[i+length] = this[i]
}
//赋值前面的空位
for(let i =0;i<length;i++){
 this[i] = arguments[i]
}

return this.length
}

filter

Array.prototype.filter = function(handler){
let newArr = []

for(i in this){
if(handler(i,this[i]),this){
newArr[newArr.length] = this[i]
}
return newArr
}

reduce

Array.prototype.myReduce = function (callback, init) {
  if (!init) init = 0;
  for (var i = this.length - 1; i >= 0; i--) {
    init = callback(init, this[i], i, this);
  }
  return init;
};

setTimeout模拟定时器

function mInterval() {
  //要执行的逻辑
  console.log(1);
  //每次逻辑执行完,才会向队列添加事件
  setTimeout(mInterval, 1000);
}
 
mInterval();