Различные реализации исходного кода, все, что вам нужно, здесь

внешний интерфейс

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

  • позвонить, подать заявку, привязать реализацию
  • новая реализация
  • класс реализует наследование
  • асинхронная/ожидающая реализация
  • сократить реализацию
  • Реализация двусторонней привязки данных
  • экземпляр реализации
  • Реализация Array.isArray
  • Основной принцип реализации Object.create
  • реализация getOwnPropertyNames
  • выполнение обещания
  • Напишите функцию защиты от сотрясения/дросселя вручную
  • Реализация каррирующих функций
  • почерк глубокая копия

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

call、apply、bindСуть в измененииthisнаправление, разныеcall、applyэто вызов функции напрямую,bindзаключается в возврате новой функции.callа такжеapplyТолько параметры разные.

привязать реализацию

  • стрелочная функцияthisвсегда указывайте на его масштаб
  • работать как конструкторnewключевое слово не должно менять своеthisточка, потому чтоnew绑定приоритет выше, чем显示绑定а также硬绑定
Function.prototype.mybind = function(thisArg) {
    if (typeof this !== 'function') {
      throw TypeError("Bind must be called on a function");
    }
    // 拿到参数,为了传给调用者
    const args = Array.prototype.slice.call(arguments, 1),
      // 保存 this
      self = this,
      // 构建一个干净的函数,用于保存原函数的原型
      nop = function() {},
      // 绑定的函数
      bound = function() {
        // this instanceof nop, 判断是否使用 new 来调用 bound
        // 如果是 new 来调用的话,this的指向就是其实例,
        // 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
        return self.apply(
          this instanceof nop ? this : thisArg,
          args.concat(Array.prototype.slice.call(arguments))
        );
      };

    // 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
    if (this.prototype) {
      nop.prototype = this.prototype;
    }
    // 修改绑定函数的原型指向
    bound.prototype = new nop();

    return bound;
  }
}
  1. 测试 mybind
const bar = function() {
  console.log(this.name, arguments);
};

bar.prototype.name = 'bar';

const foo = {
  name: 'foo'
};

const bound = bar.mybind(foo, 22, 33, 44);
new bound(); // bar, [22, 33, 44]
bound(); // foo, [22, 33, 44]

реализация вызова

bindупакованcallметод измененthisуказывает и возвращает новую функцию, затемcallкак внести измененияthisуказывая на это? Принцип очень простой, в режиме вызова метода,thisвсегда указывает на объект, для которого вызывается метод, для которого он вызывается,thisУказатель связан с позицией вызова метода, в котором он находится, и не имеет ничего общего с позицией объявления метода (функции со стрелками являются специальными). написать небольшойdemoчтобы понять.

const foo = { name: 'foo' };

foo.fn = function() {
  // 这里的 this 指向了 foo
  // 因为 foo 调用了 fn,
  // fn 的 this 就指向了调用它所在方法的对象 foo 上
  console.log(this.name); // foo
};

использоватьthisмеханизм достиженияcall

Function.prototype.mycall = function(thisArg) {
    // this指向调用call的对象
    if (typeof this !== 'function') {
      // 调用call的若不是函数则报错
      throw new TypeError('Error');
    }
    // 声明一个 Symbol 属性,防止 fn 被占用
    const fn = Symbol('fn')
    const args = [...arguments].slice(1);
    thisArg = thisArg || window;
    // 将调用call函数的对象添加到thisArg的属性中
    thisArg[fn] = this;
    // 执行该属性
    const result = thisArg[fn](...args);
    // 删除该属性
    delete thisArg[fn];
    // 返回函数执行结果
    return result;
  }

применить реализацию

Function.prototype.myapply = function(thisArg) {
  if (typeof this !== 'function') {
    throw this + ' is not a function';
  }

  const args = arguments[1];
  const fn = Symbol('fn')
  thisArg[fn] = this;

  const result = thisArg[fn](...arg);

  delete thisArg[fn];

  return result;
};

测试 mycall myapply

const bar = function() {
  console.log(this.name, arguments);
};

bar.prototype.name = 'bar';

const foo = {
  name: 'foo'
};

bar.mycall(foo, 1, 2, 3); // foo [1, 2, 3]
bar.myapply(foo, [1, 2, 3]); // foo [1, 2, 3]

Уменьшить принцип реализации

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

