面试题里的那些各种手写

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

内容简介:最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现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))
   }

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


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

查看所有标签

猜你喜欢:

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

Web Data Mining

Web Data Mining

Bing Liu / Springer / 2006-12-28 / USD 59.95

Web mining aims to discover useful information and knowledge from the Web hyperlink structure, page contents, and usage data. Although Web mining uses many conventional data mining techniques, it is n......一起来看看 《Web Data Mining》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

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

HEX HSV 互换工具