IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    面向面试编程 · 看不见我的美 · 是你瞎了眼

    馬腊咯稽发表于 2020-03-11 00:00:00
    love 0
    面向面试编程

    for 循环打印 0-9

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    for (var i = 0; i < 10; i++) {
     (function (i) {
     setTimeout(() => {
     console.log(i);
     }, 1000);
     })(i);
    }
    for (var i = 0; i < 10; i++) {
     setTimeout(
     i => {
     console.log(i);
     },
     1000,
     i
     );
    }
    for (var i = 0; i < 10; i++) {
     setTimeout(console.log(i), 1000);
    }
    for (var i = 0; i < 10; i++) {
     setTimeout(console.log.bind(null, i), 1000);
    }
    for (var i = 0; i < 10; i++) {
     try {
     throw i;
     } catch (i) {
     setTimeout(() => {
     console.log(i);
     }, 1000);
     }
    }
    for (let i = 0; i < 10; i++) {
     setTimeout(() => {
     console.log(i);
     }, 1000);
    }
    

    Proxy 实现数据绑定

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    <body>
     <input type="text" id="model" />
     <p id="word"></p>
    </body>
    <script>
     const inputEle = document.querySelector('#model');
     const pEle = document.querySelector('#word');
     const o1 = {};
     const o2 = new Proxy(o1, {
     get(target, key, receiver) {
     console.log('get');
     console.info(target);
     console.info(key);
     console.info(receiver);
     return Reflect.get(target, key, receiver);
     },
     set(target, key, value, receiver) {
     console.log('set');
     console.info(target);
     console.info(key);
     console.info(value);
     console.info(receiver);
     if (key === 'text') {
     inputEle.value = value;
     pEle.innerText = value;
     }
     return Reflect.set(target, key, value, receiver);
     }
     });
     inputEle.addEventListener('input', e => {
     console.log(e);
     o2.text = e.target.value;
     });
    </script>
    

    实现 apply、call、bind

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    Function.prototype.apply = function (context = window, args = []) {
     if (typeof this !== 'function') {
     throw new Error('apply 方法只能在函数上调用');
     }
     const fn = Symbol('fn');
     context[fn] = this;
     const result = context[fn](...args);
     delete context[fn];
     return result;
    };
    Function.prototype.call = function (context = window, ...args) {
     console.log(this); // 谁调用了 call 方法 => example
     console.log(context); // 传入的第一个参数 => eo
     if (typeof this !== 'function') {
     throw new Error('call 方法只能在函数上调用');
     }
     const fn = Symbol('fn'); // 声明一个不等于任何值的变量
     context[fn] = this; // 将 example 作为待绑定对象的一个方法
     const result = context[fn](...args); // 保存调用结果
     delete context[fn]; // 删除手动添加的属性
     return result; // 将结果返回
    };
    Function.prototype.bind = function (context = window, ...params1) {
     if (typeof this !== 'function') {
     throw new Error('bind 方法只能在函数上调用');
     }
     const that = this;
     function bindFn(...params2) {
     // 判断是否用于构造函数
     if (this instanceof bindFn) {
     // new 的优先级比 bind 高,new 出来的对象的构造函数仍然指向 that
     return new that(...params1, ...params2);
     }
     return that.call(context, ...params1, ...params2);
     }
     return bindFn;
    };
    let eo = { a: 1 };
    function example(...rest) {
     console.log(this);
     setTimeout(() => {
     console.log(this);
     }, 0);
     console.log(rest);
    }
    example.call(eo, 1, 2, 3);
    example.apply(eo, [4, 5, 6]);
    example.bind(eo, [7, 8, 9])();
    

    实现 compose

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    function compose(...fns) {
     let ctx = this;
     let temp = null;
     let length = fns.length;
     return function fn(...args) {
     temp = fns[length - 1].call(ctx, ...args);
     if (length > 1) {
     length--;
     return fn.call(ctx, temp);
     } else {
     return temp;
     }
     };
    }
    // 测试代码
    const a = msg => `${msg}a`;
    const b = msg => `${msg}b`;
    const c = msg => `${msg}c`;
    const f = compose(a, b, c);
    console.log(f('hello, ')); // hello, cba
    

    实现 instanceof

    决定类型的是 prototype,而不是构造函数:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function instanceOf(L, R) {
     if (L === null || L === undefined) {
     return false;
     }
     L = Object.getPrototypeOf(L);
     R = R.prototype;
     while (L) {
     if (L === R) {
     return true;
     }
     L = Object.getPrototypeOf(L);
     }
     return false;
    }
    console.log(instanceOf(Symbol(), Function)); // false
    console.log(instanceOf({}, Object)); // true
    console.log(instanceOf([], Array)); // true
    

    顺带,Symbol.toStringTag 属性可以影响 Object.prototype.toString 的结果:

    1
    2
    3
    4
    5
    
    const temp = {
     [Symbol.toStringTag]: 'Hello'
    };
    const toString = Object.prototype.toString;
    console.log(toString.call(temp)); // [object Hello]
    

    实现 flatten

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    function flattenOne(arr) {
     return arr.reduce(
     (acc, val) =>
     Array.isArray(val) ? [...acc, ...flattenOne(val)] : [...acc, val],
     []
     );
    }
    function flattenTwo(arr) {
     return arr.toString().split(',').map(Number);
    }
    function flattenThree(arr) {
     return JSON.parse(`[${JSON.stringify(arr).replace(/\[|\]/g, '')}]`);
    }
    const arr = [
     [1, 2, 2],
     [3, 4, 5, 5],
     [6, 7, 8, 9, [11, 12, [12, 13, [14]]]],
     10
    ];
    console.log(flattenOne(arr));
    console.log(flattenTwo(arr));
    console.log(flattenThree(arr));
    

    实现 jsonp

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    function jsonp(url, data, cb) {
     let dataStr = url.indexOf('?') >= 0 ? '&' : '?';
     for (let kv of Object.entries(data)) {
     dataStr += `${kv[0]}=${kv[1]}&`;
     }
     const cbName = `callback${Math.floor(Math.random() * 10)}`;
     dataStr = dataStr + `callback=${cbName}`;
     const scriptEle = document.createElement('script');
     scriptEle.src = url + dataStr;
     console.log(scriptEle.src);
     window[cbName] = function (data) {
     cb(data);
     document.body.removeChild(scriptEle);
     };
     document.body.appendChild(scriptEle);
    }
    jsonp(
     'https://dd-search.jd.com/?terminal=pc&newjson=1&ver=2&zip=1',
     { key: 'Apple' },
     function (data) {
     console.log(data);
     }
    );
    

    实现 map(使用 reduce)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    Array.prototype.implementMapUsingReduce = function (fn) {
     const list = this;
     return list.reduce((acc, cur, idx, src) => {
     acc[idx] = fn(cur, idx, src);
     return acc;
     }, []);
    };
    const a = [1, 2, 3, 4].implementMapUsingReduce(a => a + 1);
    const b = ['a', 'b', 'c'].implementMapUsingReduce(a => a + '!');
    console.log(a); // [2, 3, 4, 5]
    console.log(b); // ['a!', 'b!', 'c!']
    

    实现 reduce

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    Array.prototype.implementReduce = function (fn, initValue) {
     const list = this;
     if (!Array.isArray(list)) {
     throw new Error('不能在非数组上调用 reduce');
     }
     if (list.length === 0) {
     throw new Error('不能在空数组上调用 reduce');
     }
     let value = initValue === undefined ? list[0] : initValue;
     list.forEach((item, index, arr) => {
     if (index === 0) {
     if (initValue !== undefined) {
     value = fn(value, item, index, arr);
     } else {
     // 没有给初始值的时候,index = 1 才开始执行 fn!!!
     }
     } else {
     value = fn(value, item, index, arr);
     }
     });
     return value;
    };
    const a = [1, 2, 3, 4].implementReduce((acc, cur) => acc + cur);
    const b = [
     [0, 1],
     [2, 3],
     [4, 5]
    ].implementReduce((acc, cur) => acc.concat(cur), []);
    console.log(a); // 10
    console.log(b); // [0, 1, 2, 3, 4, 5]
    

    实现 new

    1
    2
    3
    4
    5
    6
    
    function customNew() {
     const con = [].shift.call(arguments); // 获取构造函数
     const obj = Object.create(con.prototype);
     const temp = con.apply(obj, arguments);
     return temp instanceof con ? temp : obj;
    }
    

    实现 setInterval/setTimeout(使用 requestAnimationFrame)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function setInterval(cb, time) {
     let getNow = () => Date.now();
     let startTime = getNow();
     let endTime = startTime;
     let timer;
     let loop = () => {
     timer = window.requestAnimationFrame(loop);
     let endTime = getNow();
     if (endTime - startTime > time) {
     endTime = startTime = getNow();
     cb(timer);
     // window.cancelAnimationFrame(timer) // 如果是 setTimeout,放开这一行
     }
     };
     timer = window.requestAnimationFrame(loop);
     return timer;
    }
    

    实现 sleep

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    async function sleep(time) {
     console.time('sleep');
     await new Promise(resolve => {
     setTimeout(resolve, time);
     });
    }
    sleep(3000).then(() => {
     console.timeEnd('sleep');
    });
    

    实现 EventEmitter

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    class EventEmitter {
     _events = Object.create(null);
     add(type, handler) {
     if (!this._events[type]) {
     this._events[type] = [];
     }
     this._events[type].push(handler);
     }
     remove(type, handler) {
     if (!this._events[type]) {
     return;
     }
     if (handler) {
     this._events[type] = this._events[type].filter(item => item !== handler);
     } else {
     delete this._events[type];
     }
     }
     once(type, handler) {
     const temp = (...args) => {
     handler(...args);
     this.remove(type, temp);
     };
     this.add(type, temp);
     }
     emit(type, ...args) {
     if (!this._events[type]) {
     return;
     }
     this._events[type].forEach(handler => {
     handler(...args);
     });
     }
    }
    

    实现 LazyMan

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    class LazyManClass {
     constructor(name) {
     this.name = name;
     this.doSomething = [];
     console.log(`Hi I am ${this.name}`);
     setTimeout(() => {
     this.next();
     }, 0);
     }
     eat(food) {
     const sth = (food => () => {
     console.log(`I am eating ${food}`);
     this.next();
     })(food);
     this.doSomething.push(sth);
     return this;
     }
     sleep(time) {
     const sth = (time => () => {
     setTimeout(() => {
     console.log(`等待了${time}秒...`);
     this.next();
     }, time * 1000);
     })(time);
     this.doSomething.push(sth);
     return this;
     }
     sleepFirst(time) {
     var sth = (time => () => {
     setTimeout(() => {
     console.log(`等待了${time}秒...`);
     this.next();
     }, time * 1000);
     })(time);
     // 将任务添加到最前边
     this.doSomething.unshift(sth);
     return this;
     }
     next() {
     let doSth = this.doSomething.shift();
     typeof doSth === 'function' && doSth();
     }
    }
    function LazyMan(name) {
     return new LazyManClass(name);
    }
    LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('juice');
    

    实现 Object.assign

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    Object.defineProperty(Object, 'customAssign', {
     value(target) {
     if (!target) {
     throw new Error('>_<');
     }
     const sources = [...arguments].slice(1);
     for (let source of sources) {
     for (let attr in source) {
     if (Object.hasOwnProperty.call(source, attr)) {
     target[attr] = source[attr];
     }
     }
     }
     return target;
     },
     enumerable: false
    });
    var a = {
     qq: 123456789,
     name: 'Cardi B',
     songs: {
     Edfsda: 1.99,
     Gfgdfi: 2.99
     }
    };
    var b = {
     qq: 987467546767,
     sex: 'female',
     fans: {
     Vfdgre: 20,
     Rfefes: 18
     }
    };
    console.log(Object.customAssign(b, a));
    

    实现 Promise

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    
    function MyPromise(executor) {
     const self = this;
     self.data = undefined;
     self.status = 'pending';
     self.onFulfilledCbs = [];
     self.onRejectedCbs = [];
     function fulfill(value) {
     if (self.status !== 'pending') {
     return;
     }
     self.status = 'fulfilled';
     self.data = value;
     self.onFulfilledCbs.forEach(fn => fn(value));
     }
     function reject(reason) {
     if (self.status !== 'pending') {
     return;
     }
     self.status = 'rejected';
     self.data = reason;
     self.onRejectedCbs.forEach(fn => fn(reason));
     }
     try {
     executor(fulfill, reject);
     } catch (e) {
     reject(e);
     }
    }
    MyPromise.prototype.then = function (onFulFilled, onRejected) {
     const self = this;
     let promise = undefined;
     onFulFilled =
     typeof onFulFilled === 'function'
     ? onFulFilled
     : function (v) {
     return v;
     };
     onRejected =
     typeof onRejected === 'function'
     ? onRejected
     : function (e) {
     throw e;
     };
     if (self.status === 'pending') {
     promise = new MyPromise(function (fulfill, reject) {
     self.onFulfilledCbs.push(function () {
     try {
     const temp = onFulFilled(self.data);
     if (temp instanceof MyPromise) {
     temp.then(fulfill, reject);
     }
     fulfill(temp);
     } catch (e) {
     reject(e);
     }
     });
     self.onRejectedCbs.push(function () {
     try {
     const temp = onRejected(self.data);
     if (temp instanceof MyPromise) {
     temp.then(fulfill, reject);
     }
     } catch (e) {
     reject(e);
     }
     });
     });
     }
     if (self.status === 'fulfilled') {
     promise = new MyPromise(function (fulfill, reject) {
     try {
     const temp = onFulfilled(self.data);
     if (temp instanceof MyPromise) {
     temp.then(fulfill, reject);
     }
     fulfill(temp);
     } catch (e) {
     reject(e);
     }
     });
     }
     if (self.status === 'rejected') {
     promise = new MyPromise(function (fulfill, reject) {
     try {
     const temp = onRejected(self.data);
     if (temp instanceof MyPromise) {
     temp.then(fulfill, reject);
     }
     } catch (e) {
     reject(e);
     }
     });
     }
     return promise;
    };
    MyPromise.prototype.catch = function (onRejected) {
     return this.then(null, onRejected);
    };
    new MyPromise((resolve, reject) => {
     setTimeout(() => resolve(123), 3000);
    })
     .then(value => {
     console.log(value);
     })
     .then(() => {
     throw new Error('hahaha');
     })
     .catch(e => console.log(e));
    

    实现 Promise.all、Promise.race、Promise.retry 和 Promise.allSettled

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    
    Promise.myAll = ps => {
     if (typeof ps[Symbol.iterator] !== 'function') {
     return Promise.reject('arguments must be an iterator object');
     }
     let resolvedList = Array(ps.length);
     let resolvedNum = 0;
     return new Promise((resolve, reject) => {
     ps.forEach((p, i) => {
     Promise.resolve(p)
     .then(v => {
     resolvedList[i] = v;
     resolvedNum = resolvedNum + 1;
     if (resolvedList.length === resolvedNum) {
     resolve(resolvedList);
     }
     })
     .catch(reject);
     });
     });
    };
    Promise.myRace = ps => {
     if (typeof ps[Symbol.iterator] !== 'function') {
     return Promise.reject('arguments must be an iterator object');
     }
     return new Promise((resolve, reject) => {
     ps.forEach(p => {
     Promise.resolve(p).then(resolve).catch(reject);
     });
     });
    };
    Promise.myRetry = (p, time = 3) => {
     return new Promise(async (resolve, reject) => {
     while (time--) {
     try {
     const v = await p();
     resolve(v);
     break;
     } catch (e) {
     if (!time) {
     reject(e);
     }
     }
     }
     });
    };
    Promise.myAllSettled = ps => {
     if (typeof ps[Symbol.iterator] !== 'function') {
     return Promise.reject('arguments must be an iterator object');
     }
     let tempList = Array(ps.length);
     let tempNum = 0;
     return new Promise((resolve, reject) => {
     ps.forEach((p, i) => {
     Promise.resolve(p)
     .then(v => {
     tempList[i] = { status: 'fulfilled', value: v };
     })
     .catch(e => {
     tempList[i] = { status: 'rejected', reason: e };
     })
     .finally(() => {
     tempNum = tempNum + 1;
     if (tempList.length === tempNum) {
     resolve(tempList);
     }
     });
     });
     });
    };
    

    实现继承

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    // 类
    class Parent {
     constructor(name, sex) {
     this.name = name;
     this.sex = sex;
     }
     eat() {
     console.log('吃');
     }
     say() {
     console.log(`我叫:${this.name}`);
     }
    }
    class Child extends Parent {
     constructor(name, sex) {
     super(name, sex);
     }
     eat() {
     super.eat();
     }
     say() {
     console.log('咿咿呀呀');
     }
    }
    /**
     * 构造函数:
     * 1. 错误方法一
     * Son.prototype = Papa.prototype;
     * 仅仅是创建了一个指向 Papa.prototype 的引用
     * 修改 Son.prototype 也会改变 Papa.prototype(两个引用会相互影响)
     * 2. 错误方法二
     * Son.prototype = new Papa();
     * 使用到了构造函数调用,假如 Papa 本身有一些副作用
     * 会影响到 Son 的实例
     * 3. ES5 的写法,最后要让 Son.prototype.constructor 指向 Son
     * Son.prototype = Object.create(Papa.prototype);
     * Son.prototype.constructor = Son;
     * 4. ES6 的写法,不用修改 constructor 的指向了
     * Object.setPrototypeOf(Son.prototype, Papa.prototype);
     */
    function Papa(name, sex) {
     this.name = name;
     this.sex = sex;
    }
    Papa.prototype.eat = function () {
     console.log('吃');
    };
    Papa.prototype.say = function () {
     console.log(`我叫:${this.name}`);
    };
    function Son(name, sex) {
     Papa.call(this, name, sex);
    }
    Object.setPrototypeOf(Son.prototype, Papa.prototype);
    Son.prototype.say = function () {
     console.log('咿咿呀呀');
    };
    

    实现柯里化

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    function currying(fn, ...rest) {
     if (rest.length >= fn.length) {
     return fn(...rest);
     } else {
     return (...args) => currying(fn, ...rest, ...args);
     }
    }
    function fn(x1, x2, x3, x4, x5) {
     console.log(x1 + x2 + x3 + x4 + x5);
    }
    const test = currying(fn, 1);
    console.log(test(2, 3, 4, 5)); // 15
    console.log(test(2, 3)(4, 5)); // 15
    console.log(test(2)(3, 4)(5)); // 15
    console.log(test(2)(3)(4)(5)); // 15
    

    实现深拷贝

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    
    function deepClone(o, map = new WeakMap()) {
     const type = o =>
     Object.prototype.toString
     .call(o)
     .replace(/\[object |\]/g, '')
     .toLowerCase();
     if (
     type(o) === 'null' ||
     type(o) === 'undefined' ||
     type(o) === 'string' ||
     type(o) === 'number' ||
     type(o) === 'boolean' ||
     type(o) === 'symbol' ||
     type(o) === 'function'
     ) {
     return o;
     }
     // if (type(o) === 'function') {
     // // https://developer.chrome.com/docs/apps/contentSecurityPolicy/#relaxing-inline-script
     // return eval(o.toString());
     // }
     if (type(o) === 'date') {
     return new Date(o);
     }
     if (type(o) === 'regexp') {
     return new RegExp(o);
     }
     if (map.has(o)) {
     return map.get(o);
     }
     let temp = new o.constructor();
     map.set(o, temp);
     // Map、Set、WeakMap、WeakSet
     if (type(o) === 'map' || type(o) === 'weakmap') {
     o.forEach((v, k) => {
     temp.set(k, deepClone(v));
     });
     return temp;
     }
     if (type(o) === 'set' || type(o) === 'weakset') {
     o.forEach(v => {
     temp.add(deepClone(v));
     });
     return temp;
     }
     // Object、Array
     for (let i in o) {
     if (o.hasOwnProperty(i)) {
     temp[i] = deepClone(o[i], map);
     }
     }
     return temp;
    }
    let temp = {
     0: null,
     1: undefined,
     a: 'a',
     b: 123,
     c: false,
     d: [1, 2, 3, 4, 5],
     e: Symbol(''),
     f: () => {},
     h: new Date(),
     i: new Set([0, 0, 0]),
     j: new Map([
     [0, 1],
     [1, 0]
     ])
    };
    deepClone(temp);
    

    实现一个无限累加的函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    function add(m) {
     function temp(n = 0) {
     m = m + n;
     return temp;
     }
     // 比 toString、valueOf 优先级高
     temp[Symbol.toPrimitive] = function () {
     return m;
     };
     return temp;
    }
    console.log(add(1)); // 1
    console.log(add(1)(2)); // 3
    console.log(add(1)(2)(3)); // 6
    console.log(add(1)(2)(3)(4)); // 10
    console.log(add(1)(2)(3)(4)(5)); // 15
    

    实现两个非常大的数字(超出了 Number 范围)的加法

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    function bigNumberSum(a, b) {
     a = `${a}`;
     b = `${b}`;
     const padding = a.length - b.length;
     if (padding > 0) {
     b = `${'0'.repeat(padding)}${b}`;
     } else {
     a = `${'0'.repeat(-padding)}${a}`;
     }
     const temp = [];
     let overTen = 0;
     for (let i = a.length - 1; i >= 0; i--) {
     let res = overTen + (a[i] - 0) + (b[i] - 0);
     if (res > 9) {
     overTen = 1;
     } else {
     overTen = 0;
     }
     temp[i] = res % 10;
     }
     if (overTen > 0) {
     temp.unshift(1);
     }
     return temp.join('');
    }
    bigNumberSum(12345435356, 94564354325); // 106909789681
    

    复杂类型转换

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    const example = {
     // 只要发生类型转换,都会按照这个优先级,和转换为什么类型无关
     toString() {
     // 优先级最低
     return '123';
     },
     valueOf() {
     // 优先级次之
     return 123;
     },
     [Symbol.toPrimitive]() {
     // 优先级最高
     return 'toPrimitive';
     }
    };
    console.log(example + ''); // toPrimitive
    console.log(example - 0); // NaN
    const hahasha = {
     variable: 0,
     valueOf() {
     return ++this.variable;
     }
    };
    if (hahasha == 1 && hahasha == 2 && hahasha == 3) {
     // 即使 hahasha 是用 const 声明的
     console.log('哈哈啥');
    }
    

    数组去重

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    function deduOne(arr) {
     return [...new Set(arr)];
    }
    function deduTwo(arr) {
     let temp = [];
     for (let item of arr) {
     if (!temp.includes(item)) {
     temp.push(item);
     }
     }
     return temp;
    }
    function deduThree(arr) {
     // 从数组的起始位置开始,元素第一次出现的位置应该等于当前的索引值
     return arr.filter((item, index, arr) => arr.indexOf(item, 0) === index);
    }
    const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4, 3, 7, 2, 8, 3, 2, 6, 1];
    console.log(deduOne(arr));
    console.log(deduTwo(arr));
    console.log(deduThree(arr));
    

    写一个函数,输入 n,求斐波那契数列的第 n 项

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    // 斐波那契的本质是 [a, b] = [b, a + b]
    function fibonacciOne(n) {
     if (n % 1 !== 0) throw new Error('>.<');
     if (n <= 0) return 0;
     if (n === 1) return 1;
     // 这个会导致调用栈爆炸,如果 n 很大的话
     return fibonacciOne(n - 1) + fibonacciOne(n - 2);
    }
    function fibonacciTwo(n) {
     const temp = [0, 1];
     if (n % 1 !== 0) throw new Error('>.<');
     if (n <= 0) return temp[0];
     if (n === 1) return temp[1];
     for (let i = 2; i <= n; i++) {
     temp[i] = temp[i - 1] + temp[i - 2];
     }
     return temp[n];
    }
    let n = 10;
    console.log(fibonacciOne(n));
    console.log(fibonacciTwo(n));
    

    扁平化去重

    1
    2
    3
    4
    5
    6
    
    // 将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
    var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
    console.log([...new Set(arr.flat(Infinity))].sort((a, b) => a - b));
    console.log(
     [...new Set(arr.toString().split(','))].sort((a, b) => a - b).map(Number)
    );
    

    利用函数柯理化动态创建函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    // 每次绑定事件都要判断一次
    function addEvent(type, el, fn, capture = false) {
     if (window.addEventListener) {
     el.addEventListener(type, fn, capture);
     } else if (window.attachEvent) {
     el.attachEvent('on' + type, fn);
     }
    }
    // 仅在初次调用时判断
    const addEvent = (function () {
     if (window.addEventListener) {
     return function (type, el, fn, capture) {
     el.addEventListener(type, fn, capture);
     };
     } else if (window.attachEvent) {
     return function (type, el, fn) {
     el.attachEvent('on' + type, fn);
     };
     }
    })();
    // 惰性函数来实现
    function addEvent(type, el, fn, capture = false) {
     // 重写函数
     if (window.addEventListener) {
     addEvent = function (type, el, fn, capture) {
     el.addEventListener(type, fn, capture);
     };
     } else if (window.attachEvent) {
     addEvent = function (type, el, fn) {
     el.attachEvent('on' + type, fn);
     };
     }
     // 执行函数
     addEvent(type, el, fn, capture);
    }
    

    节流与防抖

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    function throttle(fn, time = 300) {
     let previous = 0;
     const context = this;
     return function (...args) {
     let now = Date.now();
     if (now - previous > time) {
     previous = now;
     fn.apply(context, args);
     }
     };
    }
    window.addEventListener(
     'scroll',
     throttle(function (e) {
     console.log(e);
     }, 1000)
    );
    function debounce(fn, wait = 300, isImmediate) {
     let timer = null;
     const context = this;
     return function (...args) {
     if (timer) {
     clearTimeout(timer);
     }
     if (isImmediate && !timer) {
     fn.apply(context, args);
     }
     timer = setTimeout(() => {
     fn.apply(context, args);
     }, wait);
     };
    }
    window.addEventListener(
     'scroll',
     debounce(
     function (e) {
     console.log(e);
     },
     1000,
     true
     )
    );
    

    合并数组

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    // 把 arrOne 和 arrTwo 合并为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']
    const arrOne = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'];
    const arrTwo = ['A', 'B', 'C', 'D'];
    // 第一种
    arrTwo.forEach((item, index) => {
     // forEach 可以直接修改数组,但对于字面量的修改需要借助 index
     arrTwo[index] = item + '3';
    });
    // sort 对字符串排序不需要参数
    const temp = arrOne.concat(arrTwo).sort();
    const answer = temp.map(item => {
     if (item.includes('3')) {
     return item.split('')[0];
     }
     return item;
    });
    console.log(answer);
    // 第二种
    console.log(
     [...arrOne, ...arrTwo].sort(
     (a, b) =>
     a.codePointAt(0) - b.codePointAt(0) ||
     b.length - a.length ||
     a.codePointAt(1) - b.codePointAt(1)
     )
    );
    

    计算数组交集

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    function jiaoJi(a1, a2) {
     const temp = [];
     const map = {};
     for (let item of a1) {
     // 记录第一个数组中各项出现的次数
     if (map[item]) {
     map[item]++;
     } else {
     map[item] = 1;
     }
     }
     for (let item of a2) {
     if (map[item] > 0) {
     temp.push(item);
     map[item]--;
     }
     }
     return temp;
    }
    const randomArr = length =>
     Array.from({ length }, () => Math.floor(Math.random() * (length + 1)));
    const a1 = randomArr(50);
    const a2 = randomArr(100);
    console.log(jiaoJi(a1, a2));
    

    大小写取相反

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    function upLow(str) {
     let strArr = str.split('');
     strArr.forEach((item, index) => {
     const temp = item.toUpperCase();
     item === temp
     ? (strArr[index] = item.toLowerCase())
     : (strArr[index] = temp);
     });
     return strArr.join('');
    }
    console.log(upLow('tYUFtfhjfFugyuiygU'));
    

    字符串匹配

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    // 从长度为 n 的字符串 S 中,查找是否存在字符串 T,T 的长度是 m,若存在返回所在位置
    function fitStr(s, t) {
     const n = s.length;
     const m = t.length;
     if (n < m) {
     return -1;
     }
     for (let i = 0; i <= n - m; i++) {
     if (s.substr(i, m) === t) {
     return i;
     }
     }
     return -1;
    }
    fitStr('qwer', 'qwert'); // -1
    fitStr('qwert', 'qwer'); // 0
    fitStr('asdfg', 'dfg'); // 2
    

    旋转数组

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function spinOne(arr, k) {
     const halfOne = arr.slice(arr.length - k);
     const halfTwo = arr.slice(0, arr.length - k);
     return halfOne.concat(halfTwo);
    }
    function spinTwo(arr, k) {
     let temp = arr.concat();
     for (let i = 0; i < k; i++) {
     let val = temp.pop();
     temp.unshift(val);
     }
     return temp;
    }
    const arr = [1, 2, 3, 4, 5, 6, 7, 9, 10];
    const k = 3;
    console.log(spinOne(arr, k));
    console.log(spinTwo(arr, k));
    

    打印出 1-1000 之间所有的对称数

    1
    2
    3
    4
    5
    
    [...Array(1000).keys()].filter(
     item =>
     `${item}`.length > 1 &&
     item === Number(`${item}`.split('').reverse().join(''))
    );
    

    找区间

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    function findRange(arr) {
     let temp = [];
     for (let i = 0; i <= arr.length - 1; i++) {
     const range = { from: arr[i], to: arr[i] };
     while (arr[i + 1] === arr[i] + 1) {
     range.to = arr[i + 1];
     i++;
     }
     temp.push(range);
     }
     temp = temp.map(item =>
     item.from === item.to ? item.from : `${item.from}~${item.to}`
     );
     return temp.join(' ');
    }
    findRange([1, 2, 3, 5, 7, 8, 10]); // 1~3 5 7~8 10
    

    给定一个整数数组和一个目标值,找出数组中和为目标值的两个数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    function findSum(arr, target) {
     for (let i = 0; i < arr.length; i++) {
     for (let j = i + 1; j < arr.length; j++) {
     if (arr[i] + arr[j] === target) {
     return [i, j];
     }
     }
     }
     return -1;
    }
    console.log(findSum([2, 7, 11, 15], 9));
    

    不使用加减乘除计算整数的 7 倍

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    function sevenOne(num) {
     return '1'.repeat(num).repeat(7).length;
    }
    function seventTwo(num) {
     return Array(7)
     .fill(Array.from({ length: num }))
     .flat().length;
    }
    console.log(sevenOne(3));
    console.log(seventTwo(3));
    

    字符串排序

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    function sortStr(str) {
     let temp = str.split('');
     let rule = { 黄: 1, 红: 2, 蓝: 3 };
     temp.sort((prev, next) => {
     return rule[prev] - rule[next];
     });
     temp = temp.join('');
     return temp;
    }
    console.log(sortStr('红蓝蓝黄红黄蓝红红黄红')); // 黄黄黄红红红红红蓝蓝蓝
    

    将扁平的数据结构转化为树

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    const exampleArr = [
     { id: 3, name: '部门 3', pid: 1 },
     { id: 4, name: '部门 4', pid: 3 },
     { id: 5, name: '部门 5', pid: 4 },
     { id: 1, name: '部门 1', pid: 0 },
     { id: 2, name: '部门 2', pid: 1 }
    ];
    {
     function arr2Tree1(arr, pid = 0) {
     let temp = [];
     getChildren(arr, temp, pid);
     return temp;
     }
     function getChildren(arr, temp, pid) {
     for (let item of arr) {
     if (item.pid === pid) {
     const newItem = { ...item, children: [] };
     temp.push(newItem);
     getChildren(arr, newItem.children, item.id);
     }
     }
     }
    }
    {
     function arr2Tree2(arr) {
     const temp = [];
     const map = {};
     for (let item of arr) {
     map[item.id] = { ...item, children: [] };
     }
     for (let item of arr) {
     const { id, pid } = item;
     const mapItem = map[id];
     if (pid === 0) {
     temp.push(mapItem);
     } else {
     if (!map[pid]) {
     map[pid] = { children: [] };
     } else {
     map[pid].children.push(mapItem);
     }
     }
     }
     return temp;
     }
    }
    console.log(arr2Tree1(exampleArr));
    console.log(arr2Tree2(exampleArr));
    

    检验链表是否成环

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    function hasCycle(linkedList) {
     // 分别取两个指针(快、慢),如果成环,那么快、慢指针一定会相遇
     if (!linkedList.head) return false;
     let fast = linkedList.head;
     let slow = linkedList.head;
     while (fast.next && fast.next.next) {
     slow = slow.next;
     fast = fast.next.next;
     if (fast === slow) {
     break;
     }
     }
     if (!fast) {
     return false;
     }
     slow = linkedList.head;
     while (slow !== fast) {
     // 重新相遇的点就是入环节点
     slow = slow.next;
     fast = fast.next;
     }
     return slow;
    }
    

    参考

    • 每日进阶
    • 壹题


沪ICP备19023445号-2号
友情链接