前端常见手写题

577 阅读10分钟

1.call

let obj = {
  name: "vinomars",
};
function getName(p1, p2) {
  console.log(this.name);
  console.log(p1, p2);
}
getName("p1", "p2");

// 1.入参为当前对象和参数
// 2.在对象中添加属性方法为目标函数(隐式绑定this)
// 3.在对象中调用函数、删除该属性方法并返回值
Function.prototype.myCall = function(context = window, ...args) {
    if(typeof this !== 'function') {
       throw new TypeError('its not a function')
    }
    // 创建对象属性fn
    let fn = Symbol()
    // 隐式绑定this
    context[fn] = this;
    context[fn](...args);
    // 删除
    delete context[fn]
}
// test
getName.myCall(obj, "callp1", "callp2");

2.apply

// 主要是调用的时候参数处理不同
Function.prototype.myApply = function(context = window, arr) {
    if(typeof this !== 'function') {
       throw new TypeError('its not a function')
    }
    if(!Array.isArray) {
        throw new TypeError('arr is not an Array')
    }
    let fn = Symbol()
    context[fn] = this;
    context[fn](...arr);
    delete context[fn]
}
// test
getName.myApply(obj, ["callp1", "callp2"]);

3.bind

// 返回函数,要区分构造函数(带上构造函数参数)和普通函数
Function.prototype.myBind = function (context = window) {
  if (typeof this !== "function") {
    throw new TypeError("its not a function");
  }
  let self = this;
  let args = [...arguments].slice(1);
  return function Fn() {
    args = [...args, ...arguments];
    if (this instanceof Fn) {
      return new self(args);
    } else {
      return self.apply(context, args);
    }
  };
};
// test
const getBindName = getName.myBind(obj, "callp1", "callp2");
getBindName()

4.new

// 1.新建对象
// 2.对象继承构造函数的prototype
// 3.改变this指向
// 4.根据构造函数返回值类型来返回结果
const myNew = (fn, ...args) => {
  if (typeof fn !== "function") {
    throw new TypeError("its not a function");
  }
  let temp = Object.create(fn.prototype);
  // 一般构造函数无返回,若有要区分返回的是值还是引用类型
  let res = fn.apply(temp, args);
  return res instanceof Object ? res : temp;
};
// test
const Fn = function (name) {
  this.name = name;
};
let _fn = myNew(Fn, "HelloVino");
console.log(_fn.name); // HelloVino
function foo() {
  this.name = "ciel";
  return function () {};
}
console.log(myNew(foo)); //  fn(){}
function bar() {
  this.name = "ciel";
  return 1;
}
console.log(myNew(bar)); // bar {name: ciel}

5.防抖debounce 场景:输入框输入事件 resize等

// 一直触发事件,防抖的表现是不执行function直至不触发事件timer后,节流是隔timer内执行一次
// 1.传入fn delay immediate(立即执行)
// 2.利用闭包
const debounce = function (fn, delay, immediate) {
  if (typeof fn !== "function") {
    throw new TypeError("its not a function");
  }
  let timer = null;
  return function () {
    let self = this;
    // 取参数
    let args = [...arguments];
    // delay时间内再次执行动作会clear重新计时
    timer && clearTimeout(timer);
    if (immediate) {
      let callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, delay);
      callNow && fn.apply(self, args);
    } else {
      timer = setTimeout(() => {
        fn.apply(self, args);
      }, delay);
    }
  };
};

6.节流throttle 验证码button点击事件

const throttle = (fn, delay) => {
  if (typeof fn !== "function") {
    throw new TypeError("its not a function");
  }
  let timer = null;
  return function () {
    let self = this;
    let args = [...arguments];
    // 只有上一次的动作执行完后才会在setTimeout将timer赋为null
    if (timer) return;
    timer = setTimeout(() => {
      fn.apply(self, args);
      timer = null;
    }, delay);
  };
};

7.深拷贝deepClone/深比较deepEqual

7.1深拷贝deepClone

