js高频手撕面试题(简单好记)

AI 概述
1. 原生 Ajax 请求2. Apply3. Call4. Create5. Currying6. Debounce7. Deduplication8. Deepclone9. Eventmitter10. Extend11. Flatten12. Insatnceof13. JSONP14. New15. Promise16. Reduce17. Throttle 1. 原生 Ajax 请求 const getQueriesByObj = (object = {}, prefix = '') => Object.entries(...
目录
文章目录隐藏
  1. 1. 原生 Ajax 请求
  2. 2. Apply
  3. 3. Call
  4. 4. Create
  5. 5. Currying
  6. 6. Debounce
  7. 7. Deduplication
  8. 8. Deepclone
  9. 9. Eventmitter
  10. 10. Extend
  11. 11. Flatten
  12. 12. Insatnceof
  13. 13. JSONP
  14. 14. New
  15. 15. Promise
  16. 16. Reduce
  17. 17. Throttle

1. 原生 Ajax 请求

const getQueriesByObj = (object = {}, prefix = '') => Object.entries(object)
  .reduce((prev, [key, value]) => prev += `${key}=${value}&`, prefix)
  .slice(0, -1);

const ajax = (type = 'get') => (url = '', data, extraProps = {}) =>
  new Promise((resolve, reject) => {
    const defaultProps = { async: true, 'Content-type': 'application/x-www-form-urlencoded' };
    const props = { ...defaultProps, ...extraProps };
    const { async, baseUrl, queries = {} } = props;
    const xhr = new XMLHttpRequest();
    // 获取完整的请求 url
    if (!url.includes('http')) {
      url = `${baseUrl || window.location.origin}${url}`;
    }
    if (type === 'get') {
      url = getQueriesByObj(queries, `${url}?`)
    }
    xhr.open(type, url, async);
    Object.entries(extraProps)
      .forEach(([key, value]) => xhr.setRequestHeader(key, value.toString()));
    // get or post
    data
      ? xhr.send(data)
      : xhr.send();
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          return resolve(xhr.response);
        }
        return reject(xhr.response);
      }
    }
  });

const testUrl = 'https://www.fastmock.site/mock/3410de68d1e03849d328e0b0651c4f1f/api/api/services/app/TransactionBill/GetTransactionBillInfo';
// const ajaxGet = ajax('get');
const ajaxPost = ajax('post');

ajaxPost(testUrl).then(console.log)
ajax('post')(testUrl).then(console.log)

2. Apply

Function.prototype.myApply = function (...args) {
  if (typeof this !== 'function') {
    throw new TypeError('Error');
  }
  let [context, params] = args;
  context = context || window;
  context.fn = this;
  const result = params ? context.fn(...params) : context.fn();
  delete context.fn;
  return result;
}

3. Call

Function.prototype.myCall = function (...args) {
  if (typeof this !== 'function') {
    throw new TypeError('Error');
  }
  let [context, ...params] = args;
  context = context || window;
  context.fn = this;
  const result = context.fn(...params);
  delete context.fn;
  return result;
}

关于 apply 与 call 详细知识点,推荐阅读《实例讲解 js 中的 call() apply() bind()的用法和区别

4. Create

const create = (proto, propertiesObject) => {
  // 小写驼峰
  const headToLowerCase = v => v.replace(/(^[A-Z])/g, (m, p1) => p1.toLowerCase());
  // 类型
  const is = (val, compareType) => {
    const type = Object.prototype.toString.call(val)
      .replace(/^(\[object+\s)([\S]+)(\]$)/g, (m, p1, p2) => headToLowerCase(p2));
    // [object, 'Array'] => array
    return compareType
      ? (headToLowerCase(compareType) === type)
      : type;
  };
  const generateObj = prototype => {
    const obj = {};
    obj.__proto__ = prototype;
    // setProperty to obj
    Object.entries(propertiesObject).forEach(([key, value]) => is(value, 'object') && Object.defineProperty(obj, key, value));
    return obj;
  }
  const type = is(proto);
  const actions = {
    object: () => generateObj(proto),
    function: () => generateObj(proto),
    null: () => new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument."),
    default: () => new TypeError('Object prototype may only be an Object: ' + proto),
  }
  return actions[type]();
}

