柯里化通用模板
const curry = (fn) => {
// 实现一个curry工具方法
// TODO
const curryTemp = (...args1) => {
if (args1.length < fn.length) {
return (...args2) => {
return curryTemp(...args1, ...args2);
};
}
return fn(...args1);
};
return curryTemp;
};
组合函数通用模板
const compose = (...funcs) => {
// 组合函数工具方法
return funcs.reduce((a, b) => (...args) => a(b(...args)));
};
解析 url
const httpUrl = string =>
string
.split('?')
.slice(-1)[0]
.split('&')
.reduce((acc, cur) => {
cur = cur.split('=')
acc[cur[0]] = cur[1]
return acc
}, {})
小驼峰转下划线
const humpToUnderLine = srt => srt.replace(/([A-Z])/g, '_$1').toLowerCase()
下划线转小驼峰
const underLineToHump = str => str.replace(/_(\w)/g, (all, latter) => latter.toUpperCase())
对象拍平
// 输入
const entryObj = {
a: {
b: {
c: {
dd: 'abcdd',
},
},
d: {
xx: 'adxx',
},
e: 'ae',
},
};
const objectFlat = (data) => {
const outputTemp = {};
const objectMap = (obj, parentKey = '') => {
for (let i in obj) {
if (Object.prototype.toString.call(obj[i]) === '[object Object]') {
objectMap(obj[i], `${parentKey}.${i}`);
} else {
outputTemp[`${parentKey}.${i}`] = obj[i];
}
}
};
for (let i in data) {
if (Object.prototype.toString.call(data[i]) === '[object Object]') {
objectMap(data[i], i);
} else {
outputTemp[i] = data[i];
}
}
return outputTemp;
};
objectFlat(entryObj);
// 输出
const outputObj = {
'a.b.c.dd': 'abcdd',
'a.d.xx': 'adxx',
'a.e': 'ae',
};
数组拍平
const array = [1, 2, [3, [4], [[5]]], [6, 7]];
const arrayFlat = (array) =>
array.reduce((acc, cur) => {
if (Object.prototype.toString.call(cur) === '[object Array]') {
cur = arrayFlat(cur);
}
return acc.concat(cur);
}, []);
arrayFlat(array); // [1, 2, 3, 4, 5, 6, 7]
手写实现 bind
Function.prototype.bind(ctx) = {
var that = this
return function fn() {
that.apply(ctx,arguments)
}
}
手写实现 call
Function.prototype.ownCall = function(context, ...args) {
context = (typeof context === 'object' ? context : window)
// 防止覆盖掉原有属性
const key = Symbol()
// 这里的this为需要执行的方法
context[key] = this
// 方法执行
const result = context[key](...args)
delete context[key]
return result
}
手写实现 apply
Function.prototype.ownApply = function(context, args) {
context = (typeof context === 'object' ? context : window)
// 防止覆盖掉原有属性
const key = Symbol()
// 这里的this为需要执行的方法
context[key] = this
// 方法执行
const result = context[key](...args)
delete context[key]
return result
}
排列组合后的所有可能性
const Permutations = (arr) => {
const getNumber = (val) => {
return val ? getNumber(val - 1) * val : 1;
};
let k = 0;
let max = getNumber(arr.length);
let res = [];
while (max--) {
res.push(arr.join(''));
[arr[k], arr[k + 1]] = [arr[k + 1], arr[k]];
k = ++k % (arr.length - 1);
}
return res;
};
合并连贯数字
const mergeNumber = (arr) => {
const len = arr.length;
const empty = [];
if (len > 0) empty.push(arr[0]);
let k = 0;
let j = 0;
for (let i = 1; i < len; i++, j++) {
if (arr[j] !== arr[i] - 1) {
empty.push(arr[i]);
k = i;
}
if (i - k > 0) {
empty[empty.length - 1] = `${
empty[empty.length - 1].toString().split('-')[0]
}-${arr[i]}`;
}
}
return empty;
};
防抖和节流
防抖 - 触发高频事件后 n 秒后函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
function debounce(fn, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(...this.arguments);
}, time);
};
}
节流 - 高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
function throttle(fn, time) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
const timer = setTimeout(() => {
fn.apply(...this.arguments);
canRun = true;
clearTimeout(timer);
}, time);
};
}
继承
父类
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
原型链继承
核心: 将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
优点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 要想为子类新增属性和方法,必须要在 new Animal()这样的语句之后执行,不能放到构造器中
- 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的(详细请看附录代码: 示例 1)
- 创建子类实例时,无法向父类构造函数传参
构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
特点:
- 解决了 1 中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call 多个父类对象)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合式继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
特点:
- 弥补了方式 2 的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
寄生式继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
数组排序
哈希排序
function hashSort(arr) {
const hashTable = {};
const sortedArray = [];
// 遍历数组,并在哈希表中记录每个元素的出现次数
arr.forEach(num => {
if (hashTable[num]) {
hashTable[num]++;
} else {
hashTable[num] = 1;
}
});
console.log(hashTable)
// 遍历哈希表,将元素按照出现次数添加到排序后的数组中
for (let key in hashTable) {
let freq = hashTable[key];
while (freq > 0) {
sortedArray.push(parseInt(key));
freq--;
}
}
return sortedArray;
}
每隔一秒输出一个数字
const func5 = async () => {
for (let i = 1; i < 6; i++) {
await new Promise((reslove) => {setTimeout(() => { reslove(i) }, 1000, i) }).then(res =>{ console.log(res)})
}
}