前言
系列首发于公众号『前端进阶圈』 ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。
手撕代码系列(二)
手写函数柯里化 curring
/**
* 函数柯里化 curring
* @param {Function}
* @return 视具体方法而定
*
* @logic
* 1.创建一个参数数组 args
* 2.创建一个函数,接收参数列表
* 3.函数判断参数数组是否有值
* 4.如果有值,则往 args 中进行 push 操作,否则就是没有参数了,则直接进行调用
* 5.调用后,将 args 参数数组清空
* 6.返回参数数组
*/
const curring = fn => {
let args = [];
return function temp(...rest) {
if (rest.length) {
args.push(...rest);
return temp;
} else {
let result = fn.apply(this, args);
args = [];
return result;
}
}
}
// test:
const add = (...args) => {
return args.reduce((prev, next) => prev + next, 0);
}
console.log(curring(add)(1)(2)(3)(4)(5)()); // 15
console.log(curring(add)(1)(2)(3)(4,5)()); // 15
console.log(curring(add)(1,2)(3,4,5)()); // 15
console.log(curring(add)(1)(2,3,4,5)()); // 15
手写数组乱序方法 shuffle
/**
* 随机洗牌
* @param {Array} arr 要被打乱的数组
* @returns 打乱后的元素
*
* @logic
* 1. 从还没处理的数组中,从 [0, array.length] 之间取一个随机数 random number
* 2. 从剩下的 array.length 中把第 random 元素取出来放在新数组 result 中
* 3. 删除原数组中第 random 个元素
* 4. 重复2,3步骤直到所有元素取完
* 5. 最终返回新数组即可
*/
const shuffle = source => {
let arr = Array.from(source);
let result = [];
while (arr.length) {
let random = (Math.random() * arr.length) | 0;
// let random = Math.floor(Math.random() * arr.length);
// let random = (Math.random() * arr.length) >> 0;
// let random = (Math.random() * arr.length) << 0;
// let random = (Math.random() * arr.length) >>> 0;
// let random = ~~(Math.random() * arr.length);
result.push(arr[random]);
arr.splice(random, 1)
}
return result;
}
// test:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
console.log("shuffle(numbers) ------->", shuffle(numbers));
// shuffle(numbers) -------> [
// 9, 1, 17, 6, 10, 12, 18,
// 19, 20, 8, 13, 2, 11, 15,
// 7, 4, 16, 14, 5, 3
// ]
手写解析 URL Params 方法 getUrlParams
/**
* 解析 URL Params getUrlParams
* @param {String} urlStr
* @return {Object} 参数对象
*/
// 方法一:
const getUrlParams = url => {
// 解析url,获取参数部分
let urlStr = url.split('?')[1];
// 判断是否有 hash 值
let newStr = '';
// 去重 hash 值逻辑
if (urlStr.includes('#')) {
newStr = urlStr.split('#')[0];
}
// 参数重置化
let res = new URLSearchParams(newStr);
// Object.fromEntries() 方法将 key value 数组形式转换为对象;
/**
demo:
let obj = {
a: 1,
b: 2
}
console.log(Object.fromEntries(Object.entries(obj)));
*/
return Object.fromEntries(res.entries())
}
// 方法二:
const getUrlParams = url => {
let urlParams = new URL(url);
return Object.fromEntries(urlParams.searchParams.entries());
}
// test:
let tempUrl =
"https://www.baidu.com:80/temp/src/index.html?name=John&sex=14#hash=title";
console.log("getUrlParams(tempUrl) ------->", getUrlParams(tempUrl));
// getUrlParams(tempUrl) -------> { name: 'John', sex: '14' }
手写数组扁平化 myFlat
/**
* 数组扁平化 myFlat
* @param {Array} source 需要扁平化的数组
* @returns 扁平化后的新数组
*/
// 方法一:
const myFlat = source => {
// 结果数组
let result = [];
// 遍历
for (let i = 0; i < source.length; i++){
// 数组元素
let item = source[i];
// 判断当前项是否为数组,若是数组递归执行
if (Array.isArray(item)) {
// result.concat(myFlat(item)) 使用之前的列表(result)拼接当前的列表(item),因为 concat 不会修改原数组,重新赋值给 result 数组
result = result.concat(myFlat(item));
} else {
// 不是数组,则添加到 result 数组中
result.push(item);
}
}
// 返回结果
return result;
}
// 方法二:
// some + 扩展运算符:通过 while 循环来持续判断当前项是否为数组,如果为数组,直接修改原数组 arr 等于 空数组展开当前数组,按此逻辑继续执行。
// some:如果有一个满足条件,则返回 true, 后续将和 includes 和 indexof 方法一样,不会再进行后续检测,如果不满足则返回 false
// any: 数组中的所有元素都满足条件,返回 boolean 值。
const MyFlat = (arr) => {
while (arr.some((item) => Array.isArray(item))) {
console.log(arr);
arr = [].concat(...arr);
}
return arr;
};
// 方法三:
// split + toString(): 此方法弊端,如果数组中的元素都是数字,那经过此方法会直接转为字符串。
const MyFlat = (arr) => {
return arr.toString().split(",");
};
// 方法四:
// ES6 flat
const MyFlat = (arr) => {
return arr.flat(Infinity);
};
// test:
let arr = [1, [2, [3, 4, 5]]];
console.log("myFlat(arr) after------>", myFlat(arr)); // myFlat(arr) after------> [ 1, 2, 3, 4, 5 ]
手写 myInstanceof
/**
* instanceof
* instanceof 运算符用户检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
* 语法:object(实例对象) instanceof constructor(构造函数,与 prototype 是成对出现的,你指向我,我指向你)
* @param {Object} object 某个实例对象
* @param {Function} constructorFn 某个构造函数
* @return {Boolean}
*/
const myInstanceof = (object, constructorFn) => {
// Object.getPrototypeOf(object) === object.__proto__; 都是用来获取某个对象的隐式原型
let objectRes = Object.getPrototypeOf(object);
// prototype:显式原型
const constructorRes = constructorFn.prototype;
while (true) {
if (!objectRes) return false;
// 如果构造函数的 prototype 在对象的实例中,直接返回 true
if (objectRes === constructorRes) return true;
// 继续向下寻找
objectRes = Object.getPrototypeOf(objectRes);
}
};
// test:
function F() { };
function D() { };
let f = new F();
// 表示: 构造函数 F 是否出现在 f 对象实例上
console.log(myInstanceof(f, F)); // true
// 表示: 构造函数 D 是否出现在 f 对象实例上
console.log(myInstanceof(f, D)); // false
特殊字符描述:
- 问题标注
Q:(question)
- 答案标注
R:(result)
- 注意事项标准:
A:(attention matters)
- 详情描述标注:
D:(detail info)
- 总结标注:
S:(summary)
- 分析标注:
Ana:(analysis)
提示标注:
T:(tips)
往期推荐:
- 前端面试实录HTML篇
- 前端面试实录CSS篇
- JS 如何判断一个元素是否在可视区域内?
- Vue2、3 生命周期及作用?
- 排序算法:QuickSort
- 箭头函数与普通函数的区别?
- 这是你理解的CSS选择器权重吗?
- JS 中 call, apply, bind 概念、用法、区别及实现?
- 常用位运算方法?
- Vue数据监听Object.definedProperty()方法的实现
- 为什么 0.1+ 0.2 != 0.3,如何让其相等?
- 聊聊对 this 的理解?
JavaScript 为什么要进行变量提升,它导致了什么问题?
最后:
- 欢迎关注 『前端进阶圈』 公众号 ,一起探索学习前端技术......
- 公众号回复 加群 或 扫码, 即可加入前端交流学习群,一起快乐摸鱼和学习......
- 公众号回复 加好友,即可添加为好友
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。