const testObj = create({ a: 1, b: 2 }, {
  c: {
    value: 3,
    enumerable: true,
    configurable: true,
    writable: true,
  }
});
console.log('testObj', testObj);

5. Currying

// 连续求和
const add = (...arg) => arg.reduce((prev, curr) => curr += prev, 0);
const currying = (fn, ...prevArgs) => (...currArgs) => currArgs.length
  ? currying.call(null, fn, ...prevArgs, ...currArgs)
  : fn(...prevArgs);
const curryingAdd = currying(add);
curryingAdd(1, 2)(3)(4)();
curryingAdd(1, 2)(3, 4)();
curryingAdd(1)(2)(3)(4)();

// 个性化 log
const coloring = fn => ({ background, color = 'white' }) => (...text) => fn(`%c${text.join('')}`, `color:${color};background:${background}`);
const colors = {
  primary: '#007bff',
  success: '#28a745',
  warning: '#ffc107',
  danger: '#dc3545',
  info: '#17a2b8',
};
const dir = (key = '', value = {}) => {
  logs.primary(`++++++++++++start:${key}++++++++++++++`);
  console.dir(value);
  logs.primary(`++++++++++++end:${key}++++++++++++++`);
};
const logs = Object.keys(colors)
  .reduce((prev, curr) => ({
    ...prev,
    [curr]: coloring(console.log)({ background: colors[curr] })
  }), { dir });

logs.warning('warning');

6. Debounce

HTML 代码:

<h3>Try entering something...</h3>

<input />
<p class="normal">
  Normal input:
  <span class="text"></span>
</p>
<p class="debounced">
  Debounced input:
  <span class="text"></span>
</p>

css 代码:

.normal {
  .text {
    color: pink;
  }
}

.debounced {
  .text {
    color: green;
  }
}

JS 代码:

const $inupt = $('input');
const $normalText = $('.normal .text');
const $debouncedText = $('.debounced .text');
const debounce = (fn, delay = 300) => {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  }
}

$inupt.on('keyup', function(e) {
  $normalText.text(this.value)
})

$inupt.on('keyup', debounce(function(e) {
  $debouncedText.text($inupt.val())
}))

7. Deduplication

// 1. 使用 set
const deduplication = arr => [...new Set(arr)];
const testArr = [1, 1, 2, 3, 4, 5];
deduplication(testArr);// [1,2,3,4,5]
const testArr2 = [1, 1, 2, 3, 4, 5].map(item => ({ id: item })); // [{"id":1},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]

const deduplication02 = (array, key = '') => {
  const actions = {
    object: (arr, k) => arr.reduce((prev, curr) => prev.filter(item => item[k] === curr[k]).length ? prev : [...prev, curr], []),
    plain: arr => [...new Set(arr)]
  };
  return key ? actions.object(array, key) : actions.plain(array);
};

deduplication02(testArr2, 'id'); // [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]

8. Deepclone

// deepClone 的关键在于切断引用数据类型的指向

// 低嵌套的 copy
const copiedArr = arr => arr.slice();

const deepClone = value => {
  // 小写驼峰
  const headToLowerCase = v => v.replace(/(^[A-Z])/g, (m, p1) => p1.toLowerCase());
  // 类型
  const is = (val, compareType) => {
    const type = Object.prototype.toString.call(val)
      .replace(/^(\[object+\s)([\S]+)(\]$)/g, (m, p1, p2) => headToLowerCase(p2));
    // [object, 'Array'] => array
    return compareType
      ? (headToLowerCase(compareType) === type)
      : type;
  };
  const type = is(value);
  // 开始 copy
  const actions = {
    // 基本数据类型
    string: val => new String(val).valueOf(),
    number: val => new Number(val).valueOf(),
    boolean: val => new Boolean(val).valueOf(),
    null: val => null,
    undefined: val => undefined,
    // 引用数据类型
    array: val => val.map(item => deepClone(item)),
    object: val => Object.entries(val).reduce((pre, [k, v]) => ({
      ...pre,
      [k]: deepClone(v),
    }), {}),
    // 其它数据类型
    regexp: (val) => new RegExp(val).valueOf(),
    date: (val) => new Date(val).valueOf(),
    htmlBodyElement: val => val.cloneNode(),
    // todo 数据类型没有完全
    default: val => val,
  };
  const action = actions[type] || actions.default;
  return action(value);
};

