编程

防抖+节流

function debounce(fn, wait, immediate) {
    let timer = null

    return function() {
        let args = arguments
        let context = this

        if (immediate && !timer) {
            fn.apply(context, args)
        }

        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, wait)
    }
}


function throttle(fn, wait, immediate) {
    let timer = null
    let callNow = immediate

    return function() {
        let context = this,
            args = arguments

        if (callNow) {
            fn.apply(context, args)
            callNow = false
        }

        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(context, args)
                timer = null
            }, wait)
        }
    }
}


手写call、apply和bind



Function.prototype.call = function(context = window) {
    context.fn = this; // func.call(obj,p1,p2) this为func
    const args = Array.from(arguments).slice(1); // arguments[0]为content,所以去掉
    const res = context.fn(...args);
    delete context.fn; // 删除自己添加的属性
    return res; // 返回执行结果
}

// 跟call只有传参不一样
Function.prototype.apply = function(context = window) {
    context.fn = this; // func.call(obj,p1,p2) this为func
    const res = arguments.length > 0 ? context.fn(arguments[0]) : context.fn() ;
    delete context.fn; // 删除自己添加的属性
    return res; // 返回执行结果
}

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};

    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }

    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();

    return fbound;

}

柯里华

// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

有n步台阶,一次只能上1步或2步,共有多少种走法


// 普通版
function f(n) {
  if (n <= 2) return n;
  return f(n-1) + f(n-2);
}

// 优化版
const arr = [];
function f(n) {
  if (n <= 2) arr[n] = n;
  if (arr[n] > 0) return arr[n];
  arr[n] = f(n-1) + f(n-2);
  return arr[n];
}

给一个无序数组,N个数加起来等于M


        const search = (arr, count, sum) => {
          // 计算某选择情况下有几个 `1`,也就是选择元素的个数
          const n = num => {
            let count = 0
            while(num) {
              num = (num - 1) & num
              count++
            }
            console.log('count',count)
            return count
          }

          let len = arr.length, bit = 1 << len, res = []
          console.log('bit',bit)
          // 遍历所有的选择情况
          for(let i = 1; i < bit; i++){
            // 满足选择的元素个数 === count
            if(n(i) === count){
              let s = 0, temp = []

              // 每一种满足个数为 N 的选择情况下,继续判断是否满足 和为 M
              for(let j = 0; j < len; j++){
                // 建立映射,找出选择位上的元素
                if((i & 1 << j) !== 0) {
                  s += arr[j]
                  temp.push(arr[j])
                }
              }

              // 如果这种选择情况满足和为 M
              if(s === sum) {
                res.push(temp)
              }
            }
          }

          return res
        }

        console.log(search([1,2,3,4,5],3,7))

完整深拷贝


function deepClone(o) {
    // 判断如果不是引用类型,直接返回数据即可
    if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean' || typeof o === 'undefined') {
        return o
    } else if (Array.isArray(o)) { // 如果是数组,则定义一个新数组,完成复制后返回
        // 注意,这里判断数组不能用typeof,因为typeof Array 返回的是object
        var _arr = []
        o.forEach(item => {
            if (typeof item === 'object') {
                _arr.push(deepClone(item));
            } else {
                _arr.push(item);
            }
        })
        return _arr
    } else if (typeof o === 'object') {
        var _o = {}
        for (let key in o) {
            if (o[key] instanceof Date) { // 日期
                _o[key] = new Date(o[key].valueOf())
            } else if (o[key] instanceof RegExp) { // 正则
                const pattern = o[key].valueOf();
                let flags = '';
                    flags += pattern.global ? 'g' : '';
                    flags += pattern.ignoreCase ? 'i' : '';
                    flags += pattern.multiline ? 'm' : '';
                _o[key] = new RegExp(pattern.source, flags);
            }  else if (o[key] instanceof HTMLElement) { // dom
                 _o[key] = o[key].cloneNode(true);
            } else if (o[key] instanceof Function) { // 方法
                 _o[key] = function () {
                     return o[key].apply(this, arguments);
                 }
            } else {
                _o[key] = deepClone(o[key]);
            }
        }
        return _o;
    }
}

手写new

function mynew(Func, ...args) {
    // 1.创建一个新对象
    const obj = {}
    // 2.新对象原型指向构造函数原型对象
    obj.__proto__ = Func.prototype
    // 3.将构建函数的this指向新对象
    let result = Func.apply(obj, args)
    // 4.根据返回值判断
    return result instanceof Object ? result : obj
}

观察者模式


// 观察者
class Observer {
  constructor(name, subject) {
    this.name = name;
    if (subject) {
      subject.addOberver(this);
    };
  };

  notify(msg) {
    console.log(msg);
  }


}

// 被观察者
class subject {
  constructor() {
    this.observerList = [];
  }

  addOberver(observer) {
    this.observerList.push(observer);
  }

