面试题里的那些各种手写

栏目: JavaScript · 发布时间: 5年前

内容简介:最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。翻阅了很多书籍和博客。简单的说明:

最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。

翻阅了很多书籍和博客。

这里做一个总结改进,算是对我后面大概为期一个月找工作的准备。

手写实现 bind()

Function.prototype._bind = function (context) {
            var self = this;
            var args = Array.prototype.slice.call(arguments, 1);
            var fBind = function () {
                var bindArgs = Array.prototype.slice.call(arguments);
                return self.apply(this instanceof fBind ? this : context, args.concat(bindArgs));
            }
            fBind.prototype = self.prototype&&Object.create(self.prototype)
            return fBind;
        }

简单的说明:

  • 这里之所以 传参 的时候需要两个 数组 ,是因为考虑到用 new 以构造函数的形式调用硬绑定函数的情况: this 这时是应该指向实例对象的。
  • 这样子就需要继承之前函数的方法, fBind.prototype = self.prototype&&Object.create(self.prototype)

,同时也可以用 Object.setPrototypeOf(fBind.prototype,self.prototype)

考虑到存在 undefined 的情况,前面加一个判断 self.prototype&&.....

  • 关于 apply 的第一个参数,如果考虑到之前的情况,是不能传入 context 的,这需要做一个判断。

像是下面的情况

function Foo(price){ 
       
          this.price =price
            this.fn = ()=>{
                console.log('hi fn')
            }
             console.log(this.name)
        }

        Foo.prototype.sayMyName = function(){
            console.log(this.name)
        }
      var Obj1 = {
        name:'obj1'
      }
        var b =Foo._bind(Obj1)
        b() //"obj1"
        var c = new b(1000)//"i am c"
        c.name = 'i am c'
        c.sayMyName()

这里的 this 的指向就是 c ,它指向 实例对象本身

后面以这道题为引线面试官可能会追问:

  • 什么是执行上下文
  • this的判断
  • call,bind的区别

手写一个函数实现斐波那契数列

首先拷一个阮神在他 es6教程 里的一个写法。

function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (;;) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

更精简的

const feibo= max=>{
    let [a,b,i]= [0,1,1]
    while(i++<=max) {
        [a,b] = [b,a + b ]
       console.log(b)
    }
  return a  
}

相对是非常简单的,感觉也不会多问啥,就不多说了。

手写一个简单的ajax

let xhr = new XMLHttpRequest()

        xhr.open('get', url, true)

        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
            console.log('请求完成')
                if(this.status >= 200 &&this.status<300){ 
                    conso.log('成功')
                }else{
                    consol.log('失败')
                }
            }
        }
         xhr.onerror = function(e) {
         console.log('连接失败')
        }
        xhr.send()

大概是这么个意思就差不多了,顺势可能会问一些状态码和状态值的问题,或者直接问到关于 http 上面的问题。

原型继承

function inherit(supertype,subtype){
            Object.setPrototypeOf(subtype.prototype,supertype.prototype)
            subtype.prototype.constructor = subtype
        }

        function Car(name,power){
            this.name=name
            this.power = power
        }

        Car.prototype.showPower = function(){
            console.log(this.power)
        }

        function Changan(price,name,power){
            this.price = price
            Car.call(this,name,power)
        }

        inherit(Car,Changan)

        Changan.prototype.showName = function(){
            console.log(this.name)
        }

        var star = new Changan(100000,'star',500)

        star.showPower()

防抖与节流

function debounce(fn,duration){
            var  timer
            window.clearTimeout(timer)
            timer = setTimeout(()=>{
                fn.call(this)
            },duration)
        }
  function throttle(fn,duration){
            let canIRun
            if(!canIRun)return
            fn.call(this)
            canIRun = false
            setTimeout(()=>{
                canIRun = true
            },duration)
        }

数组去重

我一般就用这两种,大部分情况都能应付了。