9. Eventmitter

class EventEmitter {
  constructor() {
    this.events = {};
  }

  sub(type, event) {
    this.events[type] = this.events[type] || [];
    this.events[type].push(event);
  }

  pub(type) {
    const events = this.events[type] || [];
    events.forEach(event => event && event());
  }

  unsub(type, event) {
    this.events[type] = this.events[type] || [];
    this.events[type] = this.events[type].filter(evt => evt !== event);
  }
}

10. Extend

const extend = (...args) => {
  const [target = {}, ...objs] = args;
  const headToLowerCase = v => v.replace(/(^[A-Z])/g, (m, p1) => p1.toLowerCase());
  // 类型
  const is = (val, compareType) => {
    const type = Object.prototype.toString.call(val)
      .replace(/^(\[object+\s)([\S]+)(\]$)/g, (m, p1, p2) => headToLowerCase(p2));
    // [object, 'Array'] => array
    return compareType
      ? (headToLowerCase(compareType) === type)
      : type;
  };
  const deepClone = value => {
    // 小写驼峰
    const type = is(value);
    // 开始 copy
    const actions = {
      // 基本数据类型
      string: val => new String(val).valueOf(),
      number: val => new Number(val).valueOf(),
      boolean: val => new Boolean(val).valueOf(),
      null: val => null,
      undefined: val => undefined,
      // 引用数据类型
      array: val => val.map(item => deepClone(item)),
      object: val => Object.entries(val).reduce((pre, [k, v]) => ({
        ...pre,
        [k]: deepClone(v),
      }), {}),
      // 其它数据类型
      regexp: (val) => new RegExp(val).valueOf(),
      date: (val) => new Date(val).valueOf(),
      htmlBodyElement: val => val.cloneNode(),
      // todo 数据类型没有完全
      default: val => val,
    };
    const action = actions[type] || actions.default;
    return action(value);
  };
  return objs.length
    ? objs.reduce((prev, curr) => is(curr, 'object') ? ({ ...prev, ...deepClone(curr) }) : prev, target)
    : target
}
// test
extend({ a: 1 }, { b: 2, c: { a: 1 } });

11. Flatten

const flatten = arr => arr.reduce((prev, curr) => Array.isArray(curr)
  ? [...prev, ...flatten(curr)]
  : [...prev, curr],
  []);

12. Insatnceof

const my_instanceof = (left, right) => {
  const rightPrototype = right.prototype;
  let leftProto = left.__proto__;
  if (leftProto === null) {
    return false;
  }
  if (leftProto === rightPrototype) {
    return true;
  }
  return my_instanceof(leftProto, right);
}

// test
function Foo() {}
my_instanceof(Object, Object); // true
my_instanceof(Function, Function); // true
my_instanceof(Function, Object); // true
my_instanceof(Foo, Foo); // false
my_instanceof(Foo, Object);// true
my_instanceof(Foo, Function);// true

// instanceof:利用原型链判断“父级”的原型(prototype)对象是否在“实例”的原型链上;
// typeof:直接根据变量值得内存标识符进行判断;
// typeof 一般用来判断 number、string、boolean、undefined、object、function、symbol 这七中类型。
// js 在底层存储变量的时候,会在变量的机器码的低位 1-3 位存储其类型信息:
//
// 000:对象
//
// 010:浮点数
//
// 100:字符串
//
// 110:布尔
//
// 1:整数

// 参考链接  https://juejin.im/post/6844903613584654344

13. JSONP

