О некоторых важных реализациях API в JS, закрепите свои собственные навыки JS

JavaScript

На собеседовании мы часто сталкиваемся с некоторыми вопросами интервью, такими как написанное от руки XXX, поэтому хорошее резюме очень необходимо, чтобы закрепить основу нашего нативного js.

Хотя сводных статей в интернете уже много, есть общая проблема, на мой взгляд, — чрезмерное усложнение принципа. С точки зрения интервьюера, его цель — проверить понимание интервьюируемым языка JS в кратчайшие сроки, но, прочитав множество сводных статей на сайте, я обнаружил, что большая часть кода имеет смысл. , такие как реализация простой защиты от сотрясений, если это основной дисплей процесса, что касается других режимов, нет необходимости копать глубже, и многие «если-иначе» заставляют людей выглядеть ослепительно. запоминать код напрямую.Кроме того, основная логика может быть отображена, и может не быть проблемой реализовать другие подобные ситуации по горизонтали.

В следующем порядке рекомендуется написать его самостоятельно в соответствии с основными моментами, а затем сравнить следующий код, эффект обзора лучше. Цель этой статьи — помочь вам понять внутреннюю работу API с точки зрения первых принципов с помощью наиболее лаконичного кода.Любые крайние случаи, которые не помогают нам понять API, не обрабатываются..

Во-первых, используйте ES5 для реализации метода отображения массивов.

Основные моменты:

1. Каковы параметры callback-функции и как работать с возвращаемым значением.

2. Не изменяйте исходный массив.

Array.prototype.MyMap = function(fn, context){
  var arr = Array.prototype.slice.call(this);//由于是ES5所以就不用...展开符了
  var mappedArr = [];
  for (var i = 0; i < arr.length; i++ ){
    mappedArr.push(fn.call(context, arr[i], i, this));
  }
  return mappedArr;
}

Во-вторых, используйте ES5 для реализации метода уменьшения массива.

Основные моменты:

1. Как быть с не переданным начальным значением

2. Каковы параметры callback-функции и как работать с возвращаемым значением.

Array.prototype.myReduce = function(fn, initialValue) {
  var arr = Array.prototype.slice.call(this);
  var res, startIndex;
  res = initialValue ? initialValue : arr[0];
  startIndex = initialValue ? 0 : 1;
  for(var i = startIndex; i < arr.length; i++) {
    res = fn.call(null, res, arr[i], i, this);
  }
  return res;
}

3. Реализовать вызов/применить

Идея: используйте функцию контекста this.

//实现apply只要把下一行中的...args换成args即可 
Function.prototype.myCall = function(context = window, ...args) {
  let func = this;
  let fn = Symbol("fn");
  context[fn] = func;

  let res = context[fn](...args);//重点代码,利用this指向,相当于context.caller(...args)

  delete context[fn];
  return res;
}

В-четвертых, реализуйте метод Object.create (обычно используется)

function create(proto) {
    function F() {};
    F.prototype = proto;
    F.prototype.constructor = F;
    
    return new F();
}

5. Реализуйте метод привязки

Основные моменты:

1. Для обычных функций привяжите this, чтобы указать на

2. Для конструктора убедитесь, что свойства объекта-прототипа исходной функции не могут быть потеряны.

Function.prototype.bind = function(context, ...args) {
    let self = this;//谨记this表示调用bind的函数
    let fBound = function() {
        //this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)
        return self.apply(this instanceof fBound ? this : context || window, args.concat(Array.prototype.slice.call(arguments)));
    }
    fBound.prototype = Object.create(this.prototype);//保证原函数的原型对象上的属性不丢失
    return fBound;
}

Рукописный бинд, о котором все обычно говорят, на самом деле так прост :)

6. Внедрите новое ключевое слово

Основные моменты:

  1. Создайте новый объект, __proto__ этого объекта должен указывать на объект-прототип конструктора.
  2. выполнить конструктор
  3. Возвращаемое значение типа объекта возвращается как возвращаемое значение нового метода, в противном случае возвращается указанный выше новый объект.
function myNew(fn, ...args) {
    let instance = Object.create(fn.prototype);
    let res = fn.apply(instance, args);
    return typeof res === 'object' ? res: instance;
}

7. Осознайте роль 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);
    }
}

8. Реализуйте одноэлементный шаблон

Основной вывод: перехват с помощью замыканий и свойств прокси

function proxy(func) {
    let instance;
    let handler = {
        construct(target, args) {
            if(!instance) {
                instance = Reflect.construct(func, args);
            }
            return instance;
        }
    }
    return new Proxy(func, handler);
}

Девять, осознайте плоскость массива

На самом деле есть много способов. Я уже делал системную сортировку. Есть шесть способов, пожалуйста, обратитесь к:

Краткое описание метода выравнивания (плоского) массива JS

10. Реализуйте функцию защиты от сотрясений

Основные моменты:

Если он снова срабатывает в пределах временных рамок таймера, он повторяется.

const debounce = (fn, delay) => {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
};

11. Реализуйте функцию дросселирования

Основные моменты:

Если он снова срабатывает в пределах временного диапазона таймера, он игнорируется, и следующий таймер может быть запущен после завершения текущего таймера.

const throttle = (fn, delay = 500) => {
  let flag = true;
  return (...args) => {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, args);
      flag = true;
    }, delay);
  };
};

12. Реализуйте EventEmit с режимом публикации-подписки

Обратитесь к другой моей статье:

Собственный пакет подключаемых модулей JS на основе «публикация-подписка»Рукописный раздел публикации-подписки в формате .

Тринадцать, реализовать глубокую копию

Ниже приведен упрощенный вариант глубокой копии, без учета ситуации с циклическими ссылками и обработкой Buffer, Promise, Set и Map.Если их реализовывать по одному, то это слишком сложно, и нереально напишите их в небольшом интервью.Если вам интересно, вы можете перейти сюда для углубленного внедрения:

Окончательное исследование Deep Copy.

const clone = parent => {
  // 判断类型
  const isType =  (target, type) => `[object ${type}]` === Object.prototype.toString.call(target)

  // 处理正则
  const getRegExp = re => {
    let flags = "";
    if (re.global) flags += "g";
    if (re.ignoreCase) flags += "i";
    if (re.multiline) flags += "m";
    return flags;
  };

  const _clone = parent => {
    if (parent === null) return null;
    if (typeof parent !== "object") return parent;

    let child, proto;

    if (isType(parent, "Array")) {
      // 对数组做特殊处理
      child = [];
    } else if (isType(parent, "RegExp")) {
      // 对正则对象做特殊处理
      child = new RegExp(parent.source, getRegExp(parent));
      if (parent.lastIndex) child.lastIndex = parent.lastIndex;
    } else if (isType(parent, "Date")) {
      // 对Date对象做特殊处理
      child = new Date(parent.getTime());
    } else {
      // 处理对象原型
      proto = Object.getPrototypeOf(parent);
      // 利用Object.create切断原型链
      child = Object.create(proto);
    }
    for (let i in parent) {
      // 递归
      child[i] = _clone(parent[i]);
    }
    return child;
  };
  return _clone(parent);
};

14. Выполнение обещаний

Ключевые моменты сложнее и сложнее.Пожалуйста, обратитесь к другой моей пошаговой статье по разборке:

Как реализовать промисы

15. Используйте ES5 для достижения наследования классов

Это тоже ключевое знание. Я уже делал детальную разборку ранее. Есть пять версий. Если каждую версию можно объяснить понятно, это вполне может отражать мое понимание цепочки прототипов. Адрес статьи:

Вещи, которые ES5 реализует наследование