最新公告  | 
  • CTRL + D 加入收藏不迷路哦

  • 欢迎您光临码云笔记网,一个关注WEB前端开发的个人技术博客!

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

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);
  };
}

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

1. 本站所有免费资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!
2. 本站不保证所提供下载的免费资源的准确性、安全性和完整性,免费资源仅供下载学习之用!如有链接无法下载、失效,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或技术教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
5. 加入前端开发QQ群:565733884,我们大家一起来交流技术!
码云笔记 » js高频手撕面试题(简单好记)

发表评论

准备开启WordPress网站建设推广?

联系我们 定制开发