前端面试之常见手写

121 阅读3分钟

最近要开始前端春招了,所以在这个文章中记下一些常见的手写代码题,巩固一下,顺带练一下手。 废话不多说,直接开始

1.手写call方法

// call函数

Function.prototype.myCall = function(context) {
    // 判断调用对象
    if (typeof this !== 'function') {
        console.error('type error')
    }
    
    // 获取除了第一项以外的所有参数
    let args = [...arguments].slice(1),
        result = null
    
    // 判断context是否传入,如果未传入则设置为 window,
    // 这与原生call方法一致,若不传入this值的时候,默认this指向全局对象window
    context = context || window
    
    // 将调用函数设置为对象的方法
    context.fn = this
    
    // 调用函数
    result = context.fn(...args)
    
    // 将属性删除
    delete context.fn
    return result
}

2.手写apply方法

// 手写apply函数 (思路和call差不多)
Function.prototype.myApply = function (context) {
    if (typeof this !== 'function') {
        console.error('type error')
    }
    
    context = context || window
    let result = null
    context.fn = this
    
    // 如果存在传入参数数组,就代入fn方法中,如果没有,就直接执行fn函数
    if (arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    
    delete context.fn
    return result
}
  1. 手写bind方法
// 手写bind方法(详细)
Function.prototype.myBind = function (context) {
      // 判断调用对象类型是否为函数
      if (typeof this !== 'function') {
          console.error('type error')
      }
      
      // 获取参数,创建fn保存调用myBind原始函数
      let args = [...arguments].splice(1),
          fn = this

      // 创建绑定函数
      const bound = function () {
          return fn.apply(
          // 判断 bound 函数是否是作为构造函数被调用(即是否使用 new 关键字)
          // 如果是作为构造函数调用,this 指向新创建的对象,那么就使用这个新对象作为 this 值;
          // 如果不是作为构造函数调用,就使用传入的 context 作为 this 值。
              this instanceof bound ? this : context,
              args.concat(...arguments)
          )
      }
      
       // 处理原型链,这里的目的是让 bound 函数作为构造函数调用时,新创建的对象能够继承原始函数 fn 的原型。
            let f = function () { }
            f.prototype = fn.prototype
            bound.prototype = new f()

            // 返回绑定函数
            return bound
      }
      

// 手写bind函数(袁老师版,比较推荐)
Function.prototype.myBind = function (context, ...args) {
    // 首先绑定调用 myBind 方法的对象
    const fn = this
    // bind方法返回一个函数,传递参数
    return function A(...agrs2) {
        // 判断是否是作为构造函数被创造(即new)
        if (Object.getPrototypeOf(this) === A.prototype) {
            // 如果是,返回一个new 构造函数
            return new fn([...args, ...agrs2])
        } else {
            // 如果不是,直接返回
            return fn.apply(context, [...args, ...agrs2])
        }

    }
}
// 手写bind函数(简单)
Function.prototyep.myBind = function(context, ...args) {
    // 绑定调用的对象到方法fn上
    let fn = this
    
    // 返回一个新函数,接受任意数量的参数args2
    return function A(...args2) {
        // 使用apply方法调用原始函数
        return fn.apply(context, [...args, ...args2])
    }
}

这个简易版手写的 myBind 函数虽然实现了 bind 方法的基本功能,但与原生的 bind 方法相比,存在一些局限性:

  • 作为构造函数调用的处理:原生 bind 方法返回的函数如果作为构造函数使用(即使用 new 关键字调用),this 会指向新创建的对象,而不是传入的 context。但这个 myBind2 函数没有处理这种情况,无论是否使用 new 关键字,this 始终绑定到传入的 context
  • 原型链继承:原生 bind 方法返回的函数在作为构造函数调用时,新创建的对象会继承原函数的原型。而 myBind2 函数没有实现这一特性。