const getQueriesByObj = (object, prefix = "") => Object.keys(object)
  .reduce((prev, curr) => (prev += `${curr}=${encodeURIComponent(object[curr])}&`), prefix)
  .slice(0, -1);

const jsonp = ({ url, params = {} }) => new Promise((resolve, reject) => {
  const script = document.createElement('script');
  const cbKey = `_jsonp${Math.random().toString().substring(2)}`;
  window[cbKey] = result => {
    delete window[cbKey];
    script.remove(); // 将添加到页面中的 script 标签移除
    return resolve(result);
  };
  script.type = 'text/javascript';
  script.id = cbKey;
  script.src = getQueriesByObj({ ...params, callback: cbKey }, url.includes('?') ? url : `${url}?`);
  document.body.append(script);
});

jsonp({
  url: "http://localhost:3000/test",
  params: {},
}).then(console.log);

14. New

const create = (...args) => {
  const obj = {};
  const [constructor, ...params] = args;
  obj.__proto__ = constructor.prototype;
  const result = constructor.apply(obj, params);
  return result instanceof Object ? result : obj;
}

function Person(name, age) {
  this.name = name;
  this.age = age;
  return this;
}

const testPerson = create(Person, 'neo', '23');

console.log('testPerson', testPerson);

15. Promise

class MyPromise {
  constructor(props) {
    this.STATE = {
      PENDING: 'pending',
      RESOLVED: 'resolved',
      REJECTED: 'rejected',
    }
    this.status = this.STATE.PENDING;
    this.data = undefined;
    this.resolvedCallbacks = [];
    this.rejectedCallbacks = [];
    this.fn = props;
    this.doTask();
  }

  doTask = () => {
    const { resolve, reject } = this;
    try {
      this.fn(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  resolve = (data) => {
    if (this.status === this.STATE.PENDING) {
      this.status = this.STATE.RESOLVED;
      this.data = data;
      this.resolvedCallbacks.forEach(cb => cb(data));
    }
  }

  reject = (data) => {
    if (this.status === this.STATE.PENDING) {
      this.status = this.STATE.REJECTED;
      this.data = data;
      this.rejectedCallbacks.forEach(cb => cb(data));
    }
  }

  handleResolve = (resolve, reject, handler) => {
    try {
      const ret = handler(this.data);
      if (ret instanceof MyPromise) {
        ret.then(resolve, reject);
      } else {
        resolve(ret);
      }
    } catch (e) {
      reject(e);
    }
  }

  handleReject = (resolve, reject, handler) => {
    try {
      const ret = handler(this.data);
      if (ret instanceof MyPromise) {
        ret.then(resolve, reject);
      } else {
        reject(ret);
      }
    } catch (e) {
      reject(e);
    }
  }

  then = (onFulfilled, onRejected) => {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : e => {throw e};
    const actions = {
      resolved: () => new MyPromise((resolve, reject) => setTimeout(() =>
        this.handleResolve(resolve, reject, onFulfilled)
      )),
      rejected: () => new MyPromise((resolve, reject) => setTimeout(() =>
        this.handleReject(resolve, reject, onRejected)
      )),
      pending: () => new MyPromise((resolve, reject) => {
        this.resolvedCallbacks.push(() => setTimeout(() =>
          this.handleResolve(resolve, reject, onFulfilled)
        ));
        this.rejectedCallbacks.push(() => setTimeout(() =>
          this.handleReject(resolve, reject, onRejected)
        ));
      }),
      default: () => {
        throw new Error('sth may happened ~')
      },
    };
    return actions[this.status]
      ? actions[this.status]()
      : actions.default();
  }

  catch = (onRejected) => {
    return this.then(null, onRejected);
  }

  static resolve = (res) => new MyPromise(resolve => resolve(res));
  static reject = (reason) => new MyPromise((reject, resolve) => reject(reason));

  static all = promises => new MyPromise((resolve, reject) => {
    const result = [];
    const { length } = promises;
    promises.forEach((promise, idx) => MyPromise.resolve(promise).then(value => {
      result[idx] = value;
      if (result.length === length) {
        return resolve(result);
      }
    }, reject));
  });

  static allSettled = promises => new MyPromise(resolve => {
    const result = [];
    const { length } = promises;
    promises.forEach((promise, idx) => MyPromise.resolve(promise).then(
      value => {
        result[idx] = {
          status: 'fulfilled',
          value
        }
        if (result.length === length) {
          return resolve(result);
        }
      },
      reason => {
        result[idx] = { status: 'rejected', reason }
        if (result.length === length) {
          return resolve(result);
        }
      }));
  });

  static race = promises => new MyPromise((resolve, reject) => promises.forEach(promise => MyPromise.resolve(promise).then(resolve, reject)));
}


new MyPromise((resolve, reject) => {
  reject('失败');
}).then().then().then(data => {
  console.log(data);
}, err => {
  console.log('err', err);
})

MyPromise.resolve(1).then(console.log)
MyPromise.reject(1).then(console.log)


// Promise.race
Promise.myRace = promises => new Promise((resolve, reject) => promises.forEach(promise => Promise.resolve(promise).then(resolve, reject)));

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 200, 'promise1');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'promise2');
});
Promise.myRace([promise1, promise2]).then(value => {
  console.log(value);
});

