常用js手写技巧总结

126 阅读2分钟

手动实现 new

创建一个新对象,这个对象的proto要指向构造函数的原型对象 执行构造函数 返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象

function _new() {
  let obj = {};
  let [constructor, ...args] = [...arguments];
  obj.__proto__ = constructor.prototype;
  let result = constructor.apply(obj, args);
  if ((result && typeof result === "function") || typeof result === "object") {
    return result;
  }
  return obj;
}

实现 instandof

instanceof 运算符主要是用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

function _instanceof(leftValue, rightValue) {
  let rightValue = rightValue.prototype; // 取得当前类的原型
  let proto = Object.getPrototypeOf(leftValue); // 取得当前实例对象的原型链上的属性

  while (true) {
    if (proto === null) {
      // 找到了 Object的基类 Object.prototype.__proto__
      return false;
    }
    if (proto === rightValue) {
      // 在当前实例对象的原型链上,找到了当前类
      return true;
    }
    proto = Object.getPrototypeOf(proto); // 沿着原型链__ptoto__一层一层向上查找
  }
}

实现 call 和 apply

改变 this 指向,唯一区别就是传递参数不同

// 实现call
Function.prototype.call = function (context, ...args) {
  //  null,undefined,和不传时,context为 window
  context = context == null ? window : context;
  // 必须保证 context 是一个对象类型
  let contextType = typeof context;
  if (!/^(object|function)$/i.test(contextType)) {
    // context = new context.constructor(context); // 不适用于 Symbol/BigInt
    context = Object(context);
  }

  let result;
  context["fn"] = this; // 把函数作为对象的某个成员值
  result = context["fn"](...args); // 把函数执行,此时函数中的this就是
  delete context["fn"]; // 设置完成员属性后,删除
  return result;
};

// 实现apply
Function.prototype.apply = function (context, args) {
  context = context == null ? window : context;

  let contextType = typeof context;
  if (!/^(object|function)$/i.test(contextType)) {
    context = Object(context);
  }

  let result;
  context["fn"] = this;
  result = context["fn"](...args);
  delete context["fn"];
  return result;
};

实现 bind

bind 它并不是立马执行函数,而是有一个延迟执行的操作,就是生成了一个新的函数,需要你去执行它

Function.prototype.mybind = function (context, ...args) {
  return (...newArgs) => {
    return this.call(context, ...args, ...newArgs);
  };
};

实现数组的扁平化 flat

  • ES6 flat 方法 arr.flat()
  • reduce 实现
function flatten(arr) {
  return arr.reduce((result, item) => {
    return result.concat(Array.isArray(item) ? flatten(item) : item);
  }, []);
}
  • 扩展运算符
function flatten(arr) {
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}

实现数据去重

  • ES6 set 方法 js const newArr = [...new Set(arr)]

  • filter 方法

    function unique1(arr) {
      const newArr = arr.filter((item, index, arr) => {
        return arr.indexOf(item) === index;
      });
    
      return newArr;
    }
    
    • reduce 方法
    function unique2(arr) {
      const newArr = arr.reduce((pre, cur) => {
        pre.includes(cur) ? pre : [...pre, cur];
      }, []);
      return newArr;
    }
    
    • 利用对象的键唯一性

      function unique3(arr) {
        let obj = {};
        const newArr = arr.filter((item) => {
          return obj.hasOwnProperty(item)
            ? false
            : (item] = true);
        });
        return newArr
      }
      

实现函数函数柯里化

函数柯里化就是把接受「多个参数」的函数变换成接受一个「单一参数」的函数,并且返回接受「余下参数」返回结果的一种应用。 所以我们可以首先判断传递的参数是否达到执行函数的 fn 个数如果没有达到的话 继续返回新的函数 并返回 curry 函数传递剩余参数

function curry(fn, ...args) {
  fn.length > args.length
    ? (...arguments) => curry(fn, ...args, ...arguments)
    : fn(...args);
}

实现 sleep 函数

某个时间过后,就去执行某个函数,基于 Promise 封装异步任务

function sleep(fn, wait) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(fn);
    });
  });
}

实现一个简单的发布订阅

class EventEmitter {
  constructor() {
    // 事件对象,存放订阅的名字和事件
    this.events = {};
  }
  // 订阅事件的方法
  on(eventName, callback) {
    if (!this.events[eventName]) {
      // 注意数据,一个名字可以订阅多个事件函数
      this.events[eventName] = [callback];
    } else {
      // 存在则push到指定数组的尾部保存
      this.events[eventName].push(callback);
    }
  }
  // 触发事件的方法
  emit(eventName) {
    // 遍历执行所有订阅的事件
    this.events[eventName] && this.events[eventName].forEach((cb) => cb());
  }
}