Array.prototype.myreduce = function reduce(callbackfn) {
  // 拿到数组
  const O = this,
    len = O.length;
  // 下标值
  let k = 0,
    // 累加器
    accumulator = undefined,
    // k下标对应的值是否存在
    kPresent = false,
    // 初始值
    initialValue = arguments.length > 1 ? arguments[1] : undefined;

  if (typeof callbackfn !== 'function') {
    throw new TypeError(callbackfn + ' is not a function');
  }

  // 数组为空,并且有初始值,报错
  if (len === 0 && arguments.length < 2) {
    throw new TypeError('Reduce of empty array with no initial value');
  }

  // 如果初始值存在
  if (arguments.length > 1) {
    // 设置累加器为初始值
    accumulator = initialValue;
    // 初始值不存在
  } else {
    accumulator = O[k];
    ++k;
  }

  while (k < len) {
    // 判断是否为 empty [,,,]
    kPresent = O.hasOwnProperty(k);

    if (kPresent) {
      const kValue = O[k];
      // 调用 callbackfn
      accumulator = callbackfn.apply(undefined, [accumulator, kValue, k, O]);
    }
    ++k;
  }

  return accumulator;
};

测试

const rReduce = ['1', null, undefined, , 3, 4].reduce((a, b) => a + b, 3);
const mReduce = ['1', null, undefined, , 3, 4].myreduce((a, b) => a + b, 3);

console.log(rReduce, mReduce);
// 31nullundefined34 31nullundefined34

новая реализация

нам нужно знать, когдаnewчто ты делал, когда

  1. создать новый объект;
  2. назначьте область конструктора новому объекту (так что это указывает на новый объект)
  3. Выполните код в конструкторе (добавьте свойства к этому новому объекту)
  4. Вернуть новый объект.

Поскольку new нельзя переопределить, мы используемmyNewфункция для имитацииnew

function myNew() {
  // 创建一个实例对象
  var obj = new Object();
  // 取得外部传入的构造器
  var Constructor = Array.prototype.shift.call(arguments);
  // 实现继承,实例可以访问构造器的属性
  obj.__proto__ = Constructor.prototype;
  // 调用构造器,并改变其 this 指向到实例
  var ret = Constructor.apply(obj, arguments);
  // 如果构造函数返回值是对象则返回这个对象,如果不是对象则返回新的实例对象
  return typeof ret === 'object' && ret !== null ? ret : obj;
}

测试 myNew

// ========= 无返回值 =============
const testNewFun = function(name) {
  this.name = name;
};

const newObj = myNew(testNewFun, 'foo');

console.log(newObj); // { name: "foo" }
console.log(newObj instanceof testNewFun); // true
// ========= 有返回值 =============
const testNewFun = function(name) {
  this.name = name;
  return {};
};

const newObj = myNew(testNewFun, 'foo');

console.log(newObj); // {}
console.log(newObj instanceof testNewFun); // false

класс реализует наследование

В основном используетсяes5а такжеes6СравниватьclassПринципы наследования

реализовать наследованиеA extends B

использоватьes6грамматика

class B {
  constructor(opt) {
    this.BName = opt.name;
  }
}
class A extends B {
  constructor() {
    // 向父类传参
    super({ name: 'B' });
    // this 必须在 super() 下面使用
    console.log(this);
  }
}

использоватьes5грамматика

Использование паразитного наследования композиции

  1. Защищенное цепное наследование, заставьте подкласс вызывать методы и свойства прототипа родительского класса.
  2. Используя наследование конструктора, вы можете передавать параметры родительскому классу.
  3. Паразитическое наследование, создание чистых функций без конструкторов, используемых для паразитирования на прототипе родительского класса.
// 实现继承,通过继承父类 prototype
function __extends(child, parent) {
  // 修改对象原型
  Object.setPrototypeOf(child, parent);
  // 寄生继承,创建一个干净的构造函数,用于继承父类的 prototype
  // 这样做的好处是,修改子类的 prototype 不会影响父类的 prototype
  function __() {
    // 修正 constructor 指向子类
    this.constructor = child;
  }
  // 原型继承,继承父类原型属性,但是无法向父类构造函数传参
  child.prototype =
    parent === null
      ? Object.create(parent)
      : ((__.prototype = parent.prototype), new __());
}

var B = (function() {
  function B(opt) {
    this.name = opt.name;
  }
  return B;
})();

var A = (function(_super) {
  __extends(A, _super);
  function A() {
    // 借用继承,可以实现向父类传参, 使用 super 可以向父类传参
    return (_super !== null && _super.apply(this, { name: 'B' })) || this;
  }
  return A;
})(B);