// 0.JSON.stringify会忽略Error,RegExp,function,Symbol,undefined
// 1.原始类型和funcion不作修改返回obj
// 2.可遍历引用类型考虑obj和arr
// 3.循环引用 new WeakMap()
// 4.性能优化 for in改为while&&兼容Symbol
// 5.各种不可遍历类型 return new Constructor(obj)
// 6.说明:引用类型拷贝的是对象在调用栈中存储的指针,所以两个对象指向同一个内存堆中的实体
// 7.证明:浅拷贝之后,改变obj1的某个属性,obj2也会跟着变
// 8.obj和arr的浅拷贝 Object.assign({},obj)和[].slice()
const isObject = (obj) => obj && typeof obj === "object";
const isFunction = (fn) => typeof fn === "function";
const deepClone = (target, map = new WeakMap()) => {
  if (!isObject(target)) {
    return target;
  }
  // function类型不做另外处理
  if (isFunction(target)) {
    return target;
  }
  // 区分数组和对象初始化
  let copy = Array.isArray(target) ? [] : {};
  // 避免循环引用
  if (map.has(target)) {
    return map.get(target);
  }
  map.set(target, copy);
  // 处理不可遍历的内置类型(还有很多未考虑,如map set等)
  let Constructor = target.constructor;
  switch (Constructor) {
    case String:
    case Number:
    case Boolean:
    case Date:
    case Error:
      return new Constructor(target);
  }
  let len = -1;
  let keys = Reflect.ownKeys(target);
  let _len = keys.length;
  // 用while效率比for高
  while (++len < _len) {
    copy[keys[len]] = deepClone(target[keys[len]], map);
  }
  return copy;
};
// test
const map = new Map();
map.set("key", "value");
map.set("ConardLi", "code秘密花园");
const set = new Set();
set.add("ConardLi");
set.add("code秘密花园");
const symbolName = Symbol();
const target = {
  field1: 1,
  field2: undefined,
  field3: {
    child: "child",
  },
  field4: [[2], 4, 8],
  empty: null,
  map,
  set,
  bool: new Boolean(true),
  num: new Number(2),
  str: new String(2),
  [symbolName]: 111,
  // symbol: Object(Symbol(1)),
  date: new Date(),
  reg: /\d+/,
  error: new Error("error!!!"),
  func1: () => {
    console.log("code秘密花园");
  },
  func2: function (a, b) {
    return a + b;
  },
};
const result = deepClone(target);
console.log(JSON.parse(JSON.stringify(target)))
console.log(result);

7.2 深比较deepEqual

let obj1 = { foo: { b: { foo: { c: { foo: null } } } } };
let obj2 = { foo: { b: { foo: { c: { foo: null } } } } };
const isObject = (obj) =>
  obj && (typeof obj === "object" || typeof obj === "function");
// 1.+0 -0
// 2.NaN
// 3.内置对象
const deepEqual = (obj1, obj2, map1 = new WeakMap(), map2 = new WeakMap()) => {
  // 普通值
  if (obj1 === obj2) {
    // 区分 +0 -0
    return obj1 !== 0 || 1 / obj1 === 2 / obj2;
  }
  // NaN
  if (obj1 !== obj1) {
    return obj2 !== obj2;
  }
  // 内置对象
  let Ctor = obj1.constructor;
  if (obj2.constructor !== Ctor) {
    return false;
  }
  switch (Ctor) {
    case String:
    case Number:
    case Boolean:
    case Date:
    case Error:
      return "" + obj1 === "" + obj2;
  }
  // 循环引用
  if (map1.has(obj1) && map2.has(obj2)) {
    return true;
  }
  map1.set(obj1, obj1);
  map2.set(obj2, obj2);
  // 引用类型
  if (isObject(obj1) && isObject(obj2)) {
    let keys1 = Reflect.ownKeys(obj1);
    let keys2 = Reflect.ownKeys(obj2);
    if (keys1.length !== keys2.length) {
      return false;
    }
    let len = keys1.length;
    while (len--) {
      let key = keys1[len];
      if (
        obj1.hasOwnProperty(key) &&
        !deepEqual(obj1[key], obj2[key], map1, map2)
      ) {
        return false;
      }
    }
    return true;
  }
  return false;
};
console.log(deepEqual(0, -0));
console.log(deepEqual(NaN, NaN));
console.log(deepEqual("Curly", new String("Curly")));
obj1.foo.b.foo.c.foo = obj1;
obj2.foo.b.foo.c.foo = obj2;
console.log(deepEqual(obj1, obj2));