  removeObserver(observer) {
    const index = this.observerList.findIndex(item => item.name === observer.name);
    this.observerList.splice(index, 1);
  }

  notifyObserver(msg) {
    this.observerList.forEach(item => {
      item.notify(msg);
    });
  }

}

走迷宫

只能向右和向下走,碰到1不能走,有几种走法

var arr = [
  [0, 0, 1, 1, 0, 1],
  [1, 0, 1, 1, 0, 1],
  [1, 0, 1, 1, 0, 1],
  [0, 0, 0, 0, 0, 1],
  [0, 0, 0, 1, 0, 1],
  [0, 1, 0, 0, 0, 0],
];

function f(i, j) {
  if (arr[i][j] === 1) return 0;
  if (i + j === 1) {
    if (arr[i][j] === 1) {
      return 0;
    }
    return 1;
  }
  if (i === 0) return f(0, j - 1);
  if (j === 0) return f(i - 1, 0);
  return f(i - 1, j) + f(i, j - 1);
}

顺时针打印矩阵(螺旋矩阵)

const arr = [
  [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],
];
var spiralOrder = function (matrix) {
  if (!matrix.length || !matrix[0].length) {
    return [];
  }
  const rows = matrix.length, columns = matrix[0].length;
  const visited = new Array(rows).fill(0).map(() => new Array(columns).fill(false));
  const total = rows * columns;
  const order = new Array(total).fill(0);

  let row = 0, column = 0;
  let curDir = 1;
  for (let i = 0; i < total; i++) {
    order[i] = matrix[row][column];
    visited[row][column] = true;
    if (curDir === 2) {
      if (row < rows - 1 && !visited[row + 1][column]) {
        row = row + 1;
      } else {
        curDir = 3;
        column = column - 1;
      }
    } else if (curDir === 1) {
      if (column < columns - 1 && !visited[row][column + 1]) {
        column = column + 1;
      } else {
        curDir = 2;
        row = row + 1;
      }
    } else if (curDir === 4) {
      if (row > 0 && !visited[row - 1][column]) {
        row = row - 1;
      } else {
        curDir = 1;
        column = column + 1;
      }
    } else if (curDir === 3) {
      if (column > 0 && !visited[row][column - 1]) {
        column = column - 1;
      } else {
        curDir = 4;
        row = row - 1;
      }
    }
  }
  return order;
};

大数相加

function add(str1, str2) {
  const arr1 = str1.split('');
  const arr2 = str2.split('');
  let flag = 0;
  let result = '';
  while (arr1.length || arr2.length || flag) {
    const sum = ~~arr1.pop() + ~~arr2.pop() + flag;
    result = sum % 10 + result;
    flag = ~~(sum / 10);
  }
  return result;
}

打印二维数组,实现函数expand

输出// > 0, 6, 10, 16, 20, 1, 5, 11, 15, 21, 2, 8, 12, 18, 22, 3, 7, 13, 17, 23, 4, 14, 24, 9, 19

const arr = [
    [0, 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 demo(arr) {
  let flag = 0;
  const res = [];
  const len = arr[0].length + arr[0].length % 2;
  for (let i = 0; i < len; i += 1) {
    flag = i;
    for (let j = 0; j < arr.length; j += 1) {
      if (arr[j][flag] !== undefined) {
        res.push(arr[j][flag]);
      }
      if (i % 2 === 0) {
        flag = j % 2 === 0 ? i + 1 : i;
      } else {
        flag = j % 2 === 0 ? i - 1 : i;
      }
    }
  }
  return res;
}
console.log(demo(arr));

快排

function quicksort(arr) {
  if (arr.length <= 1) return arr;
  const index = parseInt(arr.length / 2, 10);
  const leftArr = [];
  const rightArr = [];
  for (let i = 0; i < arr.length; i += 1) {
    if (i !== index) {
      if (arr[i] < arr[index]) {
        leftArr.push(arr[i]);
      } else {
        rightArr.push(arr[i]);
      }
    }
  }
  return quicksort(leftArr).concat(arr[index]).concat(quicksort(rightArr));
}

数组扁平化

1.reduce遍历数组每一项,若值为数组则递归遍历,否则concat。

function flatten(arr) {  
    return arr.reduce((result, item)=> {
        return result.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}

2.toString & split

function flatten(arr) {
    return arr.toString().split(',').map(function(item) {
        return Number(item);
    })
} 

3.join & split

function flatten(arr) {
    return arr.join(',').split(',').map(function(item) {
        return parseInt(item);
    })
}

4.递归

function flatten(arr) {
    var res = [];
    arr.map(item => {
        if(Array.isArray(item)) {
            res = res.concat(flatten(item));
        } else {
            res.push(item);
        }
    });
    return res;
}

5.扩展运算符

function flatten(arr) {
    while(arr.some(item=>Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}