Skip to content

高频手写题

new

js
function New(fn, ...args) {
  // 1. 创建一个新对象并将新对象的隐式原型(obj.__proto__)指向构造函数的显示原型(fn.prototype)
  const obj = Object.create(fn.prototype)
  // 2. 将构造函数的this指向新对象
  const res = fn.call(obj, ...args)
  /**
   * 3. 构造函数本身如果返回的是对象的话,则返回的就是此对象
   *    如果返回值不是对象的话,则返回对应的obj
   */
  return  res instanceof Object ? res : obj
}

call

js
Function.prototype.Call = function(context = window, ...args) {
  // 1. 获取传入对象的上下文
  const object = context
  // 2. 使用临时属性fn将构造函数挂载在传入的对象上
  object.fn = this
  // 3. 在传入的对象下执行构造函数,将构造函数的属性和方法赋值给传入的对象
  const res = object.fn(...args)
  // 4. 删除临时属性
  delete object.fn
  // 5. 返回结果
  return res
}

apply

js
Function.prototype.Apply = function(context = window, args) {
  // 1. 获取传入对象的上下文
  const object = context
  // 2. 使用临时属性fn将构造函数挂载在传入的对象上
  object.fn = this
  // 3. 在传入的对象下执行构造函数,将构造函数的属性和方法赋值给传入的对象
  const res = args ? object.fn(...args) : object.fn()
  // 4. 删除临时属性
  delete object.fn
  // 5. 返回结果
  return res
}

bind

js
Function.prototype.bind = function(context = window, ...args) {
   // 定义中转构造函数,用于通过原型连接绑定后的函数和调用bind的函数
   let F = function() {},

      // 记录调用函数,生成闭包,用于返回函数被调用时执行
      self = this,

      // 定义返回(绑定)函数
      bound = function() {
        // 返回函数后仍可接收参数 合并参数,绑定时和调用时分别传入的
        let finalArgs = [...args, ...arguments]

        /**
         * 改变作用域,注:aplly/call是立即执行函数,即绑定会直接调用
         * 这里之所以要使用instanceof做判断,是要区分是不是new xxx()调用的bind方法
         * bind返回的新函数也能使用new操作符创建对象:这种行为就像把原函数作为构造器,提供的this值被忽略,同时调动时* 的参数被提供给模拟函数。
         */
        return self.call((this instanceof bound ? this : context), ...finalArgs)
      }
    
    /**
     * 直接使用bound.prototype = self.prototype有一个缺点,直接修改bound.prototype的时候,也会直接修改self.prototype
     * 所以用一个空对象作为中介,将bound.prototype赋值为空对象的实例(原型式继承)
     */

    // 将调用函数的原型赋值到中转函数的原型上
    F.prototype = self.prototype

    // 通过原型的方式继承调用函数的原型
    bound.prototype = new F()

    return bound
}

深拷贝

js
//判断是否是对象类型
function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === "object" || valueType === "function")
}

function deepClone(originValue, map = new WeakMap()) {
  //一、进行判断
  // 判断是否是一个Set类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // 判断如果是Symbol的value, 那么创建一个新的Symbol
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description)
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 判断传入的originValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue
  }

  //解决循环引用
  if (map.has(originValue)) {
    return map.get(originValue)
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}

  map.set(originValue, newObject)//解决循环引用

  //二、值处理
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    newObject[sKey] = deepClone(originValue[sKey], map)
  }

  //三、返回新对象
  return newObject
}

柯里化

  1. 固定参数

    js
    function curry(fn, ...args) {
      /**
       * fn.length:  
        *  1. 函数的length是js函数对象的一个属性,函数的length代表形参的个数(即有多少必传参数)  
        *  2. 形参的数量不包括剩余参数的个数,仅包括“第一个具有默认值之前的参数个数”
        */
      return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
    }
    
    function add(a, b, c) {
      return a + b + c
    }
    
    const addCurry = curry(add)
    
    console.log(addCurry(1)(2)(3)) // 6
    console.log(addCurry(1, 2)(4)) // 7
  2. 不固定参数

    显示代码
    js
    function add() {
      // 第一次执行时,定义一个数组专门用来存储所有的参数
      let _args = Array.prototype.slice.call(arguments);
    
      // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
      let _adder = function() {
        _args.push(...arguments);
        if (arguments.length === 0) {
          return _args.reduce(function(a, b) {
            return a + b;
          });
        }
        return _adder;
      };
    
      // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
      // _adder.toString = function() {
      // 	return _args.reduce(function(a, b) {
      // 		return a + b;
      // 	});
      // }
      return _adder;
    }
    
    console.log(add(1)(2)(3)()) // 6
    console.log(add(1, 2, 3)(4)()) // 10
    
    // 利用隐式转换
    // console.log(add(1)(2)(3) == 6) // true

防抖

节流

Released under the MIT License.

本站访客数 148 人次 本站总访问量 220