【靠它上岸】面试题-手撕...

103 阅读14分钟

二分查找

function search(arr, target){
  let left = 0;
  let right = arr.length - 1;
  while(left <= right){
    // right-left算出两者之间的距离再除以2取中间值,再加上left
    let mid = Math.floor((right-left)/2) + left;
    if(arr[mid] > target){
      right = mid - 1;
    }else if(arr[mid] < target){
      left = mid + 1;
    }else{
      return mid;
    }
  }
  return -1; 
}
let res = search([2,3,4,6,11,23], 11);
console.log(res);

排序

搞懂基本排序算法 - 掘金

排序算法稳定性

排序稳定性是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的顺序不变

常见的排序算法之众,插入排序,合并排序,冒泡排序等都是稳定的,堆排序,快速排序等是不稳定的

冒泡排序

比较所有相邻元素,如果第一个比第二个大,则交换,一轮下来,可以保证最后一个数是最大的

function bubble(arr, target){
  let len = arr.length;
  for(let i = 0; i < len; i++){
    // 因为后面的数已经排好了,所以不用再进行比较了
    for(let j = 0; j < len - i; j++){
      if(arr[j] > arr[j + 1]){
        [arr[j], arr[j+1]] = [arr[j+1], arr[j]];
      }
    }
  }
  return arr;
}
console.log(bubble([3, 11, 2, 11, 5, 23]));

快速排序O(logN)

选择一个数为准基,遍历arr,比它大的放在left,比它小的放在right,递归

function quick(arr){
  if(arr.length <= 1) return arr;
  let left = [],
      right = [];
  let pivotIndex = Math.floor(arr.length / 2);
  let pivot = arr.splice(pivotIndex,1)[0];
  for(let i = 0; i < arr.length; i++){
    if(arr[i] > pivot){
      right.push(arr[i])
    }else{
      left.push(arr[i])
    }
  }
  return quick(left).concat([pivot],quick(right))
}
console.log(quick([3, 11, 2, 11, 5, 23]));

插入排序

从第二个数往前比,比他大就往后排,以此类推,进行到最后一个数

function insert(arr){
  for(let i = 0; i < arr.length; i++){
    const temp = arr[i];
    let j = i;
    while(j > 0){
      if(arr[j-1] > temp){
        arr[j] = arr[j-1];
      }else{
        break;
      }
      j -= 1;
    }
    arr[j] = temp;
  }
  return arr;
}
console.log(insert([3, 11, 2, 11, 5, 23]));

选择排序

找到数组中最小值,选中并将其放在第一位;然后找第二小的值放在第二位;以此类推,执行n-1轮

function check(arr){
  for(let i = 0;i < arr.length;i++){
    let indexMin = i;
    for(let j = i; j < arr.length; j++){
      if(arr[j] < arr[indexMin]){
        indexMin = j;
      }
    }
    if(indexMin !== i){
      [arr[i], arr[indexMin]] = [arr[indexMin], arr[i]]
    }
  }
  return arr;
}

归并排序

function merge(left, right){
  let temp = [];
  while(left.length && right.length){
    if(left[0] < right[0]){
      temp.push(left.shift())
    } else {
      temp.push(right.shift());
    }
  }
  return temp.concat(left, right);
}
function mergeSort(arr){
  const len = arr.length;
  if(len < 2) return arr;
  let mid = Math.floor(len / 2);
  let left = arr.slice(0, mid);
  let right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right))
}

堆排序

JavaScript 数据结构与算法之美 - 归并排序、快速排序、希尔排序、堆排序 - 掘金

