关于前端面试中常考的各种手写代码题

因为最近面试被问太多这样的套路了

  1. 谈谈对XXX的理解?
  2. 你对XXX是怎么实现的有什么思路?
  3. 你可以自己手写实现一个简单demo吗?

所以就整理了一下

手写实现Bind函数

// 简单实现

Function.prototype.bind = function(){
    var self = this;
    // 保存传入的上下文
    context = [].shift().call(arguments);
    // 保存传入的参数
    params = [].split().call(arguments);
    return this.apply(context,[].concat.call(params,[].split().call(arguments)))
}

// MDN源码实现

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        // 下面这句有点难以理解…我的理解也不一定正确
        // 当bind返回的函数不是作为构造函数的话,this instanceof fNOP 是false,反之为true
        // 所以就是进入Othis || window
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };
	// 把原来函数的原型链传给了fbound,然后返回fbound就好
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
复制代码
  • Bind是什么:bind就是改变某个方法的执行时上下文主体(简单理解为改变this的指向)
  • Bind解决了什么问题:简单理解为,在你需要的时候去借用一个函数。(指定某个方法的执行时上下文主体)
  • Bind和apply,call的区别在哪里:
    • bind返回的是改变了上下文后的函数,apply,call返回的是改变了上下文后的函数的执行结果
    • 传参格式不同,Bind是一个个传,apply传一个数组

手写实现Promise