测试 class

const a = new A();

console.log(a.BName, a.constructor); // B ,ƒ A() {}

асинхронная/ожидающая реализация

Принцип заключается в использованииgenerator(Генератор) Разделить фрагменты кода. Затем мы используем функцию, позволяющую ей повторять себя, каждый разyieldиспользоватьpromiseЗаверните. Время выполнения следующего шага задаетсяpromiseконтролировать

async/awaitявляется ключевым словом, его метод нельзя переопределить, мы используем функцию для имитации

Асинхронная итерация, имитирующая асинхронные функции

function _asyncToGenerator(fn) {
  return function() {
    var self = this,
      args = arguments;
    // 将返回值promise化
    return new Promise(function(resolve, reject) {
      // 获取迭代器实例
      var gen = fn.apply(self, args);
      // 执行下一步
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      // 抛出异常
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      // 第一次触发
      _next(undefined);
    });
  };
}

Выполнение итерационных шагов и обработка результатов следующей итерации

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    // 迭代器完成
    resolve(value);
  } else {
    // -- 这行代码就是精髓 --
    // 将所有值promise化
    // 比如 yield 1
    // const a = Promise.resolve(1) a 是一个 promise
    // const b = Promise.resolve(a) b 是一个 promise
    // 可以做到统一 promise 输出
    // 当 promise 执行完之后再执行下一步
    // 递归调用 next 函数,直到 done == true
    Promise.resolve(value).then(_next, _throw);
  }
}

测试 _asyncToGenerator

const asyncFunc = _asyncToGenerator(function*() {
  const e = yield new Promise(resolve => {
    setTimeout(() => {
      resolve('e');
    }, 1000);
  });
  const a = yield Promise.resolve('a');
  const d = yield 'd';
  const b = yield Promise.resolve('b');
  const c = yield Promise.resolve('c');
  return [a, b, c, d, e];
});

asyncFunc().then(res => {
  console.log(res); // ['a', 'b', 'c', 'd', 'e']
});

Реализовать двустороннюю привязку

definePropertyВерсия

// 数据
const data = {
  text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
Object.defineProperty(data, 'text', {
  // 数据变化 --> 修改视图
  set(newVal) {
    input.value = newVal;
    span.innerHTML = newVal;
  }
});
// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
  data.text = e.target.value;
});

proxyВерсия

// 数据
const data = {
  text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
const handler = {
  set(target, key, value) {
    target[key] = value;
    // 数据变化 --> 修改视图
    input.value = value;
    span.innerHTML = value;
    return value;
  }
};
const proxy = new Proxy(data, handler);

// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
  proxy.text = e.target.value;
});

Основной принцип реализации Object.create

if (typeof Object.create !== "function") {
  Object.create = function (prototype, properties) {
    if (typeof prototype !== "object") { throw TypeError(); }
    function Ctor() {}
    Ctor.prototype = prototype;
    var o = new Ctor();
    if (prototype) { o.constructor = Ctor; }
    if (properties !== undefined) {
      if (properties !== Object(properties)) { throw TypeError(); }
      Object.defineProperties(o, properties);
    }
    return o;
  };
}

экземпляр реализации

принцип:Lиз__proto__Равен ли онR.prototype, не значит искатьL.__proto__.__proto__до__proto__дляnull

// L 表示左表达式,R 表示右表达式
function instance_of(L, R) {
  var O = R.prototype;
  L = L.__proto__;
  while (true) {
    if (L === null) return false;
    // 这里重点:当 O 严格等于 L 时,返回 true
    if (O === L) return true;
    L = L.__proto__;
  }
}

Реализация Array.isArray

Array.myIsArray = function(o) {
  return Object.prototype.toString.call(Object(o)) === '[object Array]';
};

console.log(Array.myIsArray([])); // true

реализация getOwnPropertyNames

Примечание. Невозможно получить неперечислимые свойства.

if (typeof Object.getOwnPropertyNames !== 'function') {
  Object.getOwnPropertyNames = function(o) {
    if (o !== Object(o)) {
      throw TypeError('Object.getOwnPropertyNames called on non-object');
    }
    var props = [],
      p;
    for (p in o) {
      if (Object.prototype.hasOwnProperty.call(o, p)) {
        props.push(p);
      }
    }
    return props;
  };
}

Обещание реализации

