前端面试手写系列

272 阅读7分钟
// @ts-nocheck
/**
 * 手写系列
 */
/**
 * 防抖
 */
export function debounce(fn, delay) {
    let timer = null;

    return function (...args) {
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = null;
        }, delay);
    };
}
/**
 * 节流
 */
export function throttle(fn, interval) {
    let last = 0;

    return function (...args) {
        let now = new Date().getTime();
        if (now - last >= interval) {
            fn.apply(this, args);
            last = now;
        }
    };
}
/**
 * 节流 定时器版
 */
export function throttle2(fn, interval) {
    let timer;

    return function (...args) {
        if (timer) return;
        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = null;
        }, interval);
    };
}
/**
 * 柯里化
 */
export function currying(arg) {
    const args = [arg];
    function add(arg) {
        if (arg === undefined) return add;
        args.push(arg);
        return add;
    }

    add.toString = function () {
        return args.reduce((sum, cur) => {
            return sum + cur;
        });
    };

    return add;
}
// console.log('currying 结果:' + currying(1)(2)(3)(4)(5)()());
/**
 * 参数长度不固定
 */
function currying2(...args1) {
    const args = [...args1];
    const _add = (...args2) => {
        args.push(...args2);
        return _add;
    };
    _add.toString = () => {
        return args.reduce((sum, cur) => {
            return sum + cur;
        });
    };
    return _add;
}
// console.log('currying2 结果:' + currying2(1, 10000)(2, 20000)(3, 30000, 40000, 50000)(4)(5)()());
/**
 * 普通函数转成柯里化函数
 */
export function createCurrying(fn, ...args1) {
    if (args1.length >= fn.length) {
        return fn.apply(this, args1);
    } else {
        return function (...args2) {
            return createCurrying(fn, ...args1, ...args2);
        };
    }
    // return args1.length >= fn.length ? fn.apply(this, args1) : createCurrying.bind(this, fn, ...args1);
}
function testCreateCurrying(arg1, arg2, arg3) {
    return arg1 + arg2 + arg3;
}
const curryingTest = createCurrying(testCreateCurrying);
// console.log('createCurrying 结果:', curryingTest(1)(2)(3));
/**
 * 实现 Object.create
 */
export function objectCreate(proto) {
    function Fn() {}
    Fn.prototype = proto;
    return new Fn();
}
// console.log('objectCreate 结果:', objectCreate({ x: 123 }));
/**
 * 实现 instanceof
 */
export function myInstanceof(obj, constr) {
    if (obj === null || obj === undefined) return false;

    let proto = Object.getPrototypeOf(obj);
    const constrProto = constr?.prototype;

    while (proto !== null) {
        if (proto === constrProto) return true;
        proto = Object.getPrototypeOf(proto);
    }

    return false;
}
// console.log('myInstanceof 结果:', myInstanceof([], Object));
/**
 * 实现 new 操作符
 */
export function myNew(constr, ...args) {
    //注意要判断传进来的 constr 是不是函数
    if (typeof constr !== 'function') {
        throw new Error(`the ${constr} is not a function`);
    }
    const obj = Object.create(constr.prototype);
    const res = constr.apply(obj, args);
    // 注意:function 也是函数对象
    const flag = res !== null && (typeof res === 'object' || typeof res === 'function');
    return flag ? res : obj;
}
function ConstrMyNew(arg) {
    this.x = arg;
    // return function R() {};
}
// 箭头函数会报错
// console.log('myNew 结果:', myNew(ConstrMyNew, 123), 'new 结果:', new ConstrMyNew(123));
/**
 * 实现 Promise
 */
