柯里化
题目描述:柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。
简述:把多个参数变成传入单一参数的函数
function currying(fn,...args){
const length=fn.length //原函数的参数个数
let arrArgs = [...args]
const res = (...newArgs)=>{
allArgs = [...arrArgs,...newArgs] //收集参数
if(allArgs.length === length){ //如果搜集到的参数和原函数一样多,则执行原函数,否则继续搜集参数
return fn(...allArgs)
}else{
return res
}
}
return res
}
//把参数收集起来最终放到原函数里
const add = (a, b, c) => a + b + c;
const a = currying(add, 1);
console.log(a(2,3))
数组扁平化
题目描述:实现一个方法使多维数组变成一维数组
简述:利用数组的reduce方法。递归调用
function flatter(arr) {
if (!arr.length) return;
return arr.reduce(
(pre, cur) =>
Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],
[]
);
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
寄生组合继承
题目描述:实现一个你认为不错的 js 继承方式
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
function Parent(name) {
this.name = name;
this.say = () => {
console.log(111);
};
}
Parent.prototype.play = () => {
console.log(222);
};
function Children(name) {
Parent.call(this);
this.name = name;
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();
实现有并行限制的 Promise 调度器
题目描述:JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的输出顺序是:2 3 1 4
整个的完整执行流程:
一开始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4
实现代码如下:
class Scheduler {
constructor(limit) {
this.queue = [];//执行的数组队列
this.maxCount = limit;//同时执行的最大数量
this.runCounts = 0;//正在执行的数量
}
add(time, order) {//添加时间,和输出值
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(promiseCreator);//把要执行的函数推到执行队列里。
}
taskStart() {
for (let i = 0; i < this.maxCount; i++) {//初次,取出可以执行的数量开始执行。
this.request();
}
}
request() {
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;
//下面是取取出队列的第一个执行,利用返回的promise.then递归调用下一个
//shift()方法移除数组的第一项 shift()方法会改变数组的长度。
//shift方法的返回值是被移除的项目。shift()方法会改变原始数组。
//如需删除数组的最后一项,请使用 pop()方法。
this.queue
.shift()()
.then(() => {
this.runCounts--;
this.request();
});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
new 操作符
题目描述:手写 new 操作符实现
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
let res = fn.call(obj, ...args);
if (res && (typeof res === "object" || typeof res === "function")) {
return res;
}
return obj;
}
用法如下:
// // function Person(name, age) {
// // this.name = name;
// // this.age = age;
// // }
// // Person.prototype.say = function() {
// // console.log(this.age);
// // };
// // let p1 = myNew(Person, "lihua", 18);
// // console.log(p1.name);
// // console.log(p1);
// // p1.say();
深拷贝(考虑到复制 Symbol 类型)
Symbol 一种新的原始数据类型 Symbol ,表示独一无二的值。最大的用法是用来定义对象的唯一属性名。 ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。
Reflect 对象提供了以下静态方法,这些方法与proxy handler methods (en-US)的命名相同.
其中的一些方法与 Object相同, 尽管二者之间存在 某些细微上的差别 .
function isObject(val) {
return typeof val === "object" && val !== null;
}
function deepClone(obj, hash = new WeakMap()) {
if (!isObject(obj)) return obj;
if (hash.has(obj)) {//我总觉得这个没用
return hash.get(obj);
}
let target = Array.isArray(obj) ? [] : {};
hash.set(obj, target);
Reflect.ownKeys(obj).forEach((item) => {
if (isObject(obj[item])) {
target[item] = deepClone(obj[item], hash);
} else {
target[item] = obj[item];
}
});
return target;
}
// var obj1 = {
// a:1,
// b:{a:2}
// };
// var obj2 = deepClone(obj1);
// console.log(obj1);
instanceof
题目描述:手写 instanceof 操作符实现
function myInstanceof(left, right) {
while (true) {//return会停止while循环
if (left === null) {
return false;
}
if (left.__proto__ === right.prototype) {
return true;
}
left = left.__proto__;
}
}
冒泡排序--时间复杂度 n^2
要点 两层循环 内层把最大的移动到到最右边,最后一个最大就不比了依次减小 外层把每个都比较一遍
function bubbleSort(arr){
const len = arr.length //3
for(let i= 0;i<len;i++){ //0 1
for(let j=0;j<len-1-i;j++){ //0 231 1 213 //0 123 1 123
if(arr[j]>arr[j+1]){
[arr[j],arr[j+1]]=[arr[j+1],arr[j]]
}
}
}
return arr
}
console.log(bubbleSort([5,4,3,2,1]));
选择排序--时间复杂度 n^2
选择排序 选出最小的放在第一个位置上依次查询最小的
要点 两层循环,外层比较到每一个,取剩下的每一个和假设最小的比较,取到最小的下标值
function selectSort(arr){
const len = arr.length
let minIndex
for(let i=0;i<len-1;i++){
minIndex = i
for (let j=i;j<len;j++){
if(arr[j]<arr[minIndex]){
minIndex=j
}
}
if(minIndex !==i){
[arr[i],arr[minIndex]]=[arr[minIndex],arr[i]]
}
}
return arr
}
console.log(selectSort([5,4,3,2,1]));
插入排序--时间复杂度 n^2
插入排序 把大的向后挪,把小的
要点 外层循环每一个,内层循环当前位置前的,把这个放到比他大的前面 互换位置大的依次n->n->n->n,小的依次n<-n<-n<-
function insertSort(arr){
for (let i=1;i<arr.length;i++){
let j=i
let target = arr[j]
while(j>0&& arr[j-1]>target){
arr[j] = arr[j-1]
j--
}
arr[j]=target
}//1 45321 2 43521 34521 3 34251 32451 23451
return arr
}
console.log(insertSort([5,3,9,2,1,4]));
快排--时间复杂度 nlogn~ n^2 之间
要点 取出一个值,比它大的放右边,比它小的放左边,还有和他相等的的,注意不要重复放置,要去掉当前下标的。
function quickSort(arr){
if(arr.length<2){
return arr
}
const cur = arr[arr.length-1]
console.log(cur);
const left = arr.filter((v,i)=>v<=cur&& i!==arr.length-1)
const right = arr.filter(v=>v>cur)
return [...quickSort(left),cur,...quickSort(right)]
}
// console.log(quickSort([1,2,3,4,5,6]));
console.log(quickSort([6,5,4,3,2,1]));
// console.log(quickSort([1,2,3,6,5,4]));
// 222222
function quickSort(arr){
if(arr.length<2){
return arr
}
const cur = arr[0]
console.log(cur);
const left = arr.filter((v,i)=>v<=cur&& i!==0)
const right = arr.filter(v=>v>cur)
return [...quickSort(left),cur,...quickSort(right)]
}
console.log(quickSort([6,5,4,3,2,1]));
// console.log(quickSort([1,2,3,4,5,6]));
归并排序--时间复杂度 nlog(n)
本文摘自juejin.cn/post/696871… 作者写的精炼,自己每次看过就会忘记,自己添加了系列注释帮助理解