// Promise.myAllSettled
Promise.myAllSettled = promises => new Promise(resolve => {
  const result = [];
  const { length } = promises;
  promises.forEach((promise, idx) => Promise.resolve(promise).then(
    value => {
      result[idx] = {
        status: 'fulfilled',
        value
      }
      if (result.length === length) {
        return resolve(result);
      }
    },
    reason => {
      result[idx] = { status: 'rejected', reason }
      if (result.length === length) {
        return resolve(result);
      }
    }));
});

// Promise.all
Promise.myAll = promises => new Promise((resolve, reject) => {
  const result = [];
  const { length } = promises;
  promises.forEach((promise, idx) => Promise.resolve(promise).then(value => {
    result[idx] = value;
    if (result.length === length) {
      return resolve(result);
    }
  }, reject));
});

16. Reduce

// for 版
Array.prototype.myReduce = function (fn, initialValue) {
  const arr = this;
  const [begin, ...remain] = arr;
  const hasInitialValue = initialValue !== 'undefined';
  let prev = hasInitialValue ? begin : initialValue;
  const array = hasInitialValue ? remain : arr;
  for (let i = 0; i < array.length; i++) { prev = fn(prev, array[i], arr.indexOf(array[i]), arr); } return prev; } const sum1 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr);
const sum2 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr, 0);
console.log('**test**', sum1, sum2);

// foreach 版
Array.prototype.myReduce = function (fn, initialValue) {
  const arr = this;
  let prev = initialValue;
  arr.forEach((item, idx) => {
    prev = typeof prev === 'undefined' && idx === 0
      ? arr[idx]
      : fn(prev, arr[idx], idx, arr);
  });
  return prev;
}
const sum3 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr);
const sum4 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr, 0);
console.log('**test**', sum3, sum4);

// 递归 版
Array.prototype.myReduce = function (fn, initialValue) {
  const arr = this;
  const run = (array, prev, idx = 0) => {
    if (!array.length) return prev;
    const [current, ...remain] = array;
    idx = arr.indexOf(current);
    return typeof prev === 'undefined' && idx === 0
      ? run(remain, current, idx)
      : run(remain, fn(prev, current, idx, arr), idx)
  };
  return run(arr, initialValue);
}
const sum5 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr);
const sum6 = [1, 2, 3, 4].myReduce((prev, curr) => prev += curr, 0);
console.log('**test**', sum5, sum6);

17. Throttle

const throttle = (fn, delay = 1000) => {
  let timer = null;
  return (...args) => {
    if (timer) return;
    timer = setTimeout(() => {
      fn(...args);
      clearTimeout(timer);
      timer = null;
    }, delay);
  };
}

持续更新和纠正,欢迎大家拍砖!

以上关于js高频手撕面试题(简单好记)的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » js高频手撕面试题(简单好记)

发表回复