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); }; }
持续更新和纠正,欢迎大家拍砖!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » js高频手撕面试题(简单好记)
码云笔记 » js高频手撕面试题(简单好记)