Принцип реализации: Фактически это модель публикации-подписки.

  1. Конструктор получаетexecutorфункционировать и будетnew Promise()немедленно выполнить функцию
  2. thenПри сборе зависимостей собирайте функции обратного вызова для成功/失败队列
  3. executorвызов функцииresolve/rejectфункция
  4. resolve/rejectОбратные вызовы в очереди триггера уведомляются при вызове функции

Сначала посмотрите на общий код, есть общая концепция

полный код

const isFunction = variable => typeof variable === 'function';

// 定义Promise的三种状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 构造函数,new 时触发
  constructor(handle: Function) {
    try {
      handle(this._resolve, this._reject);
    } catch (err) {
      this._reject(err);
    }
  }
  // 状态 pending fulfilled rejected
  private _status: string = PENDING;
  // 储存 value,用于 then 返回
  private _value: string | undefined = undefined;
  // 失败队列,在 then 时注入,resolve 时触发
  private _rejectedQueues: any = [];
  // 成功队列,在 then 时注入,resolve 时触发
  private _fulfilledQueues: any = [];
  // resovle 时执行的函数
  private _resolve = val => {
    const run = () => {
      if (this._status !== PENDING) return;
      this._status = FULFILLED;
      // 依次执行成功队列中的函数,并清空队列
      const runFulfilled = value => {
        let cb;
        while ((cb = this._fulfilledQueues.shift())) {
          cb(value);
        }
      };
      // 依次执行失败队列中的函数,并清空队列
      const runRejected = error => {
        let cb;
        while ((cb = this._rejectedQueues.shift())) {
          cb(error);
        }
      };
      /*
       * 如果resolve的参数为Promise对象,
       * 则必须等待该Promise对象状态改变后当前Promsie的状态才会改变
       * 且状态取决于参数Promsie对象的状态
       */
      if (val instanceof MyPromise) {
        val.then(
          value => {
            this._value = value;
            runFulfilled(value);
          },
          err => {
            this._value = err;
            runRejected(err);
          }
        );
      } else {
        this._value = val;
        runFulfilled(val);
      }
    };
    // 异步调用
    setTimeout(run);
  };
  // reject 时执行的函数
  private _reject = err => {
    if (this._status !== PENDING) return;
    // 依次执行失败队列中的函数,并清空队列
    const run = () => {
      this._status = REJECTED;
      this._value = err;
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(err);
      }
    };
    // 为了支持同步的Promise,这里采用异步调用
    setTimeout(run);
  };
  // then 方法
  then(onFulfilled?, onRejected?) {
    const { _value, _status } = this;
    // 返回一个新的Promise对象
    return new MyPromise((onFulfilledNext, onRejectedNext) => {
      // 封装一个成功时执行的函数
      const fulfilled = value => {
        try {
          if (!isFunction(onFulfilled)) {
            onFulfilledNext(value);
          } else {
            const res = onFulfilled(value);
            if (res instanceof MyPromise) {
              // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
              res.then(onFulfilledNext, onRejectedNext);
            } else {
              //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
              onFulfilledNext(res);
            }
          }
        } catch (err) {
          // 如果函数执行出错,新的Promise对象的状态为失败
          onRejectedNext(err);
        }
      };

      // 封装一个失败时执行的函数
      const rejected = error => {
        try {
          if (!isFunction(onRejected)) {
            onRejectedNext(error);
          } else {
            const res = onRejected(error);
            if (res instanceof MyPromise) {
              // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
              res.then(onFulfilledNext, onRejectedNext);
            } else {
              //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
              onFulfilledNext(res);
            }
          }
        } catch (err) {
          // 如果函数执行出错,新的Promise对象的状态为失败
          onRejectedNext(err);
        }
      };

      switch (_status) {
        // 当状态为pending时,将then方法回调函数加入执行队列等待执行
        case PENDING:
          this._fulfilledQueues.push(fulfilled);
          this._rejectedQueues.push(rejected);
          break;
        // 当状态已经改变时,立即执行对应的回调函数
        case FULFILLED:
          fulfilled(_value);
          break;
        case REJECTED:
          rejected(_value);
          break;
      }
    });
  }
  // catch 方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
  // finally 方法
  finally(cb) {
    return this.then(
      value => MyPromise.resolve(cb()).then(() => value),
      reason =>
        MyPromise.resolve(cb()).then(() => {
          throw reason;
        })
    );
  }
  // 静态 resolve 方法
  static resolve(value) {
    // 如果参数是MyPromise实例,直接返回这个实例
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
  // 静态 reject 方法
  static reject(value) {
    return new MyPromise((resolve, reject) => reject(value));
  }
  // 静态 all 方法
  static all(list) {
    return new MyPromise((resolve, reject) => {
      // 返回值的集合
      let values = [];
      let count = 0;
      for (let [i, p] of list.entries()) {
        // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
        this.resolve(p).then(
          res => {
            values[i] = res;
            count++;
            // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
            if (count === list.length) resolve(values);
          },
          err => {
            // 有一个被rejected时返回的MyPromise状态就变成rejected
            reject(err);
          }
        );
      }
    });
  }
  // 添加静态race方法
  static race(list) {
    return new MyPromise((resolve, reject) => {
      for (let p of list) {
        // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
        this.resolve(p).then(
          res => {
            resolve(res);
          },
          err => {
            reject(err);
          }
        );
      }
    });
  }
}