8.排序

8.1 快排quickSort

// 1.找基准值 然后遍历对比 小于基准值放到left中,反之放入right,最后递归
const nums = [5, 4, 3, 0, 0, 2, 1];
const quickSort = (nums) => {
  if (!Array.isArray(nums) || nums.length < 2) {
    return nums;
  }
  let index = Math.floor(nums.length / 2);
  let numsIndex = nums.splice(index, 1)[0];
  let left = [];
  let right = [];
  let i = 0;
  while (i < nums.length) {
    if (nums[i] > numsIndex) {
      right.push(nums[i]);
    }
    if (nums[i] <= numsIndex) {
      left.push(nums[i]);
    }
    i++;
  }
  return [...quickSort(left), numsIndex, ...quickSort(right)];
};
console.log("quickSort:", quickSort(nums));

8.2 归并排序mergeSort

// 1.主要思想是不停的去比较left[0]和right[0]
const mergeSort = (arr) => {
  if (!Array.isArray(arr)) {
    return arr;
  }
  let _len = arr.length;
  if (_len < 2) {
    return arr;
  }
  let _middle = Math.floor(_len / 2);
  let left = arr.slice(0, _middle);
  let right = arr.slice(_middle, _len);
  return merge(mergeSort(left), mergeSort(right));
};
const merge = (left, right) => {
  let result = [];
  while (left.length > 0 && right.length > 0) {
    if (left[0] < right[0]) {
      result.push(left.shift());
    } else {
      result.push(right.shift());
    }
  }
  while (left.length > 0) {
    result.push(left.shift());
  }
  while (right.length > 0) {
    result.push(right.shift());
  }
  return result
};

const array = [5, 4, 3, 2, 1];
console.log("mergeSort:", mergeSort(array));

9. Promise

// promise类方法 resolve reject all race 
// promise实例方法 then catch finally

9.1 promise.all

// 1.入参为arr,返回一个promise
Promise.myAll = function (promises) {
  promises = Array.from(promises);
  const num = promises.length;
  const result = new Array(num);
  let resolvedCount = 0;

  return new Promise((resolve, reject) => {
    promises.map((promise, index) => {
      Promise.resolve(promise)
        .then((value) => {
          // 保存这个promise实例的value
          result[index] = value;
          // 通过计数器,标记是否所有实例均 fulfilled
          if (resolvedCount === num) {
            resolve(result);
          }
        })
        .catch(reject);
    });
  });
};

// test
let p1 = 1;
let p2 = Promise.reject(2);
let p3 = Promise.resolve(3);
Promise.myAll([p1, p2, p3])
  .then((results) => {
    console.log(results);
  })
  .catch((err) => {
    console.log(err);
  });
Promise.all([p1, p2, p3])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });


9.2 promise.race

const promiseRace = (arr) => {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(arr)) {
      reject("its not an arr");
    }
    arr.map((item) => {
      Promise.resolve(item).then(
        (res) => {
          resolve(res);
        },
        (err) => {
          reject(err);
        }
      );
    });
  });
};

9.3 promise.finally

// 1.实例属性放在Promise的class中
// 2.入参为finally的callback
class Promise {
  finally(callback) {
    return this.then(
      (res) => {
        // 执行callback并用promise.resolve返回结果,再执行promise的原始数据继续向下传递
        return Promise.resolve(callback()).then(() => res);
      },
      (err) => {
        return Promise.resolve(callback()).then(() => {
          throw err;
        });
      }
    );
  }
}

9.4 promise.allSettled

// 传入数组 返回promise 无论resolve还是reject都继续执行直到全部promise执行完
const formatSettledResult = (success, value) =>
  success
    ? { status: "fulfilled", value }
    : { status: "rejected", reason: value };

