博客文章头图

JS Insts

1375 字
3 分钟

工具函数

防抖与节流

/**
 * 防抖:仅执行一次
 */
const debounce = <F extends (...args: any[]) => any>(func: F, delay = 300) => {
  let timer: number | null = null;
  const ret = (...args: any) => {
  if (timer) {
    window.clearTimeout(timer);
  }
  timer = window.setTimeout(() => {
    func(...args);
    timer = null;
  }, delay);
  };
  return ret as (...args: Parameters<F>) => void;
};
element.onclick = debounce(myFunc);

/**
 * 节流:限制最小执行间隔
 */
const throttle = <F extends (...args: any[]) => any>(func: F, delay = 300) => {
  let timer: number | null = null;
  const ret = (...args: any) => {
  if (!timer) {
    timer = window.setTimeout(() => {
    func(...args);
    timer = null;
    }, delay);
  }
  };
  return ret as (...args: Parameters<F>) => void;
};
window.onresize = throttle(myFunc);

柯里化

将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)

/**
 * @param {Function} func
 * @return {Function}
 */
function curry(func) {
  return function curried(...args) {
  if (args.length >= func.length) {
    return func.apply(this, args);
  } else {
    return function (...args2) {
    return curried.apply(this, [...args, ...args2]);
    };
  }
  };
}

扁平化

数组