try {
    module.exports = Promise
} catch (e) {
    console.log(e)
}
    
  function Promise(executor) {
    var self = this
    
    // 确定一个当前状态值
    self.status = 'pending'
    // 创建一个resolved 和 reject 的调用链
    self.onResolvedCallback = []
    self.onRejectedCallback = []
    
    function resolve(value) {
      // 如果传入的是一个Promise对象,则继续链式调用
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      setTimeout(function() { // 异步执行所有的回调函数
        if (self.status === 'pending') {
          self.status = 'resolved'
          self.data = value
          for (var i = 0; i < self.onResolvedCallback.length; i++) {
            self.onResolvedCallback[i](value)
          }
        }
      })
    }
    
    function reject(reason) {
      setTimeout(function() { // 异步执行所有的回调函数
        if (self.status === 'pending') {
          self.status = 'rejected'
          self.data = reason
          for (var i = 0; i < self.onRejectedCallback.length; i++) {
            self.onRejectedCallback[i](reason)
          }
        }
      })
    }
    
    // 这个executor就是执行函数,但是其实我没有查到这个函数的相关资料 = =
    try {
      executor(resolve, reject)
    } catch (reason) {
      reject(reason)
    }
  }
    
  function resolvePromise(promise2, x, resolve, reject) {
    var then
    var thenCalledOrThrow = false
    
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected for promise!'))
    }
    
    if (x instanceof Promise) {
      if (x.status === 'pending') { //because x could resolved by a Promise Object
        x.then(function(v) {
          resolvePromise(promise2, v, resolve, reject)
        }, reject)
      } else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
        x.then(resolve, reject)
      }
      return
    }
    
    if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
      try {
        then = x.then //because x.then could be a getter
        if (typeof then === 'function') {
          then.call(x, function rs(y) {
            if (thenCalledOrThrow) return
            thenCalledOrThrow = true
            return resolvePromise(promise2, y, resolve, reject)
          }, function rj(r) {
            if (thenCalledOrThrow) return
            thenCalledOrThrow = true
            return reject(r)
          })
        } else {
          resolve(x)
        }
      } catch (e) {
        if (thenCalledOrThrow) return
        thenCalledOrThrow = true
        return reject(e)
      }
    } else {
      resolve(x)
    }
  }
    
  Promise.prototype.then = function(onResolved, onRejected) {
    var self = this
    var promise2
    onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
      return v
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
      throw r
    }
    
    if (self.status === 'resolved') {
      return promise2 = new Promise(function(resolve, reject) {
        setTimeout(function() { // 异步执行onResolved
          try {
            var x = onResolved(self.data)
            resolvePromise(promise2, x, resolve, reject)
          } catch (reason) {
            reject(reason)
          }
        })
      })
    }
    
    if (self.status === 'rejected') {
      return promise2 = new Promise(function(resolve, reject) {
        setTimeout(function() { // 异步执行onRejected
          try {
            var x = onRejected(self.data)
            resolvePromise(promise2, x, resolve, reject)
          } catch (reason) {
            reject(reason)
          }
        })
      })
    }
    
    if (self.status === 'pending') {
      // 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
      return promise2 = new Promise(function(resolve, reject) {
        self.onResolvedCallback.push(function(value) {
          try {
            var x = onResolved(value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (r) {
            reject(r)
          }
        })
    
        self.onRejectedCallback.push(function(reason) {
            try {
              var x = onRejected(reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (r) {
              reject(r)
            }
          })
      })
    }
  }
    
  Promise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected)
  }
    
  Promise.deferred = Promise.defer = function() {
    var dfd = {}
    dfd.promise = new Promise(function(resolve, reject) {
      dfd.resolve = resolve
      dfd.reject = reject
    })
    return dfd
  }
复制代码

手写原生AJAX

function loadXMLDoc()
{
var xmlhttp;
    if (window.XMLHttpRequest){
        // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else{
        // 旧版IE用ActiveXObject
        // code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function(){
        // readyState有5个状态,分别代表
        // 0: 请求未初始化
        // 1: 服务器连接已建立
        // 2: 请求已接收
        // 3: 请求处理中
        // 4: 请求已完成,且响应已就绪
        if (xmlhttp.readyState==4 && xmlhttp.status==200){
            document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET","/ajax/test1.txt",true);
    xmlhttp.send();
}
复制代码

手写Promise包装下的AJAX

function myGet(url) {
    return new Promise(function(resolve, reject) {
        $.get(url, resolve);
    });
}

myGet('/url1').then(function (data) {
    // 一些处理,data为返回值
    return myGet('/url2');
}).then(function (data) {
    // 一些处理
    return myGet('/url3');
}).then(function (data) {
    // 最终处理
});

复制代码

手写 EventEmitter

// 参考https://blog.csdn.net/bdss58/article/details/51473107
// 参考https://github.com/mqyqingfeng/EventEmitter/blob/master/eventEmitter.js
// 参考https://juejin.im/post/5b5797b0f265da0fa8674669

class EventEmitter {
  constructor(max_event) {
      this.listeners = new Map();
      // 设置一个最大监听数
      this.max_event = max_event || 10; 
  }
  // 添加一个监听器
  addListener(label, callback) {
      this.listeners.has(label) || this.listeners.set(label, []); // 这里的意思其实是,如果有这个label,那么将传入的cb函数再传给label下的函数,再放到队尾;如果没有label,直接push
      this.listeners.get(label).push(callback);
  }
  // 移除一个监听器
  removeListener(label, callback) {
      let listeners = this.listeners.get(label);
      let index;
      if (listeners && listeners.length) {
          // MDN:reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其简化为单个值。
          // reduce() API:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
          // 注意到addListener方法中,一个label是对应了一个数组的,也就是说label下可以监听多个函数,那么remove和emit都要找到这个用户指定的函数。找不到就是-1;但是这里有个问题就是用户如果重复监听怎么办
          // 也就是两次addListener同一个label同一个callback
          index = listeners.reduce((i, listener, index) => {
              return (isFunction(listener) && listener === callback) ? i = index : i;
          }, -1);
      }
      // 如果有这个函数,删掉
      if (index > -1) {

          listeners.splice(index, 1);
          this.listeners.set(label, listeners);
          return true;
      }

      return false;
  }
  // 监听,调用,注意这里是把这个label下的所有方法全都调用一次
  emit(label, ...args) {
      let listeners = this.listeners.get(label);
      if (listeners && listeners.length) {
          listeners.forEach((listener) => {
              listener(...args);
          })
          return true;
      }

      return false;
  }
}

// 实现观察者
class Observer {
  constructor(id, subject) {
      this.id = id;
      this.subject = subject;
  }
  on(label, callback) {
      this.subject.addListener(label, callback);
  }
}
复制代码

手写kmp算法

var kmp = function(sourceStr, subStr){
    var partMatch = kmpGetPartMatchLen(subStr);
    var result = false;

    for(var i = 0; i < sourceStr.length; i++){
        for(var j = 0; j < subStr.length; j++){
            if(subStr.charAt(j) === sourceStr.charAt(i + j)){
                if(j === subStr.length - 1){
                    result = true;
                    break;
                }
            }else{
                //实现回滚,以subStr为参照物,即sourceStr往前移动
                if(j > 0 && partMatch[j-1] >= 0){
                	 //公式在此处实现
                    i += (j - 1 - partMatch[j-1] - 1);
                }else{
                    break;
                }
            }
        }
        if(result) break;
    }

    if(result){
        return i;
    }else{
        return -1;
    }
};
复制代码

参考资料

转载于:https://juejin.im/post/5ba8bb05e51d450e79037a24

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值