const heapSort = arr => {
    // 初始化大顶堆,从第一个非叶子结点开始
    for(let i = Math.floor(arr.length / 2 - 1); i >= 0; i--){
        heapify(arr,i,arr.length);
    }
    // 排序,每一次 for 循环找出一个当前最大值,数组长度减一
    for(let i = Math.floor(arr.length - 1); i > 0;i--){
        // 排序,每一次 for 循环找出一个当前最大值,数组长度减一
        swap(arr,0,i);
        // 从根节点开始调整,并且最后一个结点已经为当前最大值,不需要再参与比较,所以第三个参数为 i,即比较到最后一个结点前一个即可
        heapify(arr,0,i);
    }
    return arr;
}
// 交换两个节点
const swap = (arr,i ,j)=>{
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
// 将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:
// 假设结点 i 以下的子堆已经是一个大顶堆,heapify 函数实现的
// 功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。
// 后面将写一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点
// 都执行 heapify 操作,所以就满足了结点 i 以下的子堆已经是一大顶堆
const heapify = (arr,i,length)=>{
    let temp = arr[i];// 当前父节点
    // j < length 的目的是对结点 i 以下的结点全部做顺序调整
    for(let j = 2 * i + 1; j < length; j = 2 * j + 1){
        temp = arr[i];// 将 array[i] 取出,整个过程相当于找到 array[i] 应处于的位置
        if(j + 1 < length && array[j] < array[j + 1]){
            j++;// 找到两个孩子中较大的一个,再与父节点比较
        }
        if(temp < arr[j]){
            swap(arr,i,j); // 如果父节点小于子节点:交换;否则跳出
            i = j;// 交换后,temp 的下标变为 j
        }else{
            break;
        }
    }
}

无注释版

const heapSort = arr => {
    for(let i = Math.floor(arr.length / 2 - 1); i >= 0; i--){
        heapify(arr,i,arr.length);
    }
    for(let i = Math.floor(arr.length - 1); i > 0;i--){
        swap(arr,0,i);
        heapify(arr,0,i);
    }
    return arr;
}
const swap = (arr,i ,j)=>{
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
const heapify = (arr,i,length)=>{
    let temp = arr[i];
    for(let j = 2 * i + 1; j < length; j = 2 * j + 1){
        temp = arr[i];
        if(j + 1 < length && array[j] < array[j + 1]){
            j++;
        }
        if(temp < arr[j]){
            swap(arr,i,j); 
            i = j;
        }else{
            break;
        }
    }
}

二叉树

深度指的是节点到顶点的距离,求深度用前序遍历

高度指的是节点到底的距离,求高度用后序遍历

先序遍历

function front(arr){
  const res = [];
  const rec = n => {
    if(!n) return;
    res.push(n.val);
    rec(n.left);
    rec(n.right);
  }
  rec(root);
  return res;
}

中序遍历

function front(arr){
  const res = [];
  const rec = n => {
    if(!n) return;
    rec(n.left);
    res.push(n.val);
    rec(n.right);
  }
  rec(root);
  return res;
}

后序遍历

function back(arr){
  const res = [];
  const ret = n => {
    if(!n) return;
    rec(n.left);
    rec(n.right);
    res.push(n.val);
  }
  rec(root);
  return res;
}

层序遍历

var levelOrder = function (root) {
  let res = [];
  let quene = [];
  quene.push(root);
  if(root === null) return res;
  while(quene.length !== 0){
    let len = quene.length;
    let curLevel = [];
    for(let i = 0; i < len ;i++){
      let node = quene.shift();
      curLevel.push(node.val);
      node.left && quene.push(node.left);
      node.right && quene.push(node.right);
    }
    res.push(curLevel);
  }
  return res;
}

ajax

let xhr = new XMLHttpRequest();
xhr.open('method', 'url','async');
xhr.send();
xhr.setRequestHeader('Content-Type','application/json');
xhr.onreadyStatechange = function(){
  if(xhr.readyState === 4){
    if(xhr.status >= 200 && xhr.status < 300){
      // some code
    }
  }
}

promise封装ajax

const getJson = function (url){
  return new Promise(fucntion (resolve, reject){
    const handler = function (){
      if(this.readyState !== 4){
        return ;
      }
      if(this.status === 200){
        resolve(this.response);
      }else{
        reject(new Error(this.statusText));
      }
    }
    const client = new XHRHttpRequest();
    client.open('GET', url);
    client.onreadystatechange = handler;
    client.responseType = 'json';
    client.setRequestHeader('Accept','application/json');
    client.send();
  })
}
getJson("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

防抖

触发事件后在n秒内函数只能触发一次,如果在n秒内又触发了时间,则会重新计算函数执行时间。单位时间内,操作n次,保留最后一次。

function debounce(fn, delay) {
	let timeout = null;
  return function () {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay)
  }
}

节流

连续触发事件但是在n秒内只执行一次函数,节流会稀释函数的执行频率。单位时间内,操作n次,执行第一次

function throttle(fn, delay) {
  let canrun = true;
  return function () {
    if(!canrun) return;
    canrun = false;
    setTimeout(() => {
      fn.apply(this, arguments);
      canrun = true;
    }, delay)
  }
}

new

  1. 在内存中创建一个新对象
  2. 这个新对象内部的[[prototype]]特性被赋值为构造函数的prototype属性
  3. 构造函数内部的this被赋值为这个新对象
  4. 执行构造函数的代码
  5. 如果构造函数返回空对象,则返回该对象,否则,返回刚创建的新对象
Function create(con, ...args){
  var obj = {};
  obj.__proto__ = con.prototype;
  var result = con.apply(obj, args);
  return result instanceof object ? result : obj;
}
var a = create(构造函数名称,需要传入的构造函数参数)

蹦床函数

内存的清道夫——函数的尾调用 - 掘金

可以将递归执行转为循环执行。

接受一个函数f作为参数,只要f执行后返回一个函数,就继续执行。注意,这里是返回一个函数,然后执行该函数,而不是函数里面调用函数,这样就避免了递归执行,从而消除调用栈过大的问题

function trampoline(f){
  while(f && f instanceof Function){
    f= f();
  }
  return f;
}

函数柯里化

柯里化函数应用 | Heying Ye’s Personal Website

function createCurry(fn){
  if(typeof fn !== 'function'){
    throw TyepError("fn is not function");
  }
  // 复用第一个参数
  var args = [].slice.call(arguments, 1);
  // 返回新函数
  return funciton (){
    // 收集剩余参数
    var _args = [].slice.call(arguments);
    // 返回结果
    return fn.apply(this, args.concat(_args));
  }
}

//add(19)(10, 20, 30),求该函数传递的参数和
var add = createCurry(function() {
  //获取所有参数
  var args = [].slice.call(arguments);
  //返回累加结果
  return args.reduce(function(accumulator, currentValue) {
    return accumulator + currentValue
  })
}, 19)
add(10, 20, 30);    //79

隐式包装类

function createSymbolObject(description){
  return function (){
    return this
  }.call(Symbol(description))
}
function createBigIntObject(description){
  return function (){
    return this
  }.call(BigInt(description))
}

Promise

JavaScript 深入系列之 Promise 核心原理的模拟实现,通过 Promises/A+ 官方872个测试用例 · Issue #125 · yuanyuanbyte/Blog

看了就会,手写Promise原理,最通俗易懂的版本!!! - 掘金

从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 - 掘金

class Promise {
  constructor(exeutor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    let resolve = value => {
      if(this.state === 'pending'){
        this.state = 'fulfilled';
        this.value = value;
      }
    }
    let reject = reason => {
      if(this.state === 'pending'){
        this.state = 'rejected';
        this.reason = reason;
      }
		}
  	try{
   		executor(resolve,reject);
    }catch(err){
      reject(err);
    }
  }
  then(onFulfiled,onRejected){
    if(this.state === 'fulfilled'){
   	  onFulfilled(this.value);
    }
    if(this.state === 'rejected'){
      onRejected(this.reason);
    }
  	return this;
  }
}
//promise.all
Promise.all = function(iterators){
  let promises = Array.from(iterators);
  let len = promises.length;
  let res = new Array(len);
  let resolveNum = 0;
  return new Promise((resolve,reject)=>{
    promises.forEach((promise,index)=>{
      Promise.resolve(promise)
      .then(value=>{
      	res[index] = value;
     	  if(++resolveNum === len){
      		resolve(res);
      	}
      })
      .catch(reject);
    })
  })
}
// promise.race
Promise.race = function(iterators){
  const promises = Array.from(iterators);
  return new promise((resolve, reject) => {
    promises.forEach(promise => {
      Promise.resolve(promise).then(resolve).catch(reject);
    })
  })
}
// promise.any 
Promise.any = function(iterators){
  const promises = Array.from(iterators);
  let len = promises.length;
  let rejectedList = new Array(len);
  let rejectedNum = 0;
  return new Promise((resolve,reject)=>{
    promises.forEach((promise,index)=>{
      Promise.resolve(promise)
      .then(resolve)
      .catch(reason=>{
        rejectedList[index] = reason;
        if(++rejectedNum === len){
        	reject(rejectedList);
        }
      })
    })
  })
}
// promise.allSettled
const result = (success,value)=>{
  success
  ? {status:'fulfilled',value}
  : {status:'rejected',reason:value}
}
Promise.allSettled = function (iterators){
  const promises = Array.from(iterators);
  const len = promises.length;
  const settledList = new Array(len);
  let settledNum = 0;
  return new promise((resolve,reject)=>{
    promises.forEach((promise,index)=>{
      Promise.resolve(promise)
      .then(value=>{
        settledList[index] = result(true,value);
        if(++settledNum === len){
        	resolve(settledList);
     		}
    	})
      .catch(reason=>{
        settledList[index] = reason;
        if(++settledNum === len){
        	reject(settledList);
        }
      })
  	})
  })
}
class myPromise {
  // 构造方法
  constructor(executor){
    // 初始化值
    this.PromiseResult = null // 终值
    this.PromiseState = 'pending' // 状态
    this.onFulfilledCallbacks = [] // 保存成功回调
    this.onRejectedCallbacks = [] // 保存失败回调
    // 初始化this
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
    // 执行传进来的函数
    try{
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  
  resolve(value){
    if (this.PromiseState !== 'pending') return
    // 如果执行resolve,状态变为fulfilled
    this.PromiseState = 'fulfilled';
    // 终值为传进来的值
    this.PromiseResult = value;
    while (this.onFulfilledCallbacks.length) {
      this.onFulfilledCallbacks.shift()(this.PromiseResult)
		}
  }
  
  reject(reason){
    if (this.PromiseState !== 'pending') return
    // 如果执行reject,状态变为rejected
    this.PromiseState = 'rejected';
    // 终值为传进来的reason
    this.PromiseResult = reason;
    while (this.onRejectedCallbacks.length) {
      this.onRejectedCallbacks.shift()(this.PromiseResult)
		}
  }
  
  then(onFulfilled, onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
    let thenPromise = new myPromise((resolve, reject) => {
      const resolvePromise = cb => {
        setTimeout(()=>{
          try {
            const x = cb(this.PromiseResult);
            if(x === thenPromise){
              throw new Error('不能返回自身。。。')
            }
            if (x instanceof myPromise) {
                x.then(resolve, reject)
              } else {
                // 非Promise就直接成功
                resolve(x)
              }
            } catch (err) {
              // 处理报错
              reject(err)
              throw new Error(err)
            }
          }
        })
        if(this.PromiseState === 'fulfilled'){
          resolvePromise(onFulfilled);
        }else if(this.PromiseState === 'rejected'){
          resolvePromise(onRejected);
        }else if(this.PromiseState === 'pending'){
          // 如果状态为待定状态,暂时保存两个回调
          this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));
          this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));
        }
      }
    })
    
  }
  
  static all(promises) {
    const result = [];
    let count = 0;
    return new myPromise((resolve, reject) => {
      const addData = (index, value) => {
        result[index] = value;
        count++;
        if(count === promises.length) resolve(result);
      }
      promises.forEach((promise, index) => {
        if(promise instanceof myPromise){
          promise.then(res => {
            addData(index, res);
          },err => reject(err))
        }else{
          addData(index, promise);
        }
      })
    })
  }

  static race(promises){
    return new myPromise((resolve, reject) => {
      promises.forEach(promise => {
        if(promise instanceof myPromise){
          promise.then(res => {
            resolve(res);
          },err => {
            reject(err);
          })
        }else{
          resolve(promise)
        }
      })
    })
  }
  
  static allSettled(promises){
    return new myPromise((resolve, reject) => {
      const res = [];
      let count = 0;
      const addData = (status, value, i) => {
        res[i] = {
          status,
          value,
        }
        count++;
        if(count === promises.length){
          resolve(res);
        }
      }
      promises.forEach((promise, i) => {
        if(promise instanceof myPromise){
          promise.then(res => {
            addData('fulfilled', res, i);
          },err => {
            addData('rejected', err, i);
          })
        } else {
          addData('fulfilled', promise, i);
        }
      })
    })
  }

  static any(promises){
    return new Promise((resolve, reject) => {
      let count = 0;
      promise.forEach(promise => {
        promise.then(val => {
          resolve(val);
        },err => {
          count++;
          if(count === promises.length){
            reject(new AggregateError('All promises were rejected'))
          }
        })
      })
    })
  }
}
class myPromise {
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';
  constructor(func){
    this.PromiseState = myPromise.PENDING;
    this.PromiseResult = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCakkbacks = [];
    try {
      func(this.resolve.bind(this), this.reject.bind(this));
    } catch {
      this.reject(error);
    }
   
  }
  resolve(result){
    if(this.PromiseState === myPromise.PENDING){
      this.PromiseState = myPromise.FULFILLED;
      this.PromiseResult = result;
      onFulfilledCallbacks.forEach( callback => {
        callback(result);
      })
    }
  }
  reject(reason){
    if(this.PromiseState === myPromise.PENDING){
      this.PromiseState = myPromise.REJECTED;
      this.PromiseReason = reason;
      onRejectedCallbacks.forEach( callback => {
        callback(reason);
      })
    }
  }
  then(onFulfilled, onRejected){
    let promise2 = new myPromise((resolve, reject) => {
      if(this.PromiseState === myPromise.FULFILLED){
        setTimeout(() => {
          try {
            if(typeof onFulfilled !== 'function'){
              resolve(this.PromiseResult);
            } else {
              let x = onFulfilled(this.PromiseResult);
              resolvePromise(promise2, x, resolve, reject);
            }
          } catch (e){
            reject(e);
          }
        })
      } else if (this.PromiseState === myPromise.REJECTED){
        setTimeout(() => {
          try {
            if(typeof onRejected !== 'function'){
              reject(this.PromiseResult);
            } else {
              let x = onRejected(this.PromiseResult);
              resolvePromise(promise2, x, resolve, reject);
            }
          } catch (e){
            reject(e);
          }
        })
      } else if (this.PromiseState === myPromise.PENDING){
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              if(typeof onFulfilled !== 'function'){
                resolve(this.PromiseResult);
              } else {
                let x = onFulfilled(this.PromiseResult);
                resolvePromise(promise2, x, resolve, reject);
              }
            } catch (e){
              reject(e);
            }
          })
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              if(typeof onRejected !== 'function'){
                reject(this.PromiseResult);
              } else {
                let x = onRejected(this.PromiseResult);
                resolvePromise(promise2, x, resolve, reject);
              }
            } catch (e){
              reject(e);
            }
        	})
        })
      }
    })
    return promise2
  }
  catch(onRejected){
    return this.then(undefined, onRejected);
  }
  finally(callBack){
    return this.then(callBack, callBack);
  }
  static resolve(value){
    // 如果是一个promise则返回这个promise
    if(value instanceof myPromise){
      return value;
    }else if(value instanceof Object && 'then' in value){
     // 如果是一个具有then方法的对象则返回的promise会跟随这个thenable的对象
     // 采用它的最终状态 
      return new myPromise((resolve, reject) => {
        value.then(resolve, reject);
      })
    }
    // 否则返回的promise值将以此值完成,即以辞职执行resolve()方法,状态为fulfilled;
    return new myPromise(resolve => {
      resolve(value);
    })
  }
  static reject(reason){
    return new myPromise((resolve, reject) => {
      reject(reason);
    })
  }
  static all(promises){
    return new myPromise((resolve, reject) => {
      if(Array.isArray(promises)){
        let result = [];
        let count = 0;
        if(!promises.length){
          return resolve(promises);
        }
        promises.forEach((promise, index) => {
          myPromise.resolve(item).then(
            value => {
              count++;
              result[index] = value;
              count === promises.length && resolve(result);
            },
            reason => {
              reject(reason);
            }
          )
        })
      } else {
        return reject(new TypeError('Argument is not iterable'))
      }
    })
  }
  static allSettled(promises){
    return new myPromise((resolve, reject) => {
      if(Array.isArray(promises)){
        let result = [];
        let count = 0;
        if(!promises.length) return resolve(promises);
        promises.forEach((item, index) => {
          myPromise.resolve(item).then(
            value => {
              count++;
              result[index] = {
                status: 'fulfilled',
                value
              };
              count === promises.length && resolve(result);
            },
            reason => {
              count++;
              result[index] = {
                status: 'rejected',
                reason
              };
              count === promises.length && resolve(result);
            }
          )
        })
      } else {
        return reject(new TypeError('Argument is not iterable'));
      }
    })
  }
  static any(promises){
    if(Array.isArray(promises)){
      let result = [];
      let count = 0;
      if(!promises.length) return reject(new AggregateError('All promises were rejected'))
      promises.forEach(promise => {
        myPromise.resolve(promise).then(
          value => {
            resolve(value);
          },
          reason => {
            count++;
            errors.push(reason);
            count === promises.length && reject(new AggregrateError(errors));
          }
        )
      })
    } else {
      return reject(new TypeError('Argument is not iterable'))
    }
  }
  static race(promises){
    return new myPromise((resolve, reject) => {
      if(Array.isArray(promises)){
        if(promises.length > 0){
          promises.forEach(promise => {
            myPromise.resolve(promise).then(resolve, reject);
          })
        }
      } else {
        return reject(new TypeError('Argument is not iterable'))
      }
    })
  }
}
function resolvePromise(promise2, x, resolve, reject){
  if(x === promise2){
    throw new TypeError('Chaining cycle detected for promise');
  }
  if(x instanceof myPromise){
    x.then(y => {
      resolvePromise(promise2, y, resolve, reject);
    }, reject);
  } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))){
    try {
      var then = x.then;
    } catch (e) {
      return reject(e);
    }
    if(typeof then === 'function'){
      let called = false;
      try {
        then.call(
          x,
          y => {
            if(called) return;
            called = true;
            reject(r);
          }
        )
      } catch(e){
        if(called) return;
        called = true;
        reject(e);
      }
    }else{
      resolve(x)
    }
  } else {
    return resolve(x);
  }

}

