数组去重办法汇总

首先:我晓得多少种去重形式

1. 双层 for 循环

function distinct(arr) {    for (let i=0, len=arr.length; i<len; i++) {        for (let j=i+1; j<len; j++) {            if (arr[i] == arr[j]) {                arr.splice(j, 1);                // splice 会扭转数组长度,所以要将数组长度 len 和下标 j 减一                len--;                j--;            }        }    }    return arr;}
思维: 双重 for 循环是比拟蠢笨的办法,它实现的原理很简略:先定义一个蕴含原始数组第一个元素的数组,而后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不反复则增加到新数组中,最初返回新数组;因为它的工夫复杂度是O(n^2),如果数组长度很大,效率会很低

2. Array.filter() 加 indexOf/includes

function distinct(a, b) {    let arr = a.concat(b);    return arr.filter((item, index)=> {        //return arr.indexOf(item) === index        return arr.includes(item)    })}
思维: 利用indexOf检测元素在数组中第一次呈现的地位是否和元素当初的地位相等,如果不等则阐明该元素是反复元素

3. ES6 中的 Set 去重

function distinct(array) {   return Array.from(new Set(array));}
思维: ES6 提供了新的数据结构 Set,Set 构造的一个个性就是成员值都是惟一的,没有反复的值。

4. reduce 实现对象数组去反复

var resources = [    { name: "张三", age: "18" },    { name: "张三", age: "19" },    { name: "张三", age: "20" },    { name: "李四", age: "19" },    { name: "王五", age: "20" },    { name: "赵六", age: "21" }]var temp = {};resources = resources.reduce((prev, curv) => { // 如果长期对象中有这个名字,什么都不做 if (temp[curv.name]) { }else {    // 如果长期对象没有就把这个名字加进去,同时把以后的这个对象退出到prev中    temp[curv.name] = true;    prev.push(curv); } return prev}, []);console.log("后果", resources);
这种办法是利用高阶函数 reduce 进行去重, 这里只须要留神initialValue得放一个空数组[],不然没法push

数组扁平化

数组扁平化是指将一个多维数组变为一个一维数组

const arr = [1, [2, [3, [4, 5]]], 6];// => [1, 2, 3, 4, 5, 6]
办法一:应用flat()
const res1 = arr.flat(Infinity);
办法二:利用正则
const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(',');

但数据类型都会变为字符串

办法三:正则改进版本
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
办法四:应用reduce
const flatten = arr => {  return arr.reduce((pre, cur) => {    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);  }, [])}const res4 = flatten(arr);
办法五:函数递归
const res5 = [];const fn = arr => {  for (let i = 0; i < arr.length; i++) {    if (Array.isArray(arr[i])) {      fn(arr[i]);    } else {      res5.push(arr[i]);    }  }}fn(arr);

应用 reduce 求和

arr = [1,2,3,4,5,6,7,8,9,10],求和

let arr = [1,2,3,4,5,6,7,8,9,10]arr.reduce((prev, cur) => { return prev + cur }, 0)

arr = [1,2,3,[[4,5],6],7,8,9],求和

let arr = [1,2,3,4,5,6,7,8,9,10]arr.flat(Infinity).reduce((prev, cur) => { return prev + cur }, 0)

arr = [{a:1, b:3}, {a:2, b:3, c:4}, {a:3}],求和

let arr = [{a:9, b:3, c:4}, {a:1, b:3}, {a:3}] arr.reduce((prev, cur) => {    return prev + cur["a"];}, 0)

打印出以后网页应用了多少种HTML元素

一行代码能够解决:

const fn = () => {  return [...new Set([...document.querySelectorAll('*')].map(el => el.tagName))].length;}

值得注意的是:DOM操作返回的是类数组,须要转换为数组之后才能够调用数组的办法。

参考:前端手写面试题具体解答

实现prototype继承

所谓的原型链继承就是让新实例的原型等于父类的实例:

//父办法function SupperFunction(flag1){    this.flag1 = flag1;}//子办法function SubFunction(flag2){    this.flag2 = flag2;}//父实例var superInstance = new SupperFunction(true);//子继承父SubFunction.prototype = superInstance;//子实例var subInstance = new SubFunction(false);//子调用本人和父的属性subInstance.flag1;   // truesubInstance.flag2;   // false

实现 jsonp

// 动静的加载js文件function addScript(src) {  const script = document.createElement('script');  script.src = src;  script.type = "text/javascript";  document.body.appendChild(script);}addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");// 设置一个全局的callback函数来接管回调后果function handleRes(res) {  console.log(res);}// 接口返回的数据格式handleRes({a: 1, b: 2});

Promise.all

Promise.all是反对链式调用的,实质上就是返回了一个Promise实例,通过resolvereject来扭转实例状态。

Promise.myAll = function(promiseArr) {  return new Promise((resolve, reject) => {    const ans = [];    let index = 0;    for (let i = 0; i < promiseArr.length; i++) {      promiseArr[i]      .then(res => {        ans[i] = res;        index++;        if (index === promiseArr.length) {          resolve(ans);        }      })      .catch(err => reject(err));    }  })}

实现instanceOf

// 模仿 instanceoffunction instance_of(L, R) {  //L 示意左表达式,R 示意右表达式  var O = R.prototype; // 取 R 的显示原型  L = L.__proto__; // 取 L 的隐式原型  while (true) {    if (L === null) return false;    if (O === L)      // 这里重点:当 O 严格等于 L 时,返回 true      return true;    L = L.__proto__;  }}

类数组转化为数组

类数组是具备length属性,但不具备数组原型上的办法。常见的类数组有arguments、DOM操作方法返回的后果。

办法一:Array.from
Array.from(document.querySelectorAll('div'))
办法二:Array.prototype.slice.call()
Array.prototype.slice.call(document.querySelectorAll('div'))
办法三:扩大运算符
[...document.querySelectorAll('div')]
办法四:利用concat
Array.prototype.concat.apply([], document.querySelectorAll('div'));

Function.prototype.bind

Function.prototype.bind = function(context, ...args) {  if (typeof this !== 'function') {    throw new Error("Type Error");  }  // 保留this的值  var self = this;  return function F() {    // 思考new的状况    if(this instanceof F) {      return new self(...args, ...arguments)    }    return self.apply(context, [...args, ...arguments])  }}

手写 Promise.all

1) 外围思路

  1. 接管一个 Promise 实例的数组或具备 Iterator 接口的对象作为参数
  2. 这个办法返回一个新的 promise 对象,
  3. 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
  4. 参数所有回调胜利才是胜利,返回值数组与参数程序统一
  5. 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。

2)实现代码

一般来说,Promise.all 用来解决多个并发申请,也是为了页面数据结构的不便,将一个页面所用到的在不同接口的数据一起申请过去,不过,如果其中一个接口失败了,多个申请也就失败了,页面可能啥也出不来,这就看以后页面的耦合水平了

function promiseAll(promises) {  return new Promise(function(resolve, reject) {    if(!Array.isArray(promises)){        throw new TypeError(`argument must be a array`)    }    var resolvedCounter = 0;    var promiseNum = promises.length;    var resolvedResult = [];    for (let i = 0; i < promiseNum; i++) {      Promise.resolve(promises[i]).then(value=>{        resolvedCounter++;        resolvedResult[i] = value;        if (resolvedCounter == promiseNum) {            return resolve(resolvedResult)          }      },error=>{        return reject(error)      })    }  })}// testlet p1 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(1)    }, 1000)})let p2 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(2)    }, 2000)})let p3 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(3)    }, 3000)})promiseAll([p3, p1, p2]).then(res => {    console.log(res) // [3, 1, 2]})

应用 setTimeout 实现 setInterval

setInterval 的作用是每隔一段指定工夫执行一个函数,然而这个执行不是真的到了工夫立刻执行,它真正的作用是每隔一段时间将事件退出事件队列中去,只有当以后的执行栈为空的时候,能力去从事件队列中取出事件执行。所以可能会呈现这样的状况,就是以后执行栈执行的工夫很长,导致事件队列里边积攒多个定时器退出的事件,当执行栈完结的时候,这些事件会顺次执行,因而就不能到距离一段时间执行的成果。

针对 setInterval 的这个毛病,咱们能够应用 setTimeout 递归调用来模仿 setInterval,这样咱们就确保了只有一个事件完结了,咱们才会触发下一个定时器事件,这样解决了 setInterval 的问题。

实现思路是应用递归函数,一直地去执行 setTimeout 从而达到 setInterval 的成果

function mySetInterval(fn, timeout) {  // 控制器,管制定时器是否继续执行  var timer = {    flag: true  };  // 设置递归函数,模仿定时器执行。  function interval() {    if (timer.flag) {      fn();      setTimeout(interval, timeout);    }  }  // 启动定时器  setTimeout(interval, timeout);  // 返回控制器  return timer;}

验证是否是身份证

function isCardNo(number) {    var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;    return regx.test(number);}

实现简略路由

// hash路由class Route{  constructor(){    // 路由存储对象    this.routes = {}    // 以后hash    this.currentHash = ''    // 绑定this,防止监听时this指向扭转    this.freshRoute = this.freshRoute.bind(this)    // 监听    window.addEventListener('load', this.freshRoute, false)    window.addEventListener('hashchange', this.freshRoute, false)  }  // 存储  storeRoute (path, cb) {    this.routes[path] = cb || function () {}  }  // 更新  freshRoute () {    this.currentHash = location.hash.slice(1) || '/'    this.routes[this.currentHash]()  }}

渲染几万条数据不卡住页面

渲染大数据时,正当应用createDocumentFragmentrequestAnimationFrame,将操作切分为一小段一小段执行。

setTimeout(() => {  // 插入十万条数据  const total = 100000;  // 一次插入的数据  const once = 20;  // 插入数据须要的次数  const loopCount = Math.ceil(total / once);  let countOfRender = 0;  const ul = document.querySelector('ul');  // 增加数据的办法  function add() {    const fragment = document.createDocumentFragment();    for(let i = 0; i < once; i++) {      const li = document.createElement('li');      li.innerText = Math.floor(Math.random() * total);      fragment.appendChild(li);    }    ul.appendChild(fragment);    countOfRender += 1;    loop();  }  function loop() {    if(countOfRender < loopCount) {      window.requestAnimationFrame(add);    }  }  loop();}, 0)

JSONP

script标签不遵循同源协定,能够用来进行跨域申请,长处就是兼容性好但仅限于GET申请

const jsonp = ({ url, params, callbackName }) => {  const generateUrl = () => {    let dataSrc = '';    for (let key in params) {      if (Object.prototype.hasOwnProperty.call(params, key)) {        dataSrc += `${key}=${params[key]}&`;      }    }    dataSrc += `callback=${callbackName}`;    return `${url}?${dataSrc}`;  }  return new Promise((resolve, reject) => {    const scriptEle = document.createElement('script');    scriptEle.src = generateUrl();    document.body.appendChild(scriptEle);    window[callbackName] = data => {      resolve(data);      document.removeChild(scriptEle);    }  })}

将VirtualDom转化为实在DOM构造

这是以后SPA利用的外围概念之一

// vnode构造:// {//   tag,//   attrs,//   children,// }//Virtual DOM => DOMfunction render(vnode, container) {  container.appendChild(_render(vnode));}function _render(vnode) {  // 如果是数字类型转化为字符串  if (typeof vnode === 'number') {    vnode = String(vnode);  }  // 字符串类型间接就是文本节点  if (typeof vnode === 'string') {    return document.createTextNode(vnode);  }  // 一般DOM  const dom = document.createElement(vnode.tag);  if (vnode.attrs) {    // 遍历属性    Object.keys(vnode.attrs).forEach(key => {      const value = vnode.attrs[key];      dom.setAttribute(key, value);    })  }  // 子数组进行递归操作  vnode.children.forEach(child => render(child, dom));  return dom;}

实现apply办法

apply原理与call很类似,不多赘述

// 模仿 applyFunction.prototype.myapply = function(context, arr) {  var context = Object(context) || window;  context.fn = this;  var result;  if (!arr) {    result = context.fn();  } else {    var args = [];    for (var i = 0, len = arr.length; i < len; i++) {      args.push("arr[" + i + "]");    }    result = eval("context.fn(" + args + ")");  }  delete context.fn;  return result;};

手写 Promise

const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";function MyPromise(fn) {  // 保留初始化状态  var self = this;  // 初始化状态  this.state = PENDING;  // 用于保留 resolve 或者 rejected 传入的值  this.value = null;  // 用于保留 resolve 的回调函数  this.resolvedCallbacks = [];  // 用于保留 reject 的回调函数  this.rejectedCallbacks = [];  // 状态转变为 resolved 办法  function resolve(value) {    // 判断传入元素是否为 Promise 值,如果是,则状态扭转必须期待前一个状态扭转后再进行扭转    if (value instanceof MyPromise) {      return value.then(resolve, reject);    }    // 保障代码的执行程序为本轮事件循环的开端    setTimeout(() => {      // 只有状态为 pending 时能力转变,      if (self.state === PENDING) {        // 批改状态        self.state = RESOLVED;        // 设置传入的值        self.value = value;        // 执行回调函数        self.resolvedCallbacks.forEach(callback => {          callback(value);        });      }    }, 0);  }  // 状态转变为 rejected 办法  function reject(value) {    // 保障代码的执行程序为本轮事件循环的开端    setTimeout(() => {      // 只有状态为 pending 时能力转变      if (self.state === PENDING) {        // 批改状态        self.state = REJECTED;        // 设置传入的值        self.value = value;        // 执行回调函数        self.rejectedCallbacks.forEach(callback => {          callback(value);        });      }    }, 0);  }  // 将两个办法传入函数执行  try {    fn(resolve, reject);  } catch (e) {    // 遇到谬误时,捕捉谬误,执行 reject 函数    reject(e);  }}MyPromise.prototype.then = function(onResolved, onRejected) {  // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数  onResolved =    typeof onResolved === "function"      ? onResolved      : function(value) {          return value;        };  onRejected =    typeof onRejected === "function"      ? onRejected      : function(error) {          throw error;        };  // 如果是期待状态,则将函数退出对应列表中  if (this.state === PENDING) {    this.resolvedCallbacks.push(onResolved);    this.rejectedCallbacks.push(onRejected);  }  // 如果状态曾经凝固,则间接执行对应状态的函数  if (this.state === RESOLVED) {    onResolved(this.value);  }  if (this.state === REJECTED) {    onRejected(this.value);  }};

实现单例模式

外围要点: 用闭包和Proxy属性拦挡
function proxy(func) {    let instance;    let handler = {        constructor(target, args) {            if(!instance) {                instance = Reflect.constructor(fun, args);            }            return instance;        }    }    return new Proxy(func, handler);}