JavaScript常见手写题超全汇总
1. 前言
在面试以及实际工作中,常常需要手写一些核心的JavaScript代码。这些手写题目可能比较简单、或者非常复杂,但是它们都对JavaScript基础功夫有一个更加深刻的理解。
在本篇文章中,我们将会汇总一些常见的JavaScript手写题,包括但不限于:数组去重、深拷贝、Promise实现、函数柯里化等等。
2. 数组去重
数组去重是一个非常基础的手写题,着重考察了对JavaScript基本数据类型以及数据结构的理解。以下是一些常见的去重方法:
方法一: Set
通过Set将数组转换成为一个集合,然后将集合转换成为数组即可。
function uniqueArray(arr) {
return Array.from(new Set(arr));
}
方法二: for循环 + indexOf
遍历数组,用indexOf判断数组中是否已经存在当前元素,如果不存在则push进新数组中。
function uniqueArray(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
3. 深拷贝
JavaScript中的对象和数组都是引用类型,如果直接赋值只是复制了引用地址,两个变量指向的是同一个地址。因此,深拷贝是非常重要的操作。
方法一: JSON.parse 和 JSON.stringify
使用JSON.parse将对象转成字符串,再使用JSON.stringify将字符串转成对象实现深拷贝。
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
注意:这种方法虽然简单,但是会忽略undefined、function、symbol等属性及嵌套层数大于等于1000的对象。
方法二: 递归
使用递归实现深拷贝。递归思路:判断参数是否是数组或对象,是的话就新建一个数组或对象,继续遍历并赋值。
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
newObj[key] = deepClone(obj[key]);
}
return newObj;
}
4. Promise实现
Promise是ES6中的一个新特性,它通过解决回调地狱的问题,让异步操作更加方便和可读。
方法一: Promise构造函数
使用Promise构造函数来实现Promise。通过函数传递resolve、reject函数,并在异步操作完毕时调用相应的函数。
function myPromise(fn) {
let state = "pending";
let value = null;
let callbacks = [];
const resolve = function (newValue) {
if (state === "pending") {
state = "fulfilled";
value = newValue;
callbacks.forEach((callback) => callback.onFulfilled(value));
}
};
const reject = function (newValue) {
if (state === "pending") {
state = "rejected";
value = newValue;
callbacks.forEach((callback) => callback.onRejected(value));
}
};
this.then = function (onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
if (state === "pending") {
callbacks.push({
onFulfilled: (value) => {
try {
if (typeof onFulfilled === "function") {
const result = onFulfilled(value);
resolve(result);
} else {
resolve(value);
}
} catch (err) {
reject(err);
}
},
onRejected: (value) => {
try {
if (typeof onRejected === "function") {
const result = onRejected(value);
reject(result);
} else {
reject(value);
}
} catch (err) {
reject(err);
}
},
});
}
if (state === "fulfilled") {
try {
if (typeof onFulfilled === "function") {
const result = onFulfilled(value);
resolve(result);
} else {
resolve(value);
}
} catch (err) {
reject(err);
}
}
if (state === "rejected") {
try {
if (typeof onRejected === "function") {
const result = onRejected(value);
reject(result);
} else {
reject(value);
}
} catch (err) {
reject(err);
}
}
});
};
fn(resolve, reject);
}
注意: 这种方法虽然可以实现Promise实例,但是没有考虑异步,因此我们在实现时需要将异步操作放到setTimeout中。
方法二:Async/Await搭配Generator
使用Async/Await搭配Generator来实现Promise。在编写异步操作时,使用Generator的语法报告异步上下文,然后通过Async/Await来异步等待,实现同步式编码。
function myPromise(fn) {
let value = null;
let resolved = false;
let fnGenerator = function* () {
yield new Promise((resolve, reject) => {
const resolveWrap = (res) => {
value = res;
resolved = true;
resolve(res);
};
const rejectWrap = (res) => {
value = res;
resolved = true;
reject(res);
};
fn(resolveWrap, rejectWrap);
});
};
this.then = async function (cb) {
const result = await fnGenerator().next().value;
return cb(result);
};
}
5. 函数柯里化
函数柯里化是指将一个多参数的函数,转化为一个嵌套的单参数函数的技术。
以下是一些常见的函数柯里化方法:
方法一:反向柯里化
按照参数列表的最后一项先生成闭包,然后通过嵌套函数生成环境,最后在外层return内层的函数。
function curry(fn) {
const length = fn.length;
return function curryed(...args) {
if (args.length >= length) {
return fn.apply(null, args);
} else {
return function () {
const allArgs = [...args, ...arguments];
return curryed.apply(null, allArgs);
};
}
};
}
方法二:正向柯里化
又称纯函数柯里化,被称为"curry"的函数,是指能够把一个N元函数转换成N个一元函数的方法。函数柯里化的主要作用是将数据转换成为适合函数操作的形式。
function add(a, b, c) {
return a + b + c;
}
function curryedAdd(a) {
return function (b) {
return function (c) {
return add(a, b, c);
};
};
}
const result = curryedAdd(1)(2)(3);
console.log(result); //6
6. 结语
本文仅对手写题目进行了简单的介绍,对于每道题目都有更多实现方式,读者可以自行拓展。
但是需要指出的是,重点并不在于知道怎么写,而是能够思考出不同的解决方式,举一反三。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript常见手写题超全汇总 - Python技术站