面试题集合(手撕代码篇)
1. 手写 bind、call、apply
// call
Function.prototype.myCall = (content) => {
content = content || window;
content.fn = this;
const args = [...arguments].slice(1);
const result = content.fn(args);
delete content.fn;
return result;
}
// apply
Function.prototype.myApply = (content) => {
content = content || window;
content.fn = this;
let result
if (arguments[1] && Array.isArray(arguments[1])) {
if (arguments[1].length === 1) {
result = content.fn(...arguments[1])
} else {
result = content.fn()
}
}
delete content.fn
return result
}
// bind
Function.prototype.myBind = (content) => {
content = content || window;
content.fn = this;
const args = [...arguments].slice(1);
return () => {
content.fn(args)
}
}
2. 手写防抖节流
防抖
const debounce = (fn, delay) => {
let timeout = null;
return () => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
fn.call(this, arguments)
}, delay)
}
}
节流
const throttle = (fn, delay) => {
let canRun = true;
if (!canRun) {
return
}
canRun = false;
setTimeout(() => {
fn.call(this. arguments);
canRun = true;
}, delay);
}
3. 手写数组flat
const flat = arr => {
arr.reduce((pre, value) => {
return pre.concat(Array.isArray(value) ? flat(value) : value)
}, []);
}
4. 写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal
function mySetInterVal(fn, a, b) {
this.a = a;
this.b = b;
this.time = 0;
this.handle = -1;
this.start = () => {
this.handle = setTimeout(() => {
fn();
this.time++;
this.start();
}, this.a + this.time * this.b);
}
this.stop = () => {
clearTimeout(this.handle);
this.time = 0;
}
}
var a = new mySetInterVal(() => {console.log('123')},1000, 2000 );
a.start();
a.stop();
5. 斐波那契数列
const Fibonacci = (n) => {
if (n < 0) throw new Error('输入的数字不能小于0');
if (n < 2) return n;
return Fibonacci(n - 1) + Fibonacci(n - 2)
}
7. 实现 add(1)(2)(3)
const add = (a) => (b) => (c) => a + b + c;
8. 数据类型判断
typeof 可以正确识别:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,但是对于其他的都会认为是 object,比如 Null、Date 等,所以通过 typeof 来判断数据类型会不准确。但是可以使用 Object.prototype.toString 实现。
const getType = (obj) => {
return Object.prototype.toString.call(obj).slice(8, -1);
}
9. 数组扁平化
数组扁平化就是将 [1, [2, [3]]] 这种多层的数组拍平成一层 [1, 2, 3]。使用 Array.prototype.flat 可以直接将多层数组拍平成一层:
[1, [2, [3]]].flat(2) // [1, 2, 3]
关键:递归
const flatten = arr => {
let newArr = [];
for(let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
newArr.concat(flatten(arr[i]))
return
}
newArr.push(arr[i])
}
return neaArr;
}
10. 深浅拷贝
浅拷贝:只考虑对象类型
ES5版
function shallowCopy(obj) {
if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {};
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
newObj[i] = obj[i];
}
}
return newObj;
}
ES6版
const shallowCopy = obj => Array.isArray(obj) ? [...obj] : {...obj}
深拷贝
const deepClone = obj => {
if (typeof obj !== 'object') return;
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
newObj[key] = deepClone(obj[key]);
} else {
newObj[key] = obj[key];
}
}
}
return newObj;
}
11. 解析 URL 参数为对象
const urlSearch = href => {
let obj = {};
const queryIndex = href.indexOf('?');
const urlOptions = href.slice(queryIndex + 1, href.length);
const options = urlOptions.split('&');
options.map(option => {
const equalIndex = option.indexOf('=');
obj[option.slice(0, equalIndex)] = option.slice(equalIndex + 1, option.length);
});
return obj;
}
12. 实现柯里化(Currying)
function curry(fn) {
let judge = (...args) => {
if (args.length == fn.length) return fn(...args)
return (...arg) => judge(...args, ...arg)
}
return judge
}
13. 手写AJAX
const myAjax = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.setRequestHeader('Accept', 'application/json');
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
if (xhr.status === 200) {
resolve(xhr.responseText)
} else {
reject(xhr.responseText)
}
}
xhr.send();
})
}
14. 手写Promise
const PADDING = "PADDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class MyPromise {
constructor(executor) {
this.status = PADDING;
this.value = undefined;
this.error = undefined;
let resolve = (value) => {
if (this.status === PADDING) {
this.value = value;
this.status = FULFILLED;
}
}
let reject = (error) => {
if (this.status === PADDING) {
this.error = error;
this.status = REJECTED;
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.error)
}
}
}
15. 实现 Promise.all
const promiseAll = (promises) => {
if (Array.isArray(promises)) {
throw new Error('the arguments must be an array !!!')
}
const promisesLength = promises.length;
let current = 0, result = [];
new Promise((resolve, reject) => {
promises.forEach((promise, i) => {
Promise.resolve(promise).then(value => {
current ++;
result[i] = value;
if (current === promisesLength) {
resolve(result)
}
}, error => reject(error))
})
})
}
16. 手写模版字符串
const render = (str, data) => {
const reg = /\{\{(\w+)\}\}/;
if (reg.test(str)) {
const key = reg.exec(str)[1];
str = str.replace(reg, data[key]);
return render(str, data);
}
return str;
}