前端求职必备---手写题、编程题

199 阅读8分钟

二叉树非递归前、中、后序遍历

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

function preOrder(root){
  var arr = [],res = [];
  arr.push(root);
  while (arr.length!=0) {
    var temp = arr.pop();  //弹出最后一个;
    res.push(temp.val);
    //将当前节点的左右子树存入
    if(temp.right) {
      arr.push(temp.right);
    }
    if(temp.left){
      arr.push(temp.left)
    }
    
  }
  return res;
}
//中序排序,两层遍历
function midOrder(root) {
  var res = [],arr= [];
  while (true) {
    while (root != null) {
      arr.push(root);
      root = root.left;
    }
    if(arr.length==0){
      break;
    }
    var temp = arr.pop();
    res.push(temp.val);
    root = temp.right;
  }
}

//后序遍历
function lastOrder(root) {
  var arr = [],res = [];
  arr.push(root);
  while (arr.length != 0) {
    var temp = arr.pop();
    res.push(temp.val);
    if(temp.left) {
      arr.push(temp.left)
    }
    if(temp.right){
      arr.push(temp.right)
    }    
  }
  return  res.reverse();
}

//层次遍历
function levelOrder(root) {
  var stack = [root];
  var level = [];
  var res = [];
  var size = stack.length;
  while (stack.length > 0) {
    while (size-- > 0) {
      var temp = stack.shift();
      level.push(temp.val);
      temp.left && stack.push(temp.left);
      temp.right && stack.push(temp.right);
    }
    res.push(level);
    level = [];
    size = stack.length;
  }
  return res;
}

手写ajax

 <srcipt>
    var xhr = new XMLHttpRequest();
    xhr.open('post',url,true); //建立连接
    xhr.onreadstateChange = function(){
        if(xhr.readstate === 4){  //当该状态为4的时候,此时则表示响应数据已经成功接收并解析完成
            if(xhr.status === 200){ //服务器成功响应数据,饭想
               //to doing
            }
        }
    }
 </srcipt>

手写防抖、节流

// 防抖
 function debounce(fuc, wait = 1000, immediate = false){
     let timer = null;
     return function debounced(...args){
         //如果当前计时器不为空
         if(timer)clearTimeout(timer);
         //首次立即执行
         if(immediate && !timer){
             timer = setTimeout(()=>{
                 fuc.apply(this, ..args); // 调用回调函数
                 timer = null;
             },wait)             
         }
             timer = setTimeout(()=>{
                 fuc.apply(this, ..args); // 调用回调函数
                 timer = null;
             },wait)
        //提供撤回函数
        debounced.cancel = ()=>{
            clearTimeout(timer)
            timer = null
        } 
         return;
     }     
 }
 //节流,在固定时间内一定会执行某个操作
 function throttle(fn, wait = 1000 ){
    let timer = null;
    let previous = 0;
    // <!--const {leading, trailing} = options;-->
    //利用闭包实现
    const throttled = function(...args){
        const now = new Date().getTime();
        if(now - previous > wait){
            previous = now;
            fn(...args);
        }
    }
    return throttled
 }

参考博客

手写js的继承方式

//js的寄生组合继承方式
function A (name) {
    this.name = name
}
A.prototype.sayName = function () {
     console.log(this.name)  
}
//原型式继承
function B (name,age) {
    this.age = age
    A.call(this,name) //将this指向需要继承的A对象,则我们在new对象的时候,可以根据this的指向复制属性
}

(function () {
    //创建一个没有实例方法的类
    var Super = function () {}
    Super.prototype = A.prototype // 将空类的原型对象执行父类原型对象
    B.prototype = new Super(); // 将子类的原型对象指向空实例方法的类
})
B.prototype.constructor = B // 将原型对象中的
  • 创建没有实例方法的类
  • 类的原型对象指向父类的原型对象
  • 子类的原型对象指向无实例方法类的实例

手写core、jsonp的实现原理

// 手写jsonp
function addScriptTag(url) {
    var script =  document.createElement('script');
    script.setAttribute('type','text/javascript');   
    script.src = url
}
function callerHandler(params) {
    //todo somehting ...
}
window.onload = function () {
    addScriptTag("http://www.baidu.com?callback=callerHandler")
}