树转数组

function treeToArr(nodes, pid = null){
  const result = [];
  for (let node of nodes) {
    const curNode = {
      id: node.id,
      name: node.name,
      pid
    };
    result.push(curNode)
    if (node.children && node.children.length > 0) {
      const childResult = treeToArr(node.children, node.id);
      reult.push(...childrenResult);
    }
  }
  return result;
}
const arr = [
  {
    id: 0,
    name: '0',
    children: [
      {
        id: 1,
        name: '1',
        children: [
          {
            id: 11,
            name: '11'
          }
        ]
      }
    ]
  }
];

数组转树

function arrToTree(arr){
  return arr.reduce((prev, cur) => {
    cur.children = arr.filter(v => v.parentId === cur.id);
    if(!cur.parentId){
      prev.push(cur);
    }
    return prev;
  }, []);
}
const arr = [
  {
    id: 2,
    name: '部门B',
    parentId: 0
  },
  {
    id: 3,
    name: '部门C',
    parentId: 1
  },
  {
    id: 1,
    name: '部门A',
    parentId: 2
  },
]

数组拍平

  1. 使用flat()拍平数组:该方法可传入一个数字作为拍平的层数,默认为1,设置为infinity后可实现全部拍平
  2. 使用正则表达式
// 转为字符串后拼接为数组样式最后将其转换为数组对象
Json.parse(‘[’+ json.stringify(arr).replace(/[|]/g ,‘ ’) + ‘]’)
  1. 使用reduce