Стабилизатор/дросселирование

防抖函数Запускается один раз в конце onscroll, отложенное выполнение

function debounce(func, wait) {
  let timeout;
  return function() {
    let context = this;
    let args = arguments;
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}
// 使用
window.onscroll = debounce(function() {
  console.log('debounce');
}, 1000);

节流函数При прокрутке время от времени срабатывает, как капля воды.

function throttle(fn, delay) {
  var prevTime = Date.now();
  return function() {
    var curTime = Date.now();
    if (curTime - prevTime > delay) {
      fn.apply(this, arguments);
      prevTime = curTime;
    }
  };
}
// 使用
var throtteScroll = throttle(function() {
  console.log('throtte');
}, 1000);
window.onscroll = throtteScroll;

Реализация каррирования функций

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

Практическое применение:

  • Отложенный расчет (используйте замыкание для сохранения входящих параметров и запускайте выполнение функции, когда количество входящих параметров достаточно для выполнения функции)
  • Создать функцию динамически (если параметров недостаточно, она вернет функцию, которая принимает оставшиеся параметры)
  • Повторное использование параметра (каждый параметр можно использовать несколько раз)
const curry = fn =>
  (judge = (...args) =>
    args.length >= fn.length
      ? fn(...args)
      : (...arg) => judge(...args, ...arg));

const sum = (a, b, c, d) => a + b + c + d;
const currySum = curry(sum);

currySum(1)(2)(3)(4); // 10
currySum(1, 2)(3)(4); // 10
currySum(1)(2, 3)(4); // 10

почерк глубокая копия

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

Это слишком сложно, чтобы реализовать совершенную копию Бога. Позвольте мне кратко представить это здесь. Это может быть применено к большинству сценариев.

Тип решения Функция

function getType(obj) {
  const str = Object.prototype.toString.call(obj);
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  };
  if (obj instanceof Element) {
    // 判断是否是dom元素,如div等
    return 'element';
  }
  return map[str];
}

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

function deepCopy(ori) {
  const type = getType(ori);
  let copy;
  switch (type) {
    case 'array':
      return copyArray(ori, type, copy);
    case 'object':
      return copyObject(ori, type, copy);
    case 'function':
      return copyFunction(ori, type, copy);
    default:
      return ori;
  }
}

function copyArray(ori, type, copy = []) {
  for (const [index, value] of ori.entries()) {
    copy[index] = deepCopy(value);
  }
  return copy;
}

function copyObject(ori, type, copy = {}) {
  for (const [key, value] of Object.entries(ori)) {
    copy[key] = deepCopy(value);
  }
  return copy;
}

function copyFunction(ori, type, copy = () => {}) {
  const fun = eval(ori.toString());
  fun.prototype = ori.prototype
  return fun
}

Несколько последних мелочей

  1. Если вы хотите присоединиться к группе, чтобы изучить расширенный интерфейс, добавьте меня в WeChat.luoxue2479Ответьте, чтобы присоединиться к группе
  2. Если есть ошибки, укажите на них в области сообщений, обсудите вместе или добавьте меня в WeChat.
  3. Обсуждения в группе будут каждый деньGitHub.com/falling snow-Вик Т…
  4. Мой официальный аккаунт [Front-end Technician], давайте учиться вместе.

Справочная статья

cloud.Tencent.com/developer/ ах…

woo woo Краткое описание.com/fear/no 4 отправить 0425 нет 2…

blog.CSDN.net/LL187811327…