[...new Set(array)]
//hash
 function unique(array) {
      const object = {}
      array.map(number=>{
          object[number] = true
      })
      return Object.keys(object).map(//.......)
  }//大概是这样的意思,写法根据数组的不同可能会有所改变

深拷贝

应该是面试里面 手写xxx 出现频率最高的题了,无论是笔试还是面试。

总是让你手写实现深拷贝函数。

事实上,js并不能做到真正完全的标准的深拷贝

所以不管你写什么样的 深拷贝函数 都会有不适用的地方,这取决于使用的场景和拷贝的对象,如果面试官在这上面钻研比较深的话,是很难做到完美的。

既然这样就写个 将就一点 的深拷贝吧,面向面试的那种。

function deepClone(item) {
      return result;
  }
  • 首先在 类型判断 上做一个选择,一般情况来说,用 new 创建的实例对象用 typeof 判断会出问题的,相比之下 instanceof 也不靠谱。这里面 相对 比较靠谱的 Object.prototype.toString.call(item) 。(这个其实也不兼容到全部情况和性能要求,但是面向面试代码可读性会高一点)。

    type = Object.prototype.toString.call(item).slice(8,this.length-1),
    //[object String],[object Array],[object Object]
  • 函数的拷贝,这里不能使用 bind ,会改变 this 指向或影响后续使用 call 调用该拷贝的函数,大部分情况是不必要的,这里就直接赋值吧。
  • 于是这里可以把基本数据类型和 Function 放一起。

    fk= ['String','Number','Boolean','Function'].indexOf(type)>-1
  • dom 对象的拷贝: result = item.cloneNode(true);
  • 忽略正则
  • Date[object Object] , [object Array] 放到后面的判断

    let other = {           //需要递归或者其他的操作
                          Array() {
                              result = []
                              item.forEach((child, index)=>{
                                  hash.set(item, result);
                                  result[index] = deepClone(child,hash)
                              })
                          },
                          Date(){
                              result = new Date(item)
                          },
                          Object(){
                              result = {}
                              Reflect.ownKeys(item).forEach(child=>{
                                  hash.set(item, result);
                                  result[child] = deepClone(item[child],hash)
                              })
                          }
                      }
                      other[type]()

这样子是不是相对清晰一些了,应付一般的情况应该差不多了,但是没考虑 循环引用

这里给个思路是使用 ES6WeakMap ,不知道的兄弟可以看看阮神的ES6 博客 ,为防止爆栈,我把循环引用直接扔给它,完美拷贝。

就相当于

var wm = new WeakMap()

var obj = {
   name:null
 }
obj.name = obj
wm.set(obj,wm.get(obj))
console.log(wm)

现在就需要在开头检查一下循环引用,然后直接返回WeakMap对象键名为item参数对象的值

所以最终代码就是

function deepClone(item,hash = new WeakMap()) {
       if (!item) return item
       if (hash.has(item))return hash.get(item);  //检查循环引用
           var result,
             type = Object.prototype.toString.call(item).slice(8,this.length-1),
             fk= ['String','Number','Boolean','Function'].indexOf(type)>-1

               if(fk){
                   result = item;//直接赋值
               }else if(item.nodeType && typeof item.cloneNode === "function"){
                   result = item.cloneNode(true);          //是否是dom对象
               }else{

                   let other = {           //需要递归或者其他的操作
                       Array() {
                           result = []
                           item.forEach((child, index)=>{
                               hash.set(item, result);
                               result[index] = deepClone(child,hash)
                           })
                       },
                       Date(){
                           result = new Date(item)
                       },
                       Object(){
                           result = {}
                           Reflect.ownKeys(item).forEach(child=>{
                               hash.set(item, result);
                               result[child] = deepClone(item[child],hash)
                           })
                       }
                   }
                   other[type]()
               }
       return result;
   }

意思就大概是这个意思,当然深拷贝的方法有很多,甚至不一定用到递归。面试官总会有找茬的地方的。

我觉得我写的这个还是满足我现在找工作的级别要求的。

然后是我用来测试的对象

var obj1 = {
   name:'obj1',
   one : {
       a:new Date(),
       b:new String('1-2'),
       c:new Array(['this','is',1,{a:23}]),
       d: function () {
           if(true){
               return 'd'
           }
       },
       e:new Number(15),
       f:new Boolean(true)
   },
   two(x){
       console.log(x+' '+this.name)
   },
   three : [
       {
           a:'this is a',
            b:document.body,  
           c:{
               a:[1,2,3,4,5,[13,[3],true],10],
               b:{
                   a:{
                       a:[1,2,3]
                   },
                   b:2
               }
           }
       },
   ],
   four:[1,2]
}
    obj1.name=obj1
    obj1.four[3] = obj1
   var copy = deepClone(obj1)

   console.log(copy)
   copy.two.call(window,'hi')

## new

简单说下大概是这么一个过程

  • 创建一个空对象
  • 执行传入的构造函数,执行过程中对 this 操作就是对 这个空对象 进行操作。
  • 返回这个空对象

模拟需要考虑的问题

  • 是一个空对象,这里面的写法 obj 原型链是没有上一级的,即不存在与其他任何对象之间的联系,虽然在这里面没多少区别: var obj = Object.create(null),
  • this 指向这个空对象: let rt = Constructor.apply(obj, arguments);
  • 可以访问构造函数的原型链, Object.setPrototypeOf(obj, Constructor.prototype);
  • 如果构造函数有返回值,并且是一个对象,那么实例对象拿到的就是这个对象(应该只是值,并不是引用)。所以这里要做个判断 return typeof rt === 'object' ? rt : obj;

    最终的代码

function _new(){
    var obj =  Object.create(null),
    Constructor = [].shift.call(arguments);
    Object.setPrototypeOf(obj, Constructor.prototype);
    let  rt = Constructor.apply(obj, arguments);
    return rt instanceof Object ? rt : obj;
}

<br/>

<br/>

快速排序

快排

:代码精简了一点

function quickSort(arr){
       if(arr.length<=1)return arr
       var index = Math.floor(arr.length/2),
           number = arr.splice(index,1)[0],
           left = [],
           right = [];
       arr.forEach(item=>{
        item<=number?left.push(item):right.push(item)
       })
       return quickSort(left).concat([number],quickSort(right))
   }

这期间会不断更新并修改,这里面的手写实现您如果有更好的写法或者新的思路,也希望可以说明交流。最后谢谢大佬些的观看。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员的数学思维修炼(趣味解读)

程序员的数学思维修炼(趣味解读)

周颖 / 清华大学出版社 / 2014-4-1 / 45.00元

本书是一本专门为程序员而写的数学书,介绍了程序设计中常用的数学知识。本书门槛不高,不需要读者精通很多高深的数学知识,只需要读者具备基本的四则运算、乘方等数学基础知识和日常生活中的基本逻辑判断能力即可。本书拒绝枯燥乏味的讲解,而是代之以轻松活泼的风格。书中列举了大量读者都很熟悉,而且非常有趣的数学实例,并结合程序设计的思维和算法加以剖析,可以训练读者的数学思维能力和程序设计能力,进而拓宽读者的视野,......一起来看看 《程序员的数学思维修炼(趣味解读)》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具