let flat = arr => {
  arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flat(cur) : cur)
  })
}
  1. 使用递归
const res = [];
const fns = arr => {
  for(let i = 0; i < arr.length; i++){
    if(Array.isArray(arr[i])){
      fns(arr[i])
    }else{
      res.push(arr[i])
    }
  }
}

数组去重

  1. 原生js
Array.prototype.unique = function (arr){
  let temp = {};
  let res = [];
  for(let i = 0;i < arr.length; i++){
    if(!temp[this[i]]){
      temp[this[i]] = 'abc';
      res.push(this[i])
    }
  }
  return arr;
}
  1. indexOf——返回某个指定的字符串值在字符串中首次出现的位置
function unique(arr){
  if(!Array.isArray(arr)){
    console.log('error');
    return;
  }
  let res = [];
  for(let i = 0;i < arr.length; i++){
    if(res.indexOf(arr[i]) !== -1){
      return;
    }else{
      res.push(arr[i])
    }
  }
  return res;
}
  1. sort()先将数组进行排序,然后判断当前元素与前一个元素是否相同,相同说明重复,不相同就添加进res
function unique(array){
    array = array.sort();
    let res = [array[0]]
    for(let i = 1;i<len;i++){
        if(array[i] !== array[i-1]){
            res.push(array[i]);
        }
    }
    return res;
}
  1. 利用splice
