手撕常用代码

手撕常用代码

夯实基础,日常练习,点滴积累,进阶提升。(长期维护更新)

1. 浅拷贝:

1
2
3
4
5
6
7
function clone(o) {
var _o = {};
for (let k in o) {
_o[k] = o[k];
}
return _o;
}

2. 深拷贝:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(o) {
if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean' || typeof o === 'undefined') {
return o;
} else if (Array.isArray(o)) {
var arr = [];
for(let i = 0; i < o.length; i++) {
arr.push(deepClone(o[i]))
}
return arr;
} else if (typeof o === 'object') {
var obj = {};
for(let k in o) {
obj[k] = deepClone(o[k]);
}
return obj;
}
}

3. 柯里化函数

// 通用版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
} else {
return fn.apply(this,newArgs);
}
}
}

function multiFn(a, b, c) {
return a * b * c;
}

var multi = curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);

// ES6 写法

1
2
3
4
5
6
7
8
9
10
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args]);

let curryTest = curry((a,b,c,d) => a+b+c+d);
curryTest(1,2,3)(4);
curryTest(1,2)(4)(3);
curryTest(1,2)(3,4);

4. 手动封装定时函数

1
2
3
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}

5. reduce 实现 filter、map、数组扁平化等

1
2
3
4
5
6
7
8
9
10
Array.prototype._map = function(callback) {
if (typeof callback === 'function') {
return this.reduce((prev, item, index, arr) => {
prev.push(callback(item, index, arr));
return prev;
}, [])
} else {
console.log(new Error('callback is not function'));
}
}
1
2
3
4
5
6
7
8
9
10
Array.prototype._filter = function(callback) {
if (typeof callback === 'function') {
return this.reduce((prev, item, index, arr) => {
callback(item, index, arr) ? prev.push(item) : null;
return prev;
}, [])
} else {
console.log(new Error('callback is not function'));
}
}
1
2
3
4
// 求最大值/最小值
let arr = [1, 2, 3, 4, 5];
arr.reduce((prev, cur) => Math.max(prev, cur)); // 5
arr.reduce((prev, cur) => Math.min(prev, cur)); // 1
1
2
3
4
5
6
7
// 数组去重
let arr = [1, 2, 3, 1, 1, 2, 3, 3, 4, 3, 4, 5];
let result = arr.reduce((prev, item, index, arr) => {
!prev.includes(item) && prev.push(item);
return prev;
}, [])
console.log(result); // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11
// 数组扁平化
let arr = [1, 2, '3js', [4, 5, [6], [7, 8, [9, 10, 11], null, 'abc'], {age: 58}, [13, 14]], '[]', null];;
function func(arr) {
if (Array.isArray(arr)) {
arr.reduce((prev, item, index, arr) => {
return Array.isArray(item) ? prev.concat(func(item)) : prev.concat(item);
})
} else {
throw new Error('arr is not array' );
}
}

6. 防抖

1
2
3
4
5
6
7
8
9
10
11
12
function debounce(fn, wait=500) {
let timer = null;
return function(...args) {
if (timer) {
clearTimeout(timer);
}

timer = setTimeout(() => {
fn.apply(this, args);
}, wait);
}
}

7. 节流

1
2
3
4
5
6
7
8
9
10
function throttle(fn, wait=500) {
let prev = new Date();
return function(...args) {
let now = new Date();
if (now - prev > wait) {
prev = now;
fn.apply(this, args);
}
}
}

8. 手撕 new

1
2
3
4
5
6
7
function _new() {
let obj = {}; // 创建一个空对象
let con = [].shift.call(arguments); // 获取构造函数
obj.__proto__ = con.prototype; // 设置空对象的原型
let res = con.apply(obj, arguments); // 绑定 this 并执行构造函数
return res instanceof Object ? res : obj; // 确保返回值为对象
}

9. 手撕 call, apply, bind

call, apply, bind: 改变函数的执行上下文中的 this 指向,但不执行该函数(位于 Function 构造函数的原型对象上的方法)

  • call 接受多个参数,第一个为函数上下文也就是 this,后边参数为函数本身的参数
  • apply 接受两个参数,第一个参数为函数上下文 this,第二个参数为函数参数只不过是通过一个数组的形式传入的
  • bind 接收多个参数,第一个是 bind 返回值,返回值是一个函数上下文的 this,不会立即执行
1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myCall = function(context = window) {
if (typeof this !== 'function') {
throw Error('myCall is not a function');
}
context.fn = this; // 给context添加一个方法 指向this
// 处理参数,去除第一个参数this 其他传入fn
var args = [...arguments].slice(1); // [...xxx]将类数组转为数组
var result = context.fn(...args); // 执行fn
delete context.fn; //删除方法
return result;
}
1
2
3
4
5
6
7
8
9
10
Function.prototype.myApply = function(context = window) {
if (typeof this !== 'function') {
throw Errror('myApply is not function');
}
context.fn = this;
var args = [...arguments].slice(1);
var result = context.fn(args);
delete context.fn;
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.myBind = function(target) {
if (typeof this !== 'function') {
throw Error('myBind is not function');
}
var that = this; // 返回一个绑定this的函数,我们需要在此保存this
var args1 = [...arguments].slice(1); // 可以支持柯里化传参,保存参数
var func = function() {
var args2 = [...arguments]; // 同样因为支持柯里化形式传参,我们需要再次获取存储参数
return that.apply(traget || window, args1.concat(args2)); // 考虑返回函数有返回值做了return
}
return func;
}

10. 手动实现一个简单的 async/await

1
2
3
4
5
6
7
8
9
// 定义一个Promise,用来模拟异步请求,作用是传入参数++
function getNum(num) {
return new Promise((resolve, reject) => {

setTimeout(() => {
resolve(num + 1)
}, 1000)
})
}
1
2
3
4
5
6
7
8
// 所需要执行的Generator函数,内部的数据在执行完成一步的promise之后,再调用下一步
var func = function *() {
const f1 = yield getNum(1);
console.log(1);

const f2 = yield getNum(2);
console.log(2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 自动执行器,如果一个Generator函数没有执行完,则递归调用
function asyncFun(func) {
var gen = func();

function next(data) {
var result = gen.next(data);

if (result.done) return result.value;

result.value.then(function(data) {
next(data);
})
}

next();
}
1
asyncFun(func);

11. 判断一个字符串是否为回文字符串

1
2
3
4
5
6
function Palindromes(str) {
let reg = /[\W_]/g; // \w 匹配所有字母和数字以及下划线; \W与之相反; [\W_] 表示匹配下划线或者所有非字母非数字中的任意一个;/g全局匹配
let newStr = str.replace(reg, '').toLowerCase();
let reverseStr = newStr.split('').reverse().join('');
return reverseStr === newStr; // 与 newStr 对比
}

12. 数字化金额格式处理

1
2
3
4
5
6
7
8
9
10
11
12
13
function dealNumber(money) {
if (money && money != null) {
money = String(money);
let left = money.split('.')[0], right = money.split('.')[1];
right = right ? (right.length >= 2 ? '.' + right.substr(0, 2) : '.' + right + '0') : '';
let temp = left.split('').reverse().join('').match(/(\d{1,3})/g);
return (Number(money) < 0 ? '-' : '') + temp.join(',').split('').reverse().join('') + right;
} else if (money === 0) { //注意===在这里的使用,如果传入的money为0,if中会将其判定为boolean类型,故而要另外做===判断
return '0';
} else {
return '';
}
}
0%