const STATE = { PENDING: 'pending', RESOLVED: 'resolved', REJECTED: 'rejected' };
export function MyPromise(exec) {
    this.state = STATE.PENDING;
    this.value = undefined;
    this.resolvedCallbacks = [];
    this.rejectedCallbacks = [];

    const resolve = res => {
        if (this.state === STATE.PENDING) {
            this.state = STATE.RESOLVED;
            this.value = res;
            this.resolvedCallbacks.forEach(callback => {
                callback(this.value);
            });
        }
    };
    const reject = res => {
        if (this.state === STATE.PENDING) {
            this.state = STATE.REJECTED;
            this.value = res;
            this.rejectedCallbacks.forEach(callback => {
                callback(this.value);
            });
        }
    };

    try {
        exec(resolve, reject);
    } catch (e) {
        reject(e);
        console.log('这里可能会吞并一些内部错误:', e);
    }
}
MyPromise.prototype.then = function (onResolved, onRejected) {
    return new MyPromise((resolve, reject) => {
        if (typeof onResolved !== 'function') {
            onResolved = res => res;
        }
        if (typeof onRejected !== 'function') {
            onRejected = err => {
                throw err;
            };
        }
        if (this.state === STATE.RESOLVED) {
            queueMicrotask(() => {
                try {
                    const res = onResolved(this.value);
                    resolve(res);
                } catch (e) {
                    reject(e);
                }
            });
        } else if (this.state === STATE.REJECTED) {
            queueMicrotask(() => {
                try {
                    const res = onRejected(this.value);
                    resolve(res);
                } catch (e) {
                    reject(e);
                }
            });
        } else {
            this.resolvedCallbacks.push(() => {
                queueMicrotask(() => {
                    try {
                        const res = onResolved(this.value);
                        resolve(res);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
            this.rejectedCallbacks.push(() => {
                queueMicrotask(() => {
                    try {
                        const res = onRejected(this.value);
                        resolve(res);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
        }
    });
};
MyPromise.prototype.catch = function (reject) {
    return this.then(undefined, reject);
};
MyPromise.prototype.finally = function (onFinally) {
    return this.then(
        () => {
            onFinally();
            return this.value;
        },
        () => {
            onFinally();
            return this.value;
        },
    );
};
MyPromise.resolve = function (value) {
    // 注意:如果传入的值是 MyPromise 对象则直接返回
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
};
MyPromise.reject = function (err) {
    return new MyPromise((resolve, reject) => reject(err));
};

MyPromise.all = function (promises) {
    if (!Array.isArray(promises)) throw new Error(`the ${promises} is not a array`);

    return new MyPromise((resolve, reject) => {
        const result = [];
        const len = promises.length;
        let i = 0;

        promises.forEach((promise, index) => {
            MyPromise.resolve(promise).then(
                res => {
                    result[index] = res;
                    i++;
                    if (len === i) {
                        resolve(result);
                    }
                },
                err => {
                    reject(err);
                },
            );
        });
    });
};
MyPromise.allSettled = function (promises) {
    return new MyPromise(resolve => {
        const result = [];
        const len = promises.length;
        let i = 0;
        promises.forEach((promise, index) => {
            MyPromise.resolve(promise).then(
                res => {
                    result[index] = { status: 'resolved', value: res };
                    i++;
                    if (len === i) {
                        resolve(result);
                    }
                },
                err => {
                    result[index] = { status: 'rejected', value: err };
                    i++;
                    if (len === i) {
                        resolve(result);
                    }
                },
            );
        });
    });
};
MyPromise.race = function (promises) {
    if (!Array.isArray(promises)) throw new Error(`the ${promises} is not a array`);
    return new MyPromise((resolve, reject) => {
        promises.forEach(promise => {
            // MyPromise.resolve(promise).then(
            //     res => resolve(res),
            //     err => reject(err),
            // );
            MyPromise.resolve(promise).then(resolve, reject);
        });
    });
};
MyPromise.any = function (promises) {
    if (!Array.isArray(promises)) throw new Error(`the ${promises} is not a array`);
    return new MyPromise((resolve, reject) => {
        const errs = [];
        const len = promises.length;
        promises.forEach(promise => {
            MyPromise.resolve(promise).then(resolve, err => {
                errs.push(err);
                if (len === errs.length) {
                    reject(errs);
                    // reject(new AggregateError(errs));
                }
            });
        });
    });
};
// const p3 = new MyPromise((resolve, reject) => {
//     setTimeout(() => {
//         reject('p3');
//     }, 200);
// });
// const p4 = new MyPromise((resolve, reject) => {
//     setTimeout(() => {
//         reject('p4');
//     }, 150);
// });
// MyPromise.any([p3, p4])
//     .then(res => {
//         console.log('MyPromise.any [ p3 + p4 ] res--------------------->>>', res);
//     })
//     .catch(err => {
//         console.log('MyPromise.any [ p3 + p4 ] err--------------------->>>', err);
//         // console.log('MyPromise.any [ p3 + p4 ] err--------------------->>>', err.errors);
//     });
/**
 * 类型判断
 */
export function getType(value) {
    if (value === null) {
        return value + '';
    }
    if (typeof value === 'object') {
        return Object.prototype.toString
            .call(value)
            .slice(8, -1)
            .replace(/^(.)/, v1 => v1.toLowerCase());
    } else {
        return typeof value;
    }
}
// console.log('getType(null):', getType(null));
// console.log('getType(123):', getType(123));
// console.log('getType(str):', getType('str'));
// console.log('getType(class):', getType(class {}));
// console.log(
//     'getType(function):',
//     getType(function () {}),
// );
// console.log('getType(object):', getType({}));
// console.log('getType(date):', getType(new Date()));
// console.log('getType(array):', getType([]));
/**
 * 实现 call 函数
 */
Function.prototype.myCall = function (thisArg, ...args) {
    if (typeof this !== 'function') {
        throw new Error(`the ${this} is not a function`);
    }
    if (thisArg === null || thisArg === undefined) {
        // thisArg = window;
        const o = Object.create(null);
        const v = thisArg;
        o.valueOf = () => v;
        thisArg = o;
    } else {
        thisArg = Object(thisArg);
    }
    const fn = Symbol('fn');
    thisArg[fn] = this;
    const result = thisArg[fn](...args);
    delete thisArg[fn];
    return result;
};
function testMyCall(...args) {
    console.log('args ---------->>>', args);
    if (typeof this === 'object' && this !== null) {
        this.name = 'testMyCall';
    }
    return this;
}
// console.log('myCall 结果:', testMyCall.myCall({ x: 123 }, 1, 2, 3));
// console.log('myCall 结果:', testMyCall.myCall(null, 1, 2, 3) + 1);
// console.log('myCall 结果:', testMyCall.myCall(999, 1, 2, 3) + 1);
/**
 * 实现 apply 函数
 */
Function.prototype.myApply = function (thisArg, args = []) {
    if (typeof this !== 'function') {
        throw new Error(`the ${this} is not a function`);
    }

    if (thisArg === null || thisArg === undefined) {
        // thisArg = window;
        const o = Object.create(null);
        const v = thisArg;
        o.valueOf = () => v;
        thisArg = o;
    } else {
        thisArg = Object(thisArg);
    }
    const fn = Symbol('fn');
    thisArg[fn] = this;
    const result = thisArg[fn](...args);
    delete thisArg[fn];
    return result;
};
// console.log('myCall 结果:', testMyCall.myApply({ x: 123 }, [1, 2, 3]));
// console.log('myCall 结果:', testMyCall.myApply(null, [1, 2, 3]) + 1);
// console.log('myCall 结果:', testMyCall.myApply(999, [1, 2, 3]) + 1);
/**
 * 实现 bind 函数
 */
Function.prototype.myBind = function (thisArg, ...args1) {
    if (typeof this !== 'function') {
        throw new Error(`the ${this} is not a function`);
    }
    const fn = this;
    return function Fn(...args2) {
        const bindThis = this instanceof Fn ? this : thisArg;
        return fn.call(bindThis, ...args1, ...args2);
    };
};
// console.log('myCall 结果:', testMyCall.myBind({ x: 123 }, 1, 2, 3)(4, 5, 6));
// console.log('myCall 结果:', testMyCall.myBind(null, 1, 2, 3)(4, 5, 6));
// console.log('myCall 结果:', testMyCall.myBind(999, 1, 2, 3)(4, 5, 6));
// const bindTestMyCall = testMyCall.myBind({ x: 123 }, 1, 2, 3);
// const bindTestMyCall = testMyCall.bind({ x: 123 }, 1, 2, 3);
// console.log('bindTestMyCall 结果:', new bindTestMyCall(4, 5, 6));

/**
 * ajax 请求
 */
// import data from '../assets/data.json';
export function sendAjax() {
    // console.log('data', data);
    const xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        console.log('onreadystatechange readyState:', xhr.readyState);
    };
    xhr.onload = function () {
        if (xhr.readyState !== 4) return;
        if (xhr.status !== 200) {
            console.log('onload statusText ------>>>', xhr.statusText);
            return;
        }
        console.log('onload response ------>>>', xhr.response);
    };
    xhr.onerror = function () {
        console.log('onerror ------>>>', xhr);
    };
    xhr.onprogress = function () {
        console.log('onprogress readyState:', xhr.readyState); // readyState 为 3
    };
    xhr.responseType = 'json';
    xhr.open('get', 'http://localhost:3000/src/assets/data.json');
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.send(null);
}
// sendAjax();
/**
 * 使用 Promise 封装 ajax
 */
export function promiseAjax(config) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            console.log('onreadystatechange readyState:', xhr.readyState);
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200) {
                resolve(xhr.response);
                console.log('onload response ------>>>', xhr.response);
            } else {
                console.log('onload statusText ------>>>', xhr.statusText);
            }
        };
        xhr.onerror = function () {
            console.log('onerror ------>>>', xhr);
        };
        xhr.onprogress = function () {
            console.log('onprogress readyState:', xhr.readyState); // readyState 为 3
        };
        xhr.responseType = 'json';
        xhr.open(config.method, config.url);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send(config.params);
    });
}
// promiseAjax({ method: 'get', url: 'http://localhost:3000/src/assets/data.json' }).then(res => {
//     console.log('promiseAjax ------>>>', res);
// });
/**
 * 深拷贝
 */
const types = [Date, RegExp, Set, Map, WeakMap, WeakSet];
export function deepClone(obj, map = new WeakMap()) {
    // if (typeof obj === 'function') return obj;
    if (types.some(Type => obj instanceof Type)) {
        const Constr = obj.constructor;
        if (Constr && typeof Constr === 'function') {
            return new Constr(obj);
        }
    }
    if (obj instanceof Error) return obj;
    if (typeof obj === 'symbol') return Symbol(obj.description);
    if (typeof obj !== 'object' || obj === null) return obj;
    if (map.has(obj)) return map.get(obj);

    let newObj = Array.isArray(obj) ? [] : {};

    map.set(obj, newObj);

    Reflect.ownKeys(obj).forEach(key => {
        newObj[key] = deepClone(obj[key], map);
    });

    Object.setPrototypeOf(newObj, Object.getPrototypeOf(obj));

    return newObj;
}
/**
 * sleep 函数
 */
export async function asyncSleep(delay) {
    await new Promise(resolve => {
        setTimeout(resolve, delay);
    });
}
// asyncSleep(1000).then(() => {
//     console.log('我睡了 1s');
// });
/**
 * 日期格式化函数
 */
export function formatDate(date, format) {
    if (!(date instanceof Date)) {
        if (!date) return '';
        date = new Date(date);
    }
    if (Number.isNaN(date)) return '';
    const opt = {
        'y+': date.getFullYear(),
        'M+': date.getMonth() + 1,
        'd+': date.getDate(),
        'h+': date.getHours(),
        'm+': date.getMinutes(),
        's+': date.getSeconds(),
    };
    for (let key in opt) {
        const val = opt[key];
        const reg = new RegExp(key);
        console.log('reg', reg);
        format = format.replace(reg, String(val).padStart(2, '0'));
    }
    return format;
}
// console.log('formatDate 结果:', formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss'));
/**
 * 交换 a b 的值,不用临时变量
 */
export function exch(a, b) {
    a = a + b;
    b = a - b;
    a = a - b;
    return { a, b };
}
// console.log('exch 结果:', exch(2, 6));
/**
 * 数组乱序
 */
export function randomArr(arr) {
    // 方法一:使用 sort 方法返回随机数
    // return arr.sort(() => (Math.random() > 0.5 ? -1 : 1));
    // for (var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
    // 方法二:取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换。
    // const len = arr.length;
    // for (let i = 0; i < arr.length; i++) {
    //     const v = arr[i];
    //     const randomIndex = (Math.random() * (len - 1 - i) + i) >>> 0;
    //     arr[i] = arr[randomIndex];
    //     arr[randomIndex] = v;
    // }
    // 方法三:倒倒序遍历
    let len = arr.length;
    for (let i = len - 1; i >= 0; i--) {
        const v = arr[i];
        const index = (Math.random() * i) >>> 0;
        arr[i] = arr[index];
        arr[index] = v;
    }
    return arr;
}
// console.log('randomArr --->>>', randomArr([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
/**
 * 数组求和
 */
export function add(arr) {
    if (!Array.isArray(arr)) return 0;
    return arr.reduce((sum, cur) => {
        return sum + cur;
    });
}
// console.log('add 结果:', add([0, 1, 2, 3, 4, 5]));
export function add2(arr) {
    if (!Array.isArray(arr)) return 0;
    // return arr
    //     .toString()
    //     .split(',')
    //     .reduce((sum, cur) => {
    //         return sum + Number(cur);
    //     }, 0);
    return arr.flat(Infinity).reduce((sum, cur) => {
        return sum + cur;
    });
}
// console.log('add2 结果:', add2([1, 2, 3, [[4, 5], 6], 7, 8, 9]));
/**
 * 数组扁平化
 */
export function flatArray(arr) {
    // 1
    // return arr.flat(Infinity);

    // 2
    // return arr.reduce((res, cur) => {
    //     return res.concat(Array.isArray(cur) ? flatArray(cur) : cur);
    // }, []);

    // 3
    // let res = [];
    // for (const val of arr) {
    //     if (Array.isArray(val)) {
    //         res.push(...flatArray(val));
    //         // res = res.concat(flatArray(val));
    //     } else {
    //         res.push(val);
    //     }
    // }
    // return res;

    // 4
    return arr
        .toString()
        .split(',')
        .map(val => Number(val));
}
// console.log('flatArray 结果:', flatArray([1, [2, [3, 4, 5], [6, [7, [8], [9]]]]]));
/**
 * 数组去重
 */
export function unique(arr) {
    if (!Array.isArray(arr)) return arr;
    // 方法一
    // return Array.from(new Set(arr));
    // return [...new Set(arr)];

    // 方法二:
    // const set = new Set();
    // const res = [];
    // arr.forEach(val => {
    //     if (!set.has(val)) {
    //         set.add(val);
    //         res.push(val);
    //     }
    // });
    // return res;

    // 方法三:会把 {} 这种当成重复的去掉
    // const map = {};
    // const res = [];
    // arr.forEach(val => {
    //     if (!map[val]) {
    //         map[val] = true;
    //         res.push(val);
    //     }
    // });
    // return res;
}
// console.log('unique 结果:', unique([1, 1, 2, 3, 4, 5, 4, 3, 2, 1, 4, 5, 3, 2, 1, 2, 3, 4, 5, 2, 1, 3, 6, 3]));
// unique([1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]);
/**
 * 数组的 flat 方法
 */
Array.prototype.myFlat = function (depth = 1) {
    if (!Array.isArray(this)) throw new Error(`the ${this} not is a array`);

    let dep = 0;
    const _flat = arr => {
        dep++;
        return arr.reduce((res, cur) => {
            return res.concat(Array.isArray(cur) && dep < depth ? _flat(cur) : cur);
        }, []);
    };

    return _flat(this);
};
// console.log('myFlat 结果:', [1, [2, [3, 4, 5], [6, [7, [8], [9]]]]].myFlat(7));
/**
 * 数组的 push 方法
 * 注意:
 *      push 方法返回数组的长度
 *      push 可以对类数组使用
 *      对对象使用时,Array.prototype.push.call({}, 1)会返回 {0: 1, length: 1}
 *      如果为 null 或者 undefined 则报错
 */
Array.prototype.myPush = function (...items) {
    if (!Array.isArray(this)) throw new Error(`the ${this} not is a array`);

    for (const item of items) {
        this[this.length] = item;
    }
    return this.length;
};
/**
 * 数组的 filter 方法
 */
Array.prototype.myFilter = function (callback, thisArg) {
    if (!Array.isArray(this)) {
        throw new Error(`the ${this} not is a array`);
    }
    if (typeof callback !== 'function') {
        throw new Error(`the ${callback} not is a function`);
    }

    const res = [];
    const len = this.length;
    for (let i = 0; i < len; i++) {
        const flag = callback.call(thisArg, this[i], i, this);
        if (flag) {
            res.push(this[i]);
        }
    }

    return res;
};
// console.log(
//     'myPush 结果:',
//     [1, 2, 3].myFilter(function (cur, index) {
//         console.log('this cur index::: ', this, cur, index);
//         return cur > 1;
//     }),
// );
/**
 * 数组的 map 方法
 */
Array.prototype.myMap = function (callback, thisArg) {
    if (!Array.isArray(this)) {
        throw new Error(`the ${this} not is a array`);
    }
    if (typeof callback !== 'function') {
        throw new Error(`the ${callback} not is a function`);
    }

    const res = [];
    const len = this.length;
    for (let i = 0; i < len; i++) {
        const newVal = callback.call(thisArg, this[i], i, this);
        res.push(newVal);
    }

    return res;
};
// console.log(
//     'myPush 结果:',
//     [1, 2, 3].myMap((cur, index) => {
//         console.log('this cur index::: ', this, cur, index);
//         return cur * 10;
//     }),
// );
export function repeat(str, n) {
    if (typeof str !== 'string') {
        throw new Error(`the ${str} not is a string`);
    }
    if (n < 1) return '';

    // let res = '';
    // while (n > 0) {
    //     console.log(str, n);
    //     res += str;
    //     n--;
    // }
    // return res;

    return new Array(n + 1).join(str);
}
// console.log('repeat 结果:', repeat('abc-', 3));
/**
 * 字符串翻转
 */
export function reverse(str) {
    if (typeof str !== 'string') return str;
    return str.split('').reverse().join('');
}
// console.log('reverse 结果:', reverse('abc-'));
/**
 * 将数字每千分位用逗号隔开
 * 注意:
 *      要考虑小数的情况
 *      要考虑负数的情况
 *      要考虑不足三位的情况
 */
export function comma(num) {
    if (typeof num !== 'number') return num;
    // 方法一:正则
    // return String(num).replace(/\B(?=(\d{3})+(?!\d))/g, ',');

    // 方法二:转成数组,添加豆号
    // const str = String(Math.abs(num));
    // const flag = num < 0;
    // const [a, b] = str.split('.');
    // const transform = val => {
    //     if (!val) return '';
    //     const nums = val.split('');
    //     const len = nums.length;
    //     for (let i = len; i > 3; i -= 3) {
    //         nums.splice(i - 3, 0, ',');
    //     }
    //     return nums.join('');
    // };
    // const res = `${flag ? '-' : ''}${transform(a)}`;
    // return b ? `${res}.${transform(b)}` : res;

    // 方法三:使用正则匹配出来
    const str = String(Math.abs(num));
    const flag = num > 0;
    const [a, b] = str.split('.');
    const transform = val => {
        if (!val) return '';
        const len = val.length;
        if (len < 3) return val;
        const n = len % 3;
        if (n > 0) {
            const arr = val.slice(n, len).match(/\d{3}/g);
            return `${val.slice(0, n)},${arr ? arr.join(',') : ''}`;
        } else {
            const arr = val.match(/\d{3}/g);
            return arr ? arr.join(',') : '';
        }
    };
    const res = `${flag ? '-' : ''}${transform(a)}`;
    return b ? `${res}.${transform(b)}` : res;
}

// console.log('comma 结果:', comma(12345678.12345678));
// console.log(comma(0));
// console.log(comma(123));
// console.log(comma(-123));
// console.log(comma(1234567));
// console.log(comma(-1234567));
// console.log(comma(1234567.12345678));
// console.log(comma(-1234567.12345678));
/**
 * 大数相加
 */
export function sumBigNumber(a, b) {
    if (typeof a !== 'string' || typeof b !== 'string') return '';
    if (isNaN(a) || isNaN(b)) return '';

    a = a.split('');
    b = b.split('');

    let res = '';
    let r = 0;

    while (a.length > 0 || b.length > 0) {
        // r = (Number(a.pop()) || 0) + (Number(b.pop()) || 0) + r;
        r = ~~a.pop() + ~~b.pop() + r;
        res = (r % 10) + res;
        // r = Math.floor(r / 10);
        r = r > 9;
    }

    if (r > 0) {
        res = 1 + res;
    }

    return res.replace(/^0+/, '');
}
// console.log('sumBigNumber 结果:', sumBigNumber('012345678', '0123456'));
/**
 * 大数相乘
 */
export function multiplyBigNumber(a, b) {
    if (typeof a !== 'string' || typeof b !== 'string') return '';
    if (isNaN(a) || isNaN(b)) return '';
    if (a === '0' || b === '0') return '0';

    const len1 = a.length;
    const len2 = b.length;
    const res = [];

    for (let i = len1 - 1; i >= 0; i--) {
        for (let j = len2 - 1; j >= 0; j--) {
            const i1 = i + j;
            const i2 = i + j + 1;
            const r = a[i] * b[j] + (res[i2] || 0);
            res[i1] = Math.floor(r / 10) + (res[i1] || 0);
            res[i2] = r % 10;
        }
    }

    return res.join('').replace(/^0+/, '');
}
// console.log('multiplyBigNumber 结果:', multiplyBigNumber('01234', '2'));
// console.log('multiplyBigNumber 结果:', multiplyBigNumber('012345678', '0123456'));
/**
 * 将对象转换为树形结构
 */
export function objToTree(arr) {
    if (!Array.isArray(arr)) return [];
    const tree = [];
    const map = new Map();
    for (let item of arr) {
        const { id, pid } = item;
        const children = map.get(id);
        if (children) {
            item.children = children;
        } else {
            item.children = [];
            map.set(id, item.children);
        }
        if (pid) {
            const children = map.get(pid);
            if (children) {
                children.push(item);
            } else {
                map.set(pid, [item]);
            }
        } else {
            tree.push(item);
        }
    }
    return tree;
}
// 转换前:
const source = [
    {
        id: 1,
        pid: 0,
        name: 'body',
    },
    {
        id: 2,
        pid: 1,
        name: 'title',
    },
    {
        id: 3,
        pid: 2,
        name: 'div',
    },
];
// 转换为:
// const tree = [{
//   id: 1,
//   pid: 0,
//   name: 'body',
//   children: [{
//     id: 2,
//     pid: 1,
//     name: 'title',
//     children: [{
//       id: 3,
//       pid: 1,
//       name: 'div'
//     }]
//   }
// }]
// console.log(' 结果:', objToTree(source));
/**
 * 使用ES5和ES6求函数参数的和
 */
export function funArgSum(...args) {
    return args.reduce((sum, cur) => sum + Number(cur), 0);
}
export function funArgSum2() {
    return Array.prototype.reduce.call(arguments, (sum, cur) => sum + Number(cur), 0);
}
// console.log('funArgSum 结果:', funArgSum(1, 2, 3, 4, 5));
// console.log('funArgSum2 结果:', funArgSum2(1, 2, 3, 4, 5));
/**
 * 解析 URL Params 为对象
 * let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
   parseParam(url)  结果
    { 
        user: 'anonymous',
        id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
        city: '北京', // 中文需解码
        enabled: true, // 未指定值得 key 约定为 true
    }
*/
export function parseParams(url) {
    if (typeof url !== 'string') return {};

    const [, search] = url.split('?');
    if (!search) return {};
    const arr = search.split('&');
    const result = {};
    arr.forEach(param => {
        if (param.includes('=')) {
            const [key, val] = param.split('=');
            const v = result[key];
            if (result.hasOwnProperty(key)) {
                result[key] = [].concat(v, val);
            } else {
                result[key] = window.decodeURI(val);
            }
        } else {
            result[param] = true;
        }
    });

    return result;
}
// console.log(
//     'parseParams 结果:',
//     parseParams('http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled'),
// );
/**
 *  二维数组斜向打印
 */
export function printMatrix(arr) {
    if (!Array.isArray(arr)) return [];

    const len1 = arr.length;
    const len2 = arr[0].length;
    const res = [];
    // 左上角,从0 到 n - 1 列进行打印
    for (let i = 0; i < len2; i++) {
        for (let x = 0, y = i; x < len1 && y >= 0; x++, y--) {
            res.push(arr[x][y]);
        }
    }
    // 右下角,从1 到 n - 1 行进行打印
    for (let i = 1; i < len1; i++) {
        for (let x = i, y = len2 - 1; x < len1 && y >= 0; x++, y--) {
            res.push(arr[x][y]);
        }
    }
    return res;
}

// console.log(
//     'printMatrix 结果;',
//     printMatrix([
//         [1, 2, 3],
//         [4, 5, 6],
//         [7, 8, 9],
//         [10, 11, 12],
//     ]),
// );
// [0,0]
// [0,1] [1,0]
// [0,2] [1,1] [2,0]
// [1,2] [2,1] [3,0]
// [2,2] [3,1]
// [3,2]
/**
 * 找出 Element 元素的全部 Input 子元素
 */
export function findAllInputElement(element) {
    if (!(element instanceof Element)) return [];
    const res = [];
    const recursion = element => {
        if (element.nodeName.toUpperCase() === 'DIV') {
            res.push(element);
        }
        const children = element.children;
        if (children.length > 0) {
            for (let item of element.children) {
                recursion(item);
            }
        }
    };
    recursion(element);
    return res;
}
// setTimeout(() => {
//     console.log('findAllInputElement 结果:', findAllInputElement(document.getElementById('app')));
// }, 1000);
/**
 * 将手机号中间四位变成 *
 */
export function hidePhoneNumber(str) {
    if (typeof str !== 'string') return '';
    // return str.slice(0, 3) + '****' + str.slice(-4);
    return str.replace(/^(\d{3})\d*(\d{4}$)/, '$1****$2');
}
// console.log('hidePhoneNumber 结果:', hidePhoneNumber('13612361236'));
/**
 * 循环打印红黄绿
 * 1. 循环打印红黄绿
 * 下面来看一道比较典型的问题,通过这个问题来对比几种异步编程方法:
 * 红灯 3s 亮一次,绿灯 1s 亮一次,黄灯 2s 亮一次;如何让三个灯不断交替重复亮灯?
 */
export function loopPrint() {
    function red() {
        console.log('red');
    }
    function green() {
        console.log('green');
    }
    function yellow() {
        console.log('yellow');
    }

    function sleep(delay) {
        return new Promise(resolve => {
            setTimeout(resolve, delay);
        });
    }
    async function task() {
        await sleep(3000);
        red();
        await sleep(1000);
        green();
        await sleep(2000);
        yellow();

        task();
    }
    task();

    // function sleep(delay, callback) {
    //     return new Promise(resolve => {
    //         setTimeout(() => {
    //             callback();
    //             resolve();
    //         }, delay);
    //     });
    // }
    // async function task() {
    //     sleep(3000, red)
    //         .then(() => sleep(1000, green))
    //         .then(() => sleep(2000, yellow))
    //         .then(task);
    // }
    // task();
}
// console.log('loopPrint 结果:', loopPrint());
/**
 * 实现每隔一秒打印 1,2,3,4
 */
export function printNumber(n) {
    for (let i = 1; i <= n; i++) {
        setTimeout(() => {
            console.log('i: ', i);
        }, 1000 * i);
    }
}
// console.log('loopPrint 结果:', printNumber(4));
/**
 * 小孩报数问题
 */
function childNum(num, count) {
    // 方法一
    const arr = new Array(num).fill(1).map((v, i) => i + 1);
    let curIndex = 0;
    let curNum = 1;
    while (arr.length > 1) {
        if (curNum === count) {
            curNum = 1;
            arr.splice(curIndex, 1);
        } else {
            curIndex++;
            curNum++;
        }

        if (curIndex === arr.length) {
            curIndex = 0;
        }
    }
    return arr[0];
    // 方法二
    // const arr = new Array(num).fill(1).map((v, i) => i + 1);
    // let execCount = 0;
    // let curIndex = 0;
    // let curNum = 0;
    // while (execCount < num - 1) {
    //     if (arr[curIndex] !== 0) curNum++;

    //     if (curNum === count) {
    //         curNum = 0;
    //         arr[curIndex] = 0;
    //         execCount++;
    //     }
    //     curIndex++;
    //     if (curIndex === num) {
    //         curIndex = 0;
    //     }
    //     console.log(execCount, arr);
    // }
    // return arr.find(n => n !== 0);
}
// console.log('childNum 结果:', childNum(30, 3));
/**
 * 用 Promise 实现图片的异步加载
 */
export function asyncLoadImage(url) {
    return new Promise((resolve, reject) => {
        if (!url || !String(url).match(/^https?:\/\//)) {
            reject('图片地址错误!');
        }

        const img = new Image();
        img.src = url;
        img.onload = function () {
            resolve(img);
        };
        img.onerror = function (err) {
            reject(err);
        };
    });
}
// asyncLoadImage(
//     'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648885122&t=02370f63aa93f0f3526f0750589a2b3a',
// )
//     .then(img => {
//         console.log('加载成功:', img);
//     })
//     .catch(error => {
//         console.log('加载失败:', error);
//     });
/**
 * 查找文章中出现频率最高的单词
 */
export function findMostWord(str) {
    if (typeof str !== 'string') return [];
    str = str.trim().toLowerCase();
    const arr = str.match(/\w+/g);
    let max = 0;
    let res = '';
    const map = new Map();
    arr.forEach(val => {
        if (map.has(val)) {
            const v = map.get(val) + 1;
            map.set(val, v);
            if (v > max) {
                max = v;
                res = val;
            }
        } else {
            map.set(val, 1);
        }
    });
    return [res, map.get(res)];
}
// console.log(
//     findMostWord(
//         'Age has reached the end of the beginning of a word. May be guilty in his seems to passing a lot of different life became the appearance of the same day;',
//     ),
// );
/**
 * 封装异步的fetch,使用async await方式来使用
 */
export class FetchHttpRequest {
    /**
     * get 请求
     */
    static async get(url, search) {
        const params = new URLSearchParams(search);
        const response = await fetch(params ? `${url}${params}` : url);
        const data = await response.json();
        return data;
    }
    /**
     * post 请求
     */
    static async post(url, data) {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        });
        const result = await response.json();
        return result;
    }
}
// FetchHttpRequest.get('http://localhost:3000/src/assets/data.json').then(res => {
//     console.log('请求结果:', res);
// });
// FetchHttpRequest.post('http://localhost:3000/src/assets/data.json', { name: 'lhd', age: 18 }).then(res => {
//     console.log('请求结果:', res);
// });
/**
 * 实现 prototype 继承
 */
export function inheritPrototype(Super, Sub) {
    const proto = Object.create(Super.prototype);
    // proto.constructor = Sub;
    Object.defineProperty(proto, 'constructor', {
        configrable: true,
        enumerable: false,
        writable: true,
        value: Sub,
    });
    Sub.prototype = proto;
    Object.setPrototypeOf(Sub, Super);
}
// //父方法
// function SupperFunction(flag1) {
//     this.flag1 = flag1;
// }
// //子方法
// function SubFunction(flag2) {
//     SupperFunction.call(this, true);
//     this.flag2 = flag2;
// }
// inheritPrototype(SupperFunction, SubFunction);
// //子实例
// var subInstance = new SubFunction(false);
// //子调用自己和父的属性
// console.log('子类:', subInstance, subInstance);
/**
 * 实现双向数据绑定
 */
function twoWayDataBinding(params: type) {
    const dom = document.getElementById('app');
    const span = document.createElement('span');
    const input = document.createElement('input');
    dom.appendChild(input);
    dom.appendChild(span);
    const render = val => {
        input.value = val;
        span.innerHTML = val;
    };
    const obj = {};
    // const _obj = {};
    // Object.defineProperty(obj, 'text', {
    //     configurable: true,
    //     enumerable: true,
    //     get() {
    //         return _obj.text;
    //     },
    //     set(val) {
    //         _obj.text = val;
    //         render(val);
    //     },
    // });
    const proxyObj = new Proxy(obj, {
        get(target, key) {
            return Reflect.get(target, key);
        },
        set(target, key, val) {
            render(val);
            return Reflect.set(target, key, val);
        },
    });
    input.addEventListener('input', function (e) {
        proxyObj.text = e.target.value;
    });
}
// setTimeout(() => {
//     twoWayDataBinding();
// }, 1000);
/**
 * 实现简单路由
 */
export class Router {
    constructor() {
        this.routes = {};
        this.currentHash = '';
        this.freshRoute = this.freshRoute.bind(this);
        window.addEventListener('load', this.freshRoute, false);
        window.addEventListener('hashchange', this.freshRoute, false);
    }
    // 存储路由
    storeRoute(path, callback) {
        this.routes[path] = callback || function () {};
    }
    // 更新路由
    freshRoute() {
        this.currentHash = location.hash.slice(1) || '/';
        this.routes[this.currentHash]();
    }
}
// const router = new Router();
// router.storeRoute('/', () => console.log('//////'));
// router.storeRoute('test', () => console.log('哈哈哈'));
/**
 * 使用 setTimeout 实现 setInterval
 */
export function mySetInterval(callback, delay) {
    // let obj = { timer: null };
    // const fn = () => {
    //     obj.timer = setTimeout(() => {
    //         callback();
    //         fn.call(this);
    //     }, delay);
    // };
    // fn();
    // return obj;
    let obj = { timer: null };
    const interval = () => {
        callback();
        obj.timer = setTimeout(interval, delay);
    };
    setTimeout(interval, delay);
    return obj;
}
// const obj = mySetInterval(() => {
//     console.log(new Date().getTime());
// }, 1000);
// setTimeout(() => {
//     clearTimeout(obj.timer);
// }, 6000);
/**
 * F
 */
/**
 * 判断对象是否存在循环引用
//  */
function isObject(val) {
    return typeof val === 'object' && val !== null;
}
export function isLoopObject(obj, set = new WeakSet()) {
    if (!isObject(obj)) return false;
    let res = false;
    for (let key of Object.keys(obj)) {
        const val = obj[key];
        if (!isObject(val)) continue;
        if (set.has(val)) return true;
        set.add(val);
        res = isLoopObject(val, set);
    }
    return res;
}
// const o1 = { x: 1, y: { z: 3 } };
// console.log('判断对象是否存在循环引用:', isLoopObject(o1));
// const o2 = { x: 2, o1 };
// const obj = { o1, o2 };
// obj.o1.o2 = o2;
// console.log('判断对象是否存在循环引用:', isLoopObject(obj));
// var a = {
//     b: {
//         c: {},
//     },
// };
// a.b.c.d = a;
// console.log('判断对象是否存在循环引用:', isLoopObject(a));