function unique(arr){
  let len = arr.length;
  for(let i = 0; i < len; i++){
    for(let j = i + 1; j < len; j++){
      if(arr[i] === arr[j]){
        arr.splice(j, l);
        j--;
      }
    }
    return arr;
  }
}
  1. filter
function unique(arr){
  return arr.filter(function (item, index, arr){
    return arr.indexOf(item, 0) === index;
  })
}
  1. 利用set
function unique(arr){
  return Array.from(new Set(arr))
}

  1. map
function unique(arr){
  const seen = new Map();
  return arr.filter(a => !seen.has(a) && seen.set(a, 1))
}

图片懒加载

// 等所有的资源文件加载完毕之后再绑定事件
window.onload = function (){
  // 获取图片列表,即img标签列表
  let imgs = ducument.querySelectorAll('img');
  function lazyLoad(imgs){
    // 可视区域高度
    let innerHeight = window.innerHeight;
    // 滚动区域高度
    let scrollTop = ducument.documentElement.scrollTop || document.body.scrollTop;
    for(let i = 0;i < imgs.length; i++){
      // 图片距离顶部的距离大于可视区域和滚动区域之和时懒加载
      if((innerHeight + scrollTop) > imgs[i].offsetTop){
        // 不加立即执行函数i会等于9
        (function(i){
          // 真实情况是页面开始有2秒空白,所以使用setTimeout定时2s
          setTimeout(function (){
            //创建一个临时图片,这个图片在内存中不会到页面上去。实现隐形加载
            let temp = new Image();
            //只会请求一次
            temp.src = imgs[i].getAttribute('data-src');
            temp.onload = function (){
              // 获取自定义属性data-src,用真图片替换假图片
              imgs[i].src = imgs[i].getAttribute('data-src');
            }
          },2000)
        })(i)
      }
    }
  }
  lazyLoad(imgs);
  window.onscroll = function(){
    lazyLoad(imgs)
  }
}