// 标准递归
function flatten(arr) {
  return arr.reduce((pre, cur) => {
  return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, []);
}

// 字符串分割
function flatten(arr) {
  return Array.prototype.toString
  .call(arr)
  .split(',')
  .map((val) => Number(val));
}

// 重复扩展直至扁平
function flatten(arr) {
  while (arr.some((val) => Array.isArray(val))) {
  arr = [...arr];
  }
  return arr;
}

对象

输入:

{
  a: 1,
  b: [1, 2, { c: true }, [3]],
  d: { e: 2, f: 3 },
  g: null,
}
/**
 * @param {Array} input
 * @param {string} name
 * @param {Object} res
 */
function _flatten(input, name, res) {
  for (let key of Object.keys(input)) {
  let baseName = name;
  const val = input[key];
  if (val === null || val === undefined) {
    continue;
  }
  if (/^[0-9]/.exec(`${key}`)) {
    baseName += `[${key}]`;
  } else if (baseName === '') {
    baseName += `${key}`;
  } else {
    baseName += `.${key}`;
  }
  if (typeof val === 'object') {
    _flatten(val, baseName, res);
  } else {
    res[baseName] = val;
  }
  }
}
/**
 * @param {Array} input
 * @return {Object}
 */
function flatten(input) {
  const res = {};
  _flatten(input, '', res);
  return res;
}

去重

Array.prototype.unique = function () {
  const arr = this;
  return arr.filter((val, idx) => arr.indexOf(val) === idx);
};

深拷贝

// 可以通过 WeakMap 解决循环引用问题,同时保证内存被回收
const map = new WeakMap();
function cloneDeep(src) {
  if (src && typeof src === 'object') {
  if (map.has(src)) {
    return map.get(src);
  } else {
    map.set(src, src);
  }
  const ret = Array.isArray(src) ? [] : {};
  for (let key of Object.keys(src)) {
    ret[key] = cloneDeep(src[key]);
  }
  return ret;
  } else {
  return src;
  }
}

Promise

Promise 构造函数

Promises/A+ 标准中仅指定了 Promise 对象的 then 方法的行为,其它一切我们常见的方法、函数都并没有指定。

class Promise {
  constructor(func) {
  // 初始化状态
  const self = this;
  this.status = 'pending';
  this.data = undefined;
  this.onResolvedCallbacks = [];
  this.onRejectedCallbacks = [];

  // API 函数
  function resolve(value) {
    if (self.status === 'pending') {
    self.status = 'resolved';
    self.data = value;
    self.onResolvedCallbacks.forEach((func) => {
      func();
    });
    }
  }
  function reject(reason) {
    if (self.status === 'pending') {
    self.status = 'rejected';
    self.data = reason;
    self.onRejectedCallbacks.forEach((func) => {
      func();
    });
    }
  }

  // 执行同步构造器
  try {
    func(resolve, reject);
  } catch (e) {
    reject(e);
  }
  }

  then(onResolved, onRejected) {
  // 检查参数
  if (typeof onResolved !== 'function') {
    onResolved = function () {};
  }
  if (typeof onRejected !== 'function') {
    onRejected = function () {};
  }

  // resolve 或 reject 则执行对应回调
  if (this.status === 'resolved') {
    return new Promise((resolve, reject) => {
    try {
      resolve(onResolved(this.data));
    } catch (e) {
      reject(e);
    }
    });
  }
  if (this.status === 'rejected') {
    return new Promise((resolve, reject) => {
    try {
      resolve(onRejected(this.data));
    } catch (e) {
      reject(e);
    }
    });
  }

  // pending 状态则等待 pending 完成
  if (this.status === 'pending') {
    return new Promise((resolve, reject) => {
    this.onResolvedCallbacks.push(() => {
      try {
      resolve(onResolved(this.data));
      } catch (e) {
      reject(e);
      }
    });
    this.onRejectedCallbacks.push(() => {
      try {
      resolve(onRejected(this.data));
      } catch (e) {
      reject(e);
      }
    });
    });
  }
  }
}

Polyfill Promise.all()

/**
 * @param {Array} arr
 * @return {Promise<any[]>}
 */
function promiseAll(arr) {
  return new Promise((resolve, reject) => {
  let pending = arr.length;
  const results = new Array(arr.length);

  arr.forEach((p, idx) => {
    p.then(
    (res) => {
      results[idx] = res;
      if (--pending === 0) {
      resolve(results);
      }
    },
    (e) => reject(e)
    );
  });
  });
}

Promise 并发限制

/**
 * @param {number} limit
 * @param {Array} arr
 * @param {Function} fetch
 * @return {Promise<any[]>}
 */
async function asyncPool(limit, arr, fetch) {
  const pending = [];
  const results = [];
  let index = 0; // 入池用下标

  // 入池一个
  async function push() {
  // 若已经全部进入池子则等待全部完成
  if (index >= arr.length) {
    return;
  }

  // 获得一个 Promise
  const p = fetch(arr[index]);
  pending.push(p);
  results.push(p);
  // Promise 完成后在 pending 中删除
  p.then(() => pending.splice(pending.indexOf(p), 1));

  if (pending.length >= limit) {
    await Promise.race(pending);
  }
  index++;
  await push();
  }

  await push();
  return await Promise.all(results);
}

原生 API

bind()

Function.prototype.bind = function (...args) {
  const func = this; // 需要绑定的函数
  const ctx = args[0]; // 绑定的 this
  const params = args.slice(1); // bind 时传入的参数
  return (...args) => func.apply(ctx, [...params, ...args]);
};

instanceof

function instanceOf(inst, func) {
  let proto = Object.getPrototypeOf(inst);
  while (true) {
  if (!proto) {
    return false;
  }
  if (proto === func.prototype) {
    return true;
  }
  proto = Object.getPrototypeOf(proto);
  }
}

new

JavaScript - 模拟实现 new

reduce() 实现 map()

Array.prototype.mapPolyfill = function (func, thisValue) {
  const self = thisValue || this;
  const ret = [];
  this.reduce((pre, cur, idx, arr) => {
  return ret.push(func.call(self, cur, idx, arr));
  }, ret);
  return ret;
};

语言特性

Iterable 对象

const object = {
  a: 1,
  b: 2,
  c: 3,
  // 本质是一个 Generator 函数
  *[Symbol.iterator]() {
  for (const key of Object.keys(this)) {
    yield this[key];
  }
  },
};
console.log(...object); // 1 2 3
object.d = 4;
console.log(...object); // 1 2 3 4

LazyMan (事件循环)

new LazyMan('Tony')
  .eat('lunch')
  .eat('dinner')
  .sleepFirst(5)
  .sleep(10)
  .eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

class LazyMan {
  constructor(name) {
  this.taskList = [];
  console.log(`Hi I am ${name}`);
  // 等待第一次事件循环
  // 即 tasklist 初始化完成
  setTimeout(() => {
    this.next();
  }, 0);
  }

  next() {
  const func = this.taskList.shift();
  func && func();
  }

  eat(food) {
  const func = () => {
    console.log(`I am eating ${food}`);
    this.next();
  };
  this.taskList.push(func);
  return this;
  }

  sleepFirst(time) {
  const func = () => {
    setTimeout(() => {
    console.log(`等待了${time}秒...`);
    this.next();
    }, time * 1000);
  };
  this.taskList.unshift(func);
  return this;
  }

  sleep(time) {
  const func = () => {
    setTimeout(() => {
    console.log(`等待了${time}秒...`);
    this.next();
    }, time * 1000);
  };
  this.taskList.push(func);
  return this;
  }
}
本站内容采用 CC BY-NC-SA 4.0 许可,请注明出处;商业转载请联系作者授权。