//手写cores
function _myCore() {
    var xhr = new XMLHttpRequest();
    xhr.open('post',url,true);
    xhr.onreadystateChange = function () {
        if(xhr.readyState == 4){
            if(xhr.status==200){
                //todo something
            }
        }
    }
}

正则表达式的应用

将url查询参数解析成字典对象

  • 先截取请求参数字符串
  • 使用decodeURLComponent函数进行解码
  • 正则匹配出参数对象
 function getUrlObject(url){
     url = url==null?window.loaction.href:url;
     var search = url.substring(url.indexOf("?") + 1);
     var obj = {};
     var reg = /([^?&=]+)=([^?&=]*)/g; //   /g实现全局匹配
     search.replace(reg,function(rs,key,value){
         var name = decodeURLComponent(key);
         var val = decodeURLComponent(value);
         obj[name] = val;
     })
       
 }
手写实现给定url,将url的参数以对象形式输出

截取字符串、切割字符串、转换参数即可实现

function getUrlParma(url) {
    var pamrma = url.substr(url.indexOf('?'));
    if(!pamrma) return undefined;
    let preArr = pamrma.split('&');
    let res = {};
    for(var key in preArr){
        var item = key.split('=');
        res[item[0]] =  decodeURIComponent(item[1])
    } 
    return res
}

手写通用拷贝函数