深浅克隆

深克隆

function deepClone(target, cache = new Map()){
  if(cache.get(target)){
    return cache.get(target);
  }
  if(target instanceof Object){
    let dist;
    if(target instanceof Array){
      dist = [];
    }else if(target instanceof Function){
      dist = function (){
        return target.call(this, ...arguments);
      }
    }else if(target instanceof RegExp){
      dist = new RegExp(target.source, target.flags);
    }else if(target instanceof Date){
      dist = new Date(target); 
    }else{
      dist = {};
    }
    cache.set(target, dist);
    for(let key in target){
      if (target.hasOwnProperty(key)) {
        dist[key] = deepClone(target[key], cache);
      }
    }
    return dist;
  }else{
    return target;
  }
}

浅克隆

function clone(origin, target = null){
  for(let prop in origin){
    target[prop] = origin[prop];
  }
  retun target;
}

实现useHover hook

import React, { useState, useEffect } from 'react';
import ReactDom from 'react-dom';

const Tpp = () => {
  const isHovered = useHover();
  function useHover() {
    const [isHovered, setIsHovered] = useState(false);

    const handleMouseEnter = () => {
      setIsHovered(true);
    };

    const handleMouseLeave = () => {
      setIsHovered(false);
    };

    useEffect(() => {
      // 获取元素的引用
      const element = document.querySelector('.hover-element');

      // 添加事件监听器
      element.addEventListener('mouseenter', handleMouseEnter);
      element.addEventListener('mouseleave', handleMouseLeave);

      // 在组件卸载时清除事件监听器
      return () => {
        element.removeEventListener('mouseenter', handleMouseEnter);
        element.removeEventListener('mouseleave', handleMouseLeave);
      };
    }, []);

    return isHovered;
  }
  return (
    <div>
    <span className="hover-element">{ isHovered ? '鼠标悬停:是' : '鼠标悬停:否' } </span>
    </div>
  );
}

