前端手写题(部分)

92 阅读4分钟

前言

对于很多方法,经常需要我们可以自己手写出来,自己总结了一些手写题,欢迎点评

ajax

 //让数据变成对象的格式
 function reolveData (data) {
   let arr = []
   for (const k in data) {
     arr.push(k + '=' + data[k])
   }
   return arr.join('&')
 }
 function myAjax (options) {
   const xhr = new XMLHttpRequest()
   const qs = reolveData(options.data)
   
   //判断请求类型
   if (options.methods.toUpperCase() === "GET") {
     xhr.open(options.methods, options.url + '?' + qs)
     xhr.send()
   } else if (xhr.methods.toUpperCase() === "POST") {
     xhr.open(options.methods, options.url)
     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
     xhr.send(qs)
   }
     
   xhr.onreadystatechange = function() {
     if (xhr.readystatus === 4 && xhr.status === 200) {
       const result = JSON.parse(xhr.responseText)
       option.success(result)
     }
   }
 }

防抖

  • 指的是在一段时间内,时间只会触发最后一次,类似于打游戏的回城
 function deounce (fn, delay = 300) {
   let time = null
   return function() {
     let _this = this;
     clearTimeout(time)
     time = setTimeout(() => {
       fn.apply(_this, arguments)
     }, delay)
   }
 }

节流

  • 一般用于时间,按照一段时间的间隔了进行触发,鼠标移动,滚动条滚动很频繁触发的
 function throttle (fn) {
   let flag = null
   return function() {
     if (flag) return
     flag = setTimeout(() => {
       fn.apply(this.arguments)
       flag = null
     }, 500)
   }
 }

深拷贝

 const isObj = (val) => typeof val === "Object" && val !== null
 //方法一:
 function deepClone (obj) {
   const newObj = obj instanceof Array ? [] : {}
   for (const key in obj) {
     const item = obj[key]
     newObj[key] = isObj(item) ? deepClone(item) : item
   }
   return newObj
 }
 //方法二
 function deepClone(obj){
     let obj1=JSON.stringify(obj)
     let obj2=JSON.parse(obj1)
     return obj2
 }

intanceof

  • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
 function myInstanceof (left, right) {
   if (typeof left !== 'object' || left === null) return false
   let leftPro = left.__proto__, rightPro = right.prototype;
   while (true) {
     if (leftPro === null) return false
     if (leftPro === rightPro) return true
     leftPro = leftPro.__proto__
   }
 }

call

  • call()  方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
 Function.prototype.myCall = function(context) {
   // 判断调用者是否为函数
   if (typeof this !== "function") {
     throw new Error('type erroe')
   }
   // 不传参默认为window
   context = context || window
  // 使用Symbol 来确定唯一
   const fn = Symbol()
   // 模拟对象的this指向
   context[fn] = this
   // 获取参数
   const args = [...arguments].splice(1)
   // 绑定并执行参数
   const result = context[fn](...args)
   // 删除定义的this
   delete context[fn]
   return result
 }

apply

  • apply()  方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
 Function.prototype.myApply = function(context) {
   if (typeof this !== "function") {
     throw new Error("Error")
   }
   const fn = Symbol()
   context[fn] = this
   const arge = [...arguments][1]
   const result = context[fn](...arge)
   delete context[fn]
   return result;
 }

bind

  • bind()  方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
 Function.prototype.myBind = function(context) {
   if (typeof this !== "function") {
     throw new Error("Error")
   }
   context = context || window
   // 模拟this的指向
   const self = this
   // 获取参数
   const args = [...arguments].splice(1)
   // 最后返回一个函数,并绑定this要考虑到使用new去调用,并且bind是可以传参的
   return function Fn (...newFnArges) {
     if (this instanceof Fn) {
       return new self(...args, ...newFnArgs)
     } else {
       return self.apply(context, [...args, ...newFnArge])
     }
   }
 }

new

function myNew (fn, ...arges) {
      let newObj = Object.create(fn.prototype)
      let res = fn.call(newObj, ...arges)
      return res && typeof res === 'object' ? res : newObj
    }

继承

//call实现继承
function Person(uname, age) {
    this.uname = uname;
    this.age = age;
  }
  function Son(uname, age) {
    Person.call(this, uname, age);
  }
  var son = new Son("zhang", 12);
  console.log(son);

链表

function ListNode (val, next) {
  this.val = (val === undefined ? 0 : val)
  this.next = (next === undefined ? null : next)
}

二叉树

 function TreeNode (val) {
   this.val = val
   this.left = right = null
 }

数组去重

使用Set

不考虑兼容性,这种去重的方法代码最少。

这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。

var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log([...new Set(arr)] );

使用双重for循环,然后splice去重

双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。

NaN和{}没有去重,两个null直接消失了

function unique(arr){            
    for(var i=0; i<arr.length; i++){
    	for(var j=i+1; j<arr.length; j++){
        	if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
             	arr.splice(j,1);
                j--;
             }
         }
     }
	return arr;
}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))

利用indexOf去重

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。

NaN、{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))

利用includes

和上面indexOf思想类似

{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))

利用hasOwnProperty

所有的都去重了

function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))

数据的交并集

直接使用 filter、concat 来计算

var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
//交集
var c = a.filter(function(v){ return b.indexOf(v) > -1 })
//差集
var d = a.filter(function(v){ return b.indexOf(v) == -1 })
//补集
var e = a.filter(function(v){ return !(b.indexOf(v) > -1) })
        .concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}))
//并集
var f = a.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}));

ES6 中可以借助扩展运算符( ... )以及 Set 的特性实现相关计算,代码也会更加简单些。

var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
console.log("数组a:", a);
console.log("数组b:", b);

var sa = new Set(a);
var sb = new Set(b);

// 交集
let intersect = a.filter(x => sb.has(x));

// 差集
let minus = a.filter(x => !sb.has(x));

// 补集
let complement  = [...a.filter(x => !sb.has(x)), ...b.filter(x => !sa.has(x))];

// 并集
let unionSet = Array.from(new Set([...a, ...b]));

消息订阅与发布

class EventEmitter {
  constructor() {
    this.events = {}
  }
  //实现订阅
  on(type, callback) {
    if (!this.events[type]) {
      this.events[type] = [callback]
    } else {
      this.events[type].push(callback)
    }
  }

  //删除订阅
  off(type, callback) {
    if (!this.events[type]) return
    this.events[type] = this.events[type].filters(item => item !== callback)
  }

  //只执行一次订阅事件
  once(type, callback) {
    function fn() {
      callback()
      this.off(type, fn)
    }
    this.on(type.fn)
  }

  //触发事件
  emit(type, ...rest) {
    this.events[type] && this.events[type].forEach(fn => fn.apply(this, rest))
  }
}
// 使用如下
// const event = new EventEmitter();

// const handle = (...rest) => {
//   console.log(rest);
// };

// event.on("click", handle);

// event.emit("click", 1, 2, 3, 4);

// event.off("click", handle);

// event.emit("click", 1, 2);

// event.once("dbClick", () => {
//   console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");