function deepCopy(obj){
    var newobj 
    if(obj && typeof obj !='object'){
        newobj = obj;
    }else{
        for(var i in obj){
            if(obj.hasOwnProperty(obj[i]){
                if(obj && typeof obj != 'object'){
                    newobj[i] = obj[i]
                }else{
                    newobj[i] =  deepCopy(obj[i])
                }
            }
        }     
    }
    return newobj
}

排序方式中 冒泡、排列、堆、快排、直接排序

  //快排
  function qucikSort(arr){
       if(arr.length <= 1) return arr;
       var baseIndex = Math.floor(arr.lenght/2) //向上取整
       var base = arr.splice(baseIndex,1)[0] // 截取完返回需要截取的内容
       var left = [],right = [];
       for(var i = 0 ;i < arr.length;i++){
           if(arr[i]<base){
               left.push(arr[i])
           }else{
               right.push(arr[i])
           }
       }
       return quickSort(left).concat([base],quickSort(right));
  }
  
  // 直接插入排序,其思路是,当前元素和前面排序完成的子序列进行比较,直到找到比自己小的元素
  function insertSort(arr){
      var temp = arr[0];
      for(var i = 1 ;i < arr.lenght; i++){
         if(arr[i-1] > arr[i]){ //前提是当前值要小于前一位
              temp  = arr[i];
              for(var j = i; arr[j-1] > temp ;j--){
                  arr[j] = arr[j-1]; // 满足条件向后移动一位
              }             
         }
      }
  }
 // 冒泡排序
 function bubbleSort(arr){
     for(var i = 0 ; i < arr.length - 1 ; i++){
         for(var j = 0 ;j < arr.length - 1 - i ; j++){
             if( arr[j] > arr[j+1] ){
                 var t = arr[j];
                 arr[j] = arr[j+1];
                 arr[j+1] = t;
             }
         }
     }
 }
 //堆排序,最大堆排序:任意节点的值总不小于其子节点的值
 //1、给定的数组经过变换得到一个最大推
 //2、将最大堆和末尾的树交互,然后除了最后一个节点以外的新的二叉树进行维护形成新的最大堆

快速排序、选择排序、希尔排序、堆排序是不稳定的排序方式

bind和apply、call的区别 以及手写实现

答案解析:

三个函数都是作用都是可以在特定的作用域中调用函数,相当于设置函数体内this的值,用于扩充函数赖以运行的作用域。

  • apply接收两个参数一个this的指向对象、第二个是传递的参数
  var func = function(a,b,c){
      console.log(a,b,c);
  } 
  var obj = {a: 2} // 定义一个对象用来绑定
  Function.prototype.myApply = function(base,args){
      base  = base || windows;  //考虑base为null或者undefined的情况
      base.fn = this;  // 在base对象新增一个函数对象,在该函数中this指向的是运行时的对象
      var result = base.fn(...args); //引用指向 调用该apply函数的this对象,并且传递参数
      delete base.fn; //删除多余的属性
      return result;
  }
  func.myApply(obj,[1,2,3])
  • call接收的参数时不定的,第一个参数是需要指向的对象,其他则为形参
  var func = function(a,b,c){
      console.log(a,b,c);
  } 
  var obj = {a: 2} // 定义一个对象用来绑定
  Function.prototype.myCall = function(base,...args){
      base  = base || windows;  //考虑base为null或者undefined的情况
      base.fn = this;  // 在base对象新增一个函数对象,在该函数中this指向的是运行时的对象
      var result = base.fn(...args); //引用指向 调用该apply函数的this对象,并且传递参数
      delete base.fn; //删除多余的属性
      return result;
  }
  func.myCall(obj,1,2,3)
  • bind的区别是,它返回的是一个箭头函数,而apply,call返回的处理结果;bind传参形式与call是一致的。
  var func = function(a,b,c){
      console.log(a,b,c);
  } 
  var obj = {a: 2} // 定义一个对象用来绑定
  Function.prototype.myBind(base,..args1){
      return (...args2)=>{ // 用来接收执行bind函数时所带的参数,箭头函数没有arguement对象,但是可以使用...展开符来接收
     // 箭头函数不会生成自身作用域的this对象,会引用箭头函数声明时的父级上下文的this 
          base = base || window;
          base.fn = this;
          var result = base.fn(...args1,...args2);
      }
  }
 var _bind = func.myBind();
 _bind();  

js/ES实现发布订阅模式

简单的发布-订阅模式

class Publish{
  constructor(this){
     this.handlers = []; //初始化事件容器
  }
  //事件添加方法,参数是事件类型,事件处理方式
  addEventLister(type, handler){
    if(!this.handlers[type]){
      this.handlers[type] = [];
    }
    this.handlers[type].push(handler); // 添加事件
  }
  //触发事件两个参数(事件名,参数)
  dispatchEvent(type,...args){
    if(!this.handlers[type]){
      return new Error("未注册该类型事件")
    }
    //触发全部的类型的事件
     this.handlers[type].forEach(fn => {
         fn.apply(this,args);
     }); 
  }
}

参考文章

防抖与节流

答案解析:

// 防抖
 function debounce(fuc, wait = 1000, immediate = false){
     let timer = null;
     return function debounced(...args){
         //如果当前计时器不为空
         if(timer)clearTimeout(timer);
         //首次立即执行
         if(immediate && !timer){
             timer = setTimeout(()=>{
                 fuc.apply(this, ..args); // 调用回调函数
                 timer = null;
             },wait)             
         }
             timer = setTimeout(()=>{
                 fuc.apply(this, ..args); // 调用回调函数
                 timer = null;
             },wait)
        //提供撤回函数
        debounced.cancel = ()=>{
            clearTimeout(timer)
            timer = null
        } 
         return;
     }     
 }
 //节流,在固定时间内一定会执行某个操作
 function throttle(fn, wait = 1000 ){
    let timer = null;
    let previous = 0;
    // <!--const {leading, trailing} = options;-->
    //利用闭包实现
    const throttled = function(...args){
        const now = new Date().getTime();
        if(now - previous > wait){
            previous = now;
            fn(...args);
        }
    }
    return throttled
 }

参考博客

生成N个不重复的[min,max]的随机数

方法一:

    //存储一个顺序数组,每次随机生成一个下标树之后再删除掉
    function rand_1(n , min , max){
     var arr = [];
     // 是min到max区间的值
     for(var i = 0; i < max - min + 1; i++ ){
         arr[i] = i+1;
     } 
     for(var k = arr.length - 1; k >=0 ; k--){
         var index = parseInt(Math.random()*k) // 生成0到k直接的随机数
         var temp = arr[k];
         arr[k] = arr[index];
         arr[index] = temp;
     }
     return arr;
    }

参考链接

二叉搜索树

基本性质:

  • 在任意结点的左子树不为空,则左子树上所有结点的值均小于它的根节点的值
  • 若任意结点的右子树不为空,则右子树的所有结点的值均大于它的根节点的值
  • 任意节点的左、右子树分别也是二叉搜索树

手写new的手撕代码

使用new来调用函数会自动执行一下步骤:

  • 创建一个新的对象
  • 这个新对象会执行[[prototype]] 连接
  • 这个新的对象会绑定到函数调用的this
  • 如果函数没有返回其他对象,则new表达式中的函数会自动返回这个新的对象
function _new(){
    let obj = new Object(); //创建新的对象obj
    let fn = Array.prototype.shift().call(arguments); //取参数的第一项作为构造函数

    //将创建的对象的原型链指向构造函数的原型对象
    obj.__proto__ = fn.prototype;
    // result接收构造函数执行后返回的结果
    let  result = fn.apply(obj, arguments);
    return typeof result === 'object' ? result : obj;
}

参考博客

手写实现reduce、filter、find等函数

//必须使用function,不能使用箭头函数
Array.prototype.myReduce = function(fn , val){
    if(typeof fn != 'function'){
        throw new TypeError('Not function')
    }
    //this是调用函数的数组的
    for(let i = 0 ;i < this.length;i++){
         val = fn(val, this[i]) // val为上一个值,this[i]当前传值
    }
    return val;
}

参考文章

promise实现原理

promise可以认为是一个异步函数状态机,利用观察者模式的编程思想,通过特定书写方式去注册对应状态的事件处理函数,然后更新状态,调用注册过的处理函数即可。

实现promise原理主要是去实现定义一下变量以及方法:

/*
* status:pending(进行中)、resolved(成功)、rejected(失败)
* doneList:成功处理函数列表
* failList:失败处理函数列表
* done:注册成功处理函数
* fail:注册失败处理函数
* resolve:更新state为:resolved,并且执行成功处理队列
* reject: 更新state为:rejected,并且执行失败处理队列
*/
1、在构造函数中初始化promise实例对象的初始化状态、调用构造函数传参的function,参数是resolve和reject函数。
2、依次完成注册成功、失败处理函数,将事件添加到对应的队列中
3、声明resolve、reject函数,一旦触发,改变该promise对象的当前状态,并且执行对应处理队列的事件。

promise的all()的实现

Promise.all = function (promiseList) {
  return new Promise((resolve, reject) => {
     let count = 0;
     let res = [];
     if(promiseList.length == 0){
       resolve(res); // 如果数组中没有相关的promise对象,则直接成功返回
     }else{
       function storeValue(index, data) {
         res[index] = data; // index保证返回的结果可以按顺序返回 
         if(++count == promiseList.length){
           resolve(res) // 改变promise对象的状态
         }       
       }
       for(let i = 0; i < promiseList.length; i++){
         Promise.resolve(promiseList[i]).then((data) =>{
           storeValue(i,data);
         }, (err) =>{
           reject(err);
           return;
         })
       }
     }

  })
  
}

参考文章

事件委托的应用

利用事件冒泡原理,从目标元素到父元素层层向上,所以如果是多个重复的子元素,我们可以在父类添加点击事件,并且利用Event对象提供的属性target属性,也就是事件源,去操作返回事件的目标节点.

  <body>
    <ul id='ull'>
      <li>11</li>
      <li>22</li>
    </ul>
  </body>
  <script>
    window.onload = function () {
      var list = document.getElementsByTagName('li');
      for(let i = 0 ; i < list.length; i++){
        //一定要采用闭包形式
        list[i].onclick = (function (n) {
           return function () {
             //to do something
              alert(n)
           }
        } 
        )(i)
      }
    }
  </script>

参考文章

css实现三角形

<div class='san'></div>
<style>
 .san{
     width:100px;
     height:100px;
     border-left:100px solid transparent;
     border-right:100px soild transparent;
     border-bottom:100px soild transparent;
 }
</style>