ReactDom.render(<Tpp />, document.getElementById('app'));

大数相加

function addBigNumbers(num1, num2) {
  let len1 = num1.length;
  let len2 = num2.length;
  // 将两个大数转换为数组,并反转数组顺序方便从低位到高位相加
  let arr1 = num1.split('').reverse();
  let arr2 = num2.split('').reverse();
  // 计算结果的数组
  let result = [];
  // 取两个大数中较长的长度作为循环次数
  let maxLen = Math.max(len1, len2);
  // 进位
  let carry = 0;
  for (let i = 0; i < maxLen; i++) {
    let digit1 = i < len1 ? parseInt(arr1[i]) : 0;
    let digit2 = i < len2 ? parseInt(arr2[i]) : 0;
    let sum = digit1 + digit2 + carry;
    // 计算当前位的数值和进位
    let digit = sum % 10;
    carry = Math.floor(sum / 10);
    // 将当前位的数值插入结果数组
    result.push(digit);
  }
  // 处理最高位的进位
  if (carry > 0) {
    result.push(carry);
  }
  // 反转结果数组,并转换为字符串
  result = result.reverse().join('');
  return result;
}
// 示例用法
let num1 = '123456789012345678901234567890';
let num2 = '987654321098765432109876543210';
let sum = addBigNumbers(num1, num2);
console.log(sum);