Promise.allSettled = function (promises) {
  return new Promise((resolve, reject) => {
    promises = Array.from(promises);
    let len = promises.length;
    let results = new Array(len);
    let settledCount = 0;
    promises.map((promise, index) => {
      Promise.resolve(promise)
        .then((res) => {
          results[index] = formatSettledResult(true, res);
          ++settledCount === len && resolve(results);
        })
        .catch((err) => {
          results[index] = formatSettledResult(false, err);
          ++settledCount === len && resolve(results);
        });
    });
  });
};
const PromiseArray = [
  Promise.resolve(100),
  Promise.reject(null),
  Promise.resolve("Data release"),
  Promise.reject(new Error("Something went wrong")),
];
Promise.allSettled(PromiseArray)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => console.log(err));

9.5 promise.retry

// 闭包保存times的值,利用setTimeout调用闭包函数
Promise.retry = (fn, times, delay) => {
  return new Promise((resolve, reject) => {
    let error;
    const attempt = () => {
      if (times === 0) {
        reject(error);
      } else {
        fn()
          .then(resolve)
          .catch((err) => {
            error = err;
            times--;
            setTimeout(() => {
              attempt();
            }, delay);
          });
      }
    };
    attempt();
  });
};
function fn() {
  let p = new Promise(function (resolve, reject) {
    setTimeout(function () {
      var num = Math.ceil(Math.random() * 20); //生成1-20的随机数
      console.log("随机数生成的值:", num);
      if (num <= 5) {
        console.log("符合条件,值为" + num);
        resolve(num);
      } else {
        reject("数字大于5,执行失败");
      }
    }, 2000);
  });
  return p;
}

Promise.retry(fn, 3, 100);

9.6 promisily

// 传入:function 返回:返回promsie
// nodeCallback方法满足两个条件:1.function最后一个参数为callback 2.callback第一个参数为error或null
const promisify = (fn) => {
  return function (...args) {
    return new Promise((resolve, reject) => {
      fn.apply(this, [
        ...args,
        (callback = (err, ...values) => {
          return err ? reject(err) : resolve(...values);
        }),
      ]);
    });
  };
};
// nodeCallback方法func1
var func1 = function (a, b, c, callback) {
  callback(null, a + b + c);
};
// promise化后的func2
var func2 = promisify(func1);
// 调用后输出6
func1(1, 2, 3, (err, result) => {
  if (!err) {
    console.log(result); //输出6
  }
});
func2(1, 2, 3).then(console.log); //输出6

10.扁平化数组

// 1.递归
const flattern = (arr) => {
  if (!Array.isArray(arr)) {
    return arr;
  }
  let res = [];
  arr.map((item) => {
    // 遍历的时候item若为数组则递归
    if (Array.isArray(item)) {
      res = [...res, ...flattern(item)];
    } else {
      res.push(item);
    }
  });
  return res;
};

// reduce
const flattern1 = (arr) => {
  return arr.reduce((prev, next) => {
    return [...prev, ...(Array.isArray(next) ? flattern1(next) : [next])];
  }, []);
};

// ...
const flattern2 = (arr) => {
  // 判断item是否为数组并遍历
  while (arr.some((item) => Array.isArray(item))) {
    // 用...来结构赋值,每次都改变原数组arr(concat本身不改变原数组)
    arr = [].concat(...arr);
  }
  return arr;
};

// test
var arr = [1, [2, [3, 4]], 56, [123, [22, [33333, [121212, [122122]]]]]];
console.log(flattern2(arr));

11.柯里化累加

const add = (...args) => {
  // 利用reduce累加
  return args.reduce((prev, next) => {
    return prev + next;
  });
};

const curry = (fn) => {
  if (typeof fn !== "function") {
    throw new TypeError("its not a function");
  }
  let args = [];
  // 闭包保存参数
  return function temp(...newArgs) {
    // 判断参数是否加完
    if (newArgs.length > 0) {
      args = [...args, ...newArgs];
      return temp;
    } else {
      return fn.apply(this, args);
    }
  };
};

