Published on

手写原理实现

Authors

new的原理

new是javascript的一个关键词,用来创建对象的实例,它的实现思路如下:

创建一个空对象obj 将obj象的原型指向构造函数的prototype 执行构造函数,将obj作为其运行时的this 如果构造函数返回了一个对象,则用这个对象作为new的结果,反之则返回obj

function myNew(fn, ...args) {
  const obj = Object.create(fn.prototype);
  const res = fn.apply(obj, args);
  return typeof res === 'object' ? res : obj;
}

instanceof原理

instanceof的主要作用是判断某个实例是否属于某种类型,也可以判断一个实例是否是其父类型或者祖先类型的实例。

实现思路:通过原型链往上一级一级查找,直到找到和当前对象的原型相等的原型。

function myInstanceof(left, right) {
  let proto = Object.getPrototypeOf(left);
  while (true) {
    if (proto === null) return false;
    if (proto === right.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}

Object.create()原理

实现思路:传入一个对象,以该对象作为原型构造出一个新对象。

function myCreate(proto) {
  if (typeof proto !== 'object') {
    throw new TypeError('Object prototype may only be an Object or null');
  }
  function F() {}
  F.prototype = proto;
  return new F();
}

实现一个发布订阅模式 EventEmitter

通过初始化一个events对象保存事件和监听函数的映射关系 on:监听事件,将事件名eventName和回调函数 fn 存入events中 emit:触发事件,通过eventName事件名取出对应的回调函数并执行 off:取消监听,通过eventName事件名和找到对应的回调函数fn并将其移除 once:只触发一次,在触发fn时,调用off方法将其移除

class EventEmitter {
    constructor() {
        this.events = {}
    }
    on(eventName, fn) {
        this.events[eventName] ? this.events[eventName].push(fn) : (this.events[eventName] = [fn])
        return this
    }
    emit(eventName) {
        (this.events[eventName] || []).forEach(fn => fn())
        return this
    }
    off(eventName, fn) {
        if (this.events[eventName]) {
                this.events[eventName] = fn ? this.events[eventName].filter(item => item !== fn) : []
            }
        return this
    }
    once(eventName, callback) {
        const fn = () => {
            callback()
            this.off(eventName, fn)
        }
        this.on(eventName, fn)
    }
}


reduce原理

实现思路:

如果传入了初始值 initialValue,那么就将此值作为调用回调函数的第一个入参,然后循环的索引 i 从0开始 未传入初始值 initialValue,将数组的第一项作为 initialValue,然后循环的索引 i 从1开始 从 i 开始循环到 数组的length - 1,将上一次的输出作为下一次循环的第一个参数

function myReduce(arr, callback, initialValue) {
  let i = 0;
  let result = initialValue;
  if (initialValue === undefined) {
    result = arr[0];
    i = 1;
  }
  for (; i < arr.length; i++) {
    result = callback(result, arr[i], i, arr);
  }
  return result;
}

call/apply/bind原理

call、apply和bind都是用于改变函数this,call和apply的区别是传入的参数上有差异,apply只有两个参数,第二个参数是一个数组,call则是可以传入N个参数,第二个参数到第N个参数会当做参数传递给调用的fn,而bind与它们的区别是bind是静态绑定 。 实现思路:

bind:将当前函数赋值给传入的上下文context的某个属性,比如fn,然后通过context.fn调用 apply:除了参数的处理不一样,其它和bind相同 bind: 返回一个新函数,在新函数里面调用原函数,并将this指向它


// call
Function.prototype.myCall = function (context, ...args){
  context = context || window
  context.fn = this
  const res = context.fn(...args)
  delete context.fn
  return res
}


// apply
Function.prototype.myApply = function (context, arg){
  context = context || window
  context.fn = this
  let res
  if (Array.isArray(arg)) {
    res = context.fn(...arg)
  } else {
    res = context.fn()
  }
  delete context.fn
  return res
}



// bind
Function.prototype.myBind = function (context, ...args){
  const _this = this
  return function F(){
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, [...args, ...arguments])
  }
}

map

数组的map方法。

实现思路:

  1. 创建一个空数组,用于存储映射后的结果
  2. 遍历数组,对每个元素执行回调函数,并将结果添加到新数组中
  3. 返回新数组

function myMap(arr, callback) {
  const result = []
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i], i, arr))
  }
  return result
}