let curryAdd = curry(add)
console.log(curryAdd(11,2)(3)(111)(2,33)())

12.lodash get

const obj = {
  selector: { to: { toutiao: "FE Coder" } },
  target: [1, 2, { name: "byted" }],
};
const myGet = (obj, ...args) => {
  if (Object.prototype.toString.call(obj) !== "[object Object]") {
    return obj;
  }
  try {
    // 正则 检测表达式是否含有[number]
    const reg = /\[[0-9]+\]/gi;
    let result = [];
    args.map((item) => {
      let paths = item.split(".");
      let res = obj;
      paths.map((path) => {
        if (reg.test(path)) {
          // match的结果为[[0]]
          let match = path.match(reg)[0];
          let prev = path.replace(match, ""); // target
          // 获取[number]中的数字
          let last = match.replace(/\[|\]/gi, ""); // 0
          res = res[prev][last];
        } else {
          res = res[path];
        }
      });
      result.push(res);
    });
    return result;
  } catch (err) {
    return undefined;
  }
};
// test
console.log(myGet(obj, "selector.to.toutiao", "target[0]", "target[2].name"));
// [ 'FE Coder', 1, 'byted']

13.LazyMan

class LazyManClass {
  constructor(name) {
    this.tasks = [];
    console.log(`Hi I am ${name}`);
    setTimeout(() => {
      this.next();
    }, 0);
  }
  next() {
    let fn = this.tasks.shift();
    fn && fn();
  }
  eat(name) {
    let fn = () => {
      console.log(`I am eating ${name}`);
      this.next();
    };
    this.tasks.push(fn);
    return this;
  }
  sleep(time) {
    this.wrappedSleep(false, time);
    return this;
  }
  sleepFirst(time) {
    this.wrappedSleep(true, time);
    return this;
  }
  wrappedSleep(first, time) {
    let fn = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`);
        this.next();
      }, time * 1000);
    };
    first ? this.tasks.unshift(fn) : this.tasks.push(fn);
  }
}
function LazyMan(name) {
  return new LazyManClass(name);
}

LazyMan("Tony")
  .eat("lunch")
  .eat("dinner")
  .sleepFirst(5)
  .sleep(10)
  .eat("junk food");
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

14.sleep

// ES5 遍历循环  但会阻塞线程
const sleep = (delay) => {
  for (let start = Date.now(); Date.now() - start <= delay; );
};

// promise 不美观 停止执行要层层判断跳出
const sleep = (delay) => {
  return new Promise((resolve) => {
    setTimeout(resolve, delay);
  });
};
let start = Date.now();
sleep(3000).then(() => {
  console.log(Date.now() - start);
});

// generator 太麻烦
function* sleep(delay) {
  yield new Promise((resolve) => {
    setTimeout(resolve, delay);
  });
}
let start = Date.now();
sleep(3000)
  .next()
  .value.then(() => {
    console.log(Date.now() - start);
  });

// async
const sleep = (delay) => {
  return new Promise((resolve) => {
    setTimeout(resolve, delay);
  });
};
async function test() {
  let start = Date.now();
  const temp = await sleep(3000);
  console.log(Date.now() - start);
  return temp;
}
test();

15.字符串相关

15.1 最长公共前缀

1.every

const longestCommonPrefix1 = (arr) => {
  if (!Array.isArray(arr) || arr.length < 2) {
    return "输入不存在公共前缀。";
  }
  // 将数组第一个字符串取出来,作为对比项
  let [start, ...rest] = arr;
  let result = "";
  for (let i = 0; i < start.length; i++) {
    // every当满足条件是返回true
    let flag = rest.every((item) => start[i] === item[i]);
    if (flag) result += start[i];
    else break;
  }
  return result === "" ? "输入不存在公共前缀。" : result;
};

2.深度优先遍历

const longestCommonPrefix2 = (arr) => {
  if (!Array.isArray(arr) || arr.length < 2) {
    return "输入不存在公共前缀。";
  }
  // 取第一个字符串长度
  let minLen = arr[0].length;
  // 获取数组中所有字符串长度最小值
  arr.map((item) => {
    minLen = Math.min(minLen, item.length);
  });
  let idx = 1;
  // 将第一个字符串切割,如该例中长度最小值为9('flooooow'长度为8),则'flooooower'变为'flooooow'
  let result = arr[0].substr(0, minLen);
  while (idx < arr.length) {
    while (arr[idx].indexOf(result)) {
      result = result.substr(0, result.length - 1);
      if (!result.length) return "输入不存在公共前缀。";
    }
    idx++;
  }
  return result;
};

// test
let arr1 = ["dog", "racecar", "car"];
let arr2 = ["flooooower", "flooooow", "flooooooight"];

console.log(longestCommonPrefix1(arr1));
console.log(longestCommonPrefix2(arr2));

15.2 最长公共子串

16.常见继承

function Parent(name) {
  this.name = name;
  Parent.prototype.sayName = function () {
    console.log("parent name:", this.name);
  };
}

16.1 原型链继承

// 共享属性,子类的constructor指向父类
function Child(name) {
  this.name = name;
}
Child.prototype = new Parent();
let c = new Child("原型继承");
console.log(c.constructor); // Parent
c.sayName();

16.2 借用构造函数继承

//  原型属性方法访问不到,如sayName
function Child(name) {
  Parent.call(this, name);
}
let c = new Child("借用构造 继承");
console.log(c.name);

16.3 组合继承

// 利用原型链继承原型属性方法,利用构造函数继承实例属性方法,但是调用两次父类构造函数
function Child(name) {
  Parent.call(this, name);
}
Child.prototype = new Parent();
let c = new Child("原型继承");
c.sayName();

16.4 寄生组合继承

// 利用空函数F代替调用父类构造函数
function Child(name) {
  Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
let c = new Child("原型继承");
c.sayName();

16.5 ES6继承

class Parent {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(`Hi, Im ${this.name}`);
  }
}
class Child extends Parent {
  constructor(name) {
    super();
    this.name = name;
  }
}
const parent = new Child("Mars");
const child = new Child("Vino");
console.log(parent.name);
console.log(child.name);
parent.sayName();
child.sayName();

16.6 多继承

17. EventBus

class EventEmitter {
  constructor() {
    this.events = this.events || new Map();
  }
  addListener(type, fn) {
    let handler = this.events.get(type);
    if (!handler) {
      this.events.set(type, fn);
    } else if (typeof handler === "function") {
      this.events.set(type, [handler, fn]);
    } else {
      handler.push(fn);
    }
  }
  removeListener(type, fn) {
    let handler = this.events.get(type);
    if (handler && typeof handler === "function") {
      this.events.delete(type, fn);
    } else {
      handler = handler.filter((item) => {
        console.log(item);
        return item !== fn;
      });
    }
  }
  emit(type, ...args) {
    let handler = this.events.get(type);
    if (Array.isArray(handler)) {
      handler.map((item) => {
        item.apply(this, args);
      });
    } else {
      handler.apply(this, args);
    }
  }
}

// 实例化
const emitter = new EventEmitter();

// 监听同一个事件名
emitter.addListener("vino", (man) => {
  console.log(`one ${man}`);
});
emitter.addListener("vino", (man) => {
  console.log(`two ${man}`);
});
emitter.addListener("vino", (man) => {
  console.log(`three ${man}`);
});
emitter.addListener("mars", (man) => {
  console.log(`four ${man}`);
});
emitter.addListener("mars", (man) => {
  console.log(`five ${man}`);
});

// 触发事件
emitter.emit("vino", "hello");
emitter.emit("mars", "world");

18.instanceof

// 查找原型链的过程
const myInstanceof = (left, right) => {
  // 所有对象都有原型对象,除了Object.prototype._proto_ = null
  if (typeof left !== "object" || left === null) {
    return false;
  }
  // getPrototypeOf是Object的方法,拿到原型对象
  let proto = Object.getPrototypeOf(left);
  while (true) {
    if (proto == null) {
      return false;
    }
    // 实例left的_proto_属性和构造函数right的属性prototype都指向该构造函数的原型对象
    if (proto === right.prototype) {
      return true;
    }

    proto = Object.getPrototypeOf(proto);
  }
};
console.log(myInstanceof([], Array));
console.log(myInstanceof([], Function));
console.log([] instanceof Function);
console.log([] instanceof Array);

19.版本号比较

// 1.split 转为数字
// v1<v2 -1 v1>v2 1 v1=v2 0
const compareVersion = (v1, v2) => {
  let arr1 = v1.split(".");
  let arr2 = v2.split(".");
  let maxLength = Math.max(arr1.length, arr2.length);
  let i = 0;
  while (i < maxLength) {
    arr1[i] = +arr1[i] || 0;
    arr2[i] = +arr2[i] || 0;
    if (arr1[i] < arr2[i]) {
      return -1;
    } else if (arr1[i] > arr2[i]) {
      return 1;
    }
    i++;
  }
  return 0;
};
console.log(compareVersion("0.1", "1.1")); // -1
console.log(compareVersion("1.0.1", "1")); // 1
console.log(compareVersion("7.5.2.4", "7.5.3")); // -1
console.log(compareVersion("1.01", "1.001")); // 0
console.log(compareVersion("1.0", "1.0.0")); // 0
console.log(compareVersion("01", "1")); // 0
// 2.补0 补齐位数 但1.01和1.001与01和1这种未添加判断
const toNum = (v, len) => {
  let MAP = ["00000", "0000", "000", "00", "0", ""];
  while (v.length < len) {
    v.push("0");
  }
  v.map((item, index) => {
    v[index] = MAP[index] + item;
  });
  return v.join("");
};
const compareVersion = (v1, v2) => {
  v1 = v1.split(".");
  v2 = v2.split(".");
  let maxLength = Math.max(v1.length, v2.length);
  v1 = toNum(v1, maxLength);
  v2 = toNum(v2, maxLength);
  if (v1 < v2) {
    return -1;
  } else if (v1 > v2) {
    return 1;
  } else {
    return 0;
  }
};
console.log(compareVersion("0.1", "1.1")); // -1
console.log(compareVersion("1.0.1", "1")); // 1
console.log(compareVersion("7.5.2.4", "7.5.3")); // -1
console.log(compareVersion("1.0", "1.0.0")); // 0

20.缓存入参函数

// 利用闭包存储函数执行结果
function memorize(fn) {
  let cache = {};
  return function (...args) {
    console.log(cache);
    let _args = JSON.stringify(args);
    return cache[_args] || (cache[_args] = fn.apply(this, args));
  };
}
const add = function (a) {
  return a + 1;
};
const minus = function (a) {
  return a - 10;
};
const adder = memorize(add);
const minuser = memorize(minus);

console.log(adder(1)); // 2
console.log(adder(1)); // 2 cache: { '[1]': 2 }
console.log(minuser(1)); // -9
console.log(minuser(1)); // -9 cache: { '[1]': -9 }
console.log(adder(2));

21.ES6

21.1 const

// let用自执行函数包裹,模拟块级作用域
var __const = function __const(data, value) {
  window.data = value // 把要定义的data挂载到window下,并赋值value
  Object.defineProperty(window, data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
    enumerable: false,
    configurable: false,
    get: function () {
      return value
    },
    set: function (data) {
      if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
        throw new TypeError('xxx')
      } else {
        return value
      }
    }
  })
}
__const('a', 10)
console.log(a)

22.爬楼梯

// 递推
function climbStairs(n) {
  if (n == 1 || n == 2) return n;
  return climbStairs(n - 1) + climbStairs(n - 2);
}
console.log(climbStairs(3)); // 3

23.二分查找

let arr = [1, 2, 3, 5, 6, 7, 8, 21, 31, 222];
// 迭代
const binaryFind1 = (arr, target) => {
  let low = 0;
  let high = arr.length - 1;
  let mid;
  while (low < high) {
    mid = Math.floor((low + high) / 2);
    let cur = arr[mid];
    if (cur === target) {
      return mid;
    } else if (cur < arr[mid]) {
      low = low + 1;
    } else {
      high = high - 1;
    }
  }
};

// 递归
const binaryFind2 = (arr, target, low = 0, high = arr.length - 1) => {
  let mid = Math.floor((low + high) / 2);
  let cur = arr[mid];
  if (cur === target) {
    return mid;
  } else if (cur < target) {
    return binaryFind2(arr, target, mid, high);
  } else {
    return binaryFind2(arr, target, low, mid);
  }
};
console.log(binaryFind1(arr, 3));
console.log(binaryFind2(arr, 21));

24.hooks源码

24.1 useState

let index = 0;
let memorizedStates = [];
const useState = (init) => {
  // 初始值
  init = memorizedStates[index] || init;
  let cur = indexl;
  function setState(newState) {
    // 直接替换新的state,class中的setState是合并新旧state
    memorizedStates[cur] = newState;
    // 更新视图
    render();
  }
  return [memorizedStates[index++], setState];
};
const render = () => {
  // index清零
  index = 0;
  ReactDOM.render();
};

25.Array

25.1 打乱有序数组 洗牌算法

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
Array.prototype.shuffle = function () {
  let arr = this;
  for (let i = arr.length - 1; i >= 0; i--) {
    // 取index随机值
    let _random = Math.floor(Math.random() * (i + 1));
    // 将i和index对应的值对换
    [arr[i], arr[_random]] = [arr[_random], arr[i]];
  }
  return arr;
};
console.log(arr.shuffle());

25.2 深度广度优先遍历

const data = [
  {
    name: "a",
    children: [
      { name: "b", children: [{ name: "e" }] },
      { name: "c", children: [{ name: "f" }] },
      { name: "d", children: [{ name: "g" }] },
    ],
  },
  {
    name: "a2",
    children: [
      { name: "b2", children: [{ name: "e2" }] },
      { name: "c2", children: [{ name: "f2" }] },
      { name: "d2", children: [{ name: "g2" }] },
    ],
  },
];
// 深度优先 递归
const deepMap = (data, result = []) => {
  data.map((item) => {
    result.push(item.name);
    item.children && deepMap(item.children, result);
  });
  return result.join(",");
};

console.log(deepMap(data));

// 广度遍历, 创建一个执行队列, 当队列为空的时候则结束
const breadthMap = (tree) => {
  let node,
    list = [...tree];
  let result = [];
  while ((node = list.shift())) {
    result.push(node.name);
    node.children && list.push(...node.children);
  }
  return result.join(",");
}

console.log(breadthMap(data));


·习题
var tree = {
  name: "中国",
  children: [
    {
      name: "北京",
      children: [
        {
          name: "朝阳群众",
          children: [
            {
              name: "西宁市",
              code: "0521",
            },
          ],
        },
        {
          name: "海淀区",
        },
        {
          name: "昌平区",
        },
      ],
    },
    {
      name: "浙江省",
      children: [
        {
          name: "杭州市",
          code: "0571",
        },
        {
          name: "嘉兴市",
        },
        {
          name: "绍兴市",
        },
        {
          name: "宁波市",
        },
      ],
    },
  ],
};
// 输出: { name: '西宁市', code: '0521' }


const bfs = (data, result) => {
  const tmp = data.children;
  let res;
  let node,
    list = [...tmp];
  while ((node = list.shift())) {
    if (node.name === result) {
      res = node;
      break;
    } else {
      node.children && list.push(...node.children);
    }
  }
  return res;
};
console.log(bfs(tree, "西宁市")); // 输出: { name: '西宁市', code: '0521' }

const deepMap = (data, result) => {
  if (data.name === result) {
    return data;
  }
  if (data.children) {
    for (let i = 0; i < data.children.length; i++) {
      const res = deepMap(data.children[i], result);
      if (res) {
        return res;
      }
    }
  }
  return null;
};
console.log(deepMap(tree, "西宁市")); // 输出: { name: '西宁市', code: '0521' }