Promise

// 模仿实现Promise// Promise利用三大伎俩解决回调天堂:// 1. 回调函数提早绑定// 2. 返回值穿透// 3. 谬误冒泡// 定义三种状态const PENDING = 'PENDING';      // 进行中const FULFILLED = 'FULFILLED';  // 已胜利const REJECTED = 'REJECTED';    // 已失败class Promise {  constructor(exector) {    // 初始化状态    this.status = PENDING;    // 将胜利、失败后果放在this上,便于then、catch拜访    this.value = undefined;    this.reason = undefined;    // 胜利态回调函数队列    this.onFulfilledCallbacks = [];    // 失败态回调函数队列    this.onRejectedCallbacks = [];    const resolve = value => {      // 只有进行中状态能力更改状态      if (this.status === PENDING) {        this.status = FULFILLED;        this.value = value;        // 胜利态函数顺次执行        this.onFulfilledCallbacks.forEach(fn => fn(this.value));      }    }    const reject = reason => {      // 只有进行中状态能力更改状态      if (this.status === PENDING) {        this.status = REJECTED;        this.reason = reason;        // 失败态函数顺次执行        this.onRejectedCallbacks.forEach(fn => fn(this.reason))      }    }    try {      // 立刻执行executor      // 把外部的resolve和reject传入executor,用户可调用resolve和reject      exector(resolve, reject);    } catch(e) {      // executor执行出错,将谬误内容reject抛出去      reject(e);    }  }  then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;    onRejected = typeof onRejected === 'function'? onRejected :      reason => { throw new Error(reason instanceof Error ? reason.message : reason) }    // 保留this    const self = this;    return new Promise((resolve, reject) => {      if (self.status === PENDING) {        self.onFulfilledCallbacks.push(() => {          // try捕捉谬误          try {            // 模仿微工作            setTimeout(() => {              const result = onFulfilled(self.value);              // 分两种状况:              // 1. 回调函数返回值是Promise,执行then操作              // 2. 如果不是Promise,调用新Promise的resolve函数              result instanceof Promise ? result.then(resolve, reject) : resolve(result);            })          } catch(e) {            reject(e);          }        });        self.onRejectedCallbacks.push(() => {          // 以下同理          try {            setTimeout(() => {              const result = onRejected(self.reason);              // 不同点:此时是reject              result instanceof Promise ? result.then(resolve, reject) : resolve(result);            })          } catch(e) {            reject(e);          }        })      } else if (self.status === FULFILLED) {        try {          setTimeout(() => {            const result = onFulfilled(self.value);            result instanceof Promise ? result.then(resolve, reject) : resolve(result);          });        } catch(e) {          reject(e);        }      } else if (self.status === REJECTED) {        try {          setTimeout(() => {            const result = onRejected(self.reason);            result instanceof Promise ? result.then(resolve, reject) : resolve(result);          })        } catch(e) {          reject(e);        }      }    });  }  catch(onRejected) {    return this.then(null, onRejected);  }  static resolve(value) {    if (value instanceof Promise) {      // 如果是Promise实例,间接返回      return value;    } else {      // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED      return new Promise((resolve, reject) => resolve(value));    }  }  static reject(reason) {    return new Promise((resolve, reject) => {      reject(reason);    })  }  static all(promiseArr) {    const len = promiseArr.length;    const values = new Array(len);    // 记录曾经胜利执行的promise个数    let count = 0;    return new Promise((resolve, reject) => {      for (let i = 0; i < len; i++) {        // Promise.resolve()解决,确保每一个都是promise实例        Promise.resolve(promiseArr[i]).then(          val => {            values[i] = val;            count++;            // 如果全副执行完,返回promise的状态就能够扭转了            if (count === len) resolve(values);          },          err => reject(err),        );      }    })  }  static race(promiseArr) {    return new Promise((resolve, reject) => {      promiseArr.forEach(p => {        Promise.resolve(p).then(          val => resolve(val),          err => reject(err),        )      })    })  }}

查找字符串中呈现最多的字符和个数

例: abbcccddddd -> 字符最多的是d,呈现了5次

let str = "abcabcabcbbccccc";let num = 0;let char = ''; // 使其依照肯定的秩序排列str = str.split('').sort().join('');// "aaabbbbbcccccccc"// 定义正则表达式let re = /(\w)\1+/g;str.replace(re,($0,$1) => {    if(num < $0.length){        num = $0.length;        char = $1;            }});console.log(`字符最多的是${char},呈现了${num}次`);

实现数组的push办法

let arr = [];Array.prototype.push = function() {    for( let i = 0 ; i < arguments.length ; i++){        this[this.length] = arguments[i] ;    }    return this.length;}

手写 Promise.race

该办法的参数是 Promise 实例数组, 而后其 then 注册的回调办法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能扭转一次, 那么咱们只须要把 Promise.race 中产生的 Promise 对象的 resolve 办法, 注入到数组中的每一个 Promise 实例中的回调函数中即可.

Promise.race = function (args) {  return new Promise((resolve, reject) => {    for (let i = 0, len = args.length; i < len; i++) {      args[i].then(resolve, reject)    }  })}

模仿Object.create

Object.create()办法创立一个新对象,应用现有的对象来提供新创建的对象的__proto__。

// 模仿 Object.createfunction create(proto) {  function F() {}  F.prototype = proto;  return new F();}

实现数组的filter办法

Array.prototype._filter = function(fn) {    if (typeof fn !== "function") {        throw Error('参数必须是一个函数');    }    const res = [];    for (let i = 0, len = this.length; i < len; i++) {        fn(this[i]) && res.push(this[i]);    }    return res;}

手写 instanceof 办法

instanceof 运算符用于判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。

实现步骤:

  1. 首先获取类型的原型
  2. 而后取得对象的原型
  3. 而后始终循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null

具体实现:

function myInstanceof(left, right) {  let proto = Object.getPrototypeOf(left), // 获取对象的原型      prototype = right.prototype; // 获取构造函数的 prototype 对象  // 判断构造函数的 prototype 对象是否在对象的原型链上  while (true) {    if (!proto) return false;    if (proto === prototype) return true;    proto = Object.getPrototypeOf(proto);  }}

验证是否是身份证

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]()  }}

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

实现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;};

将数字每千分位用逗号隔开

数字有小数版本:

let format = n => {    let num = n.toString() // 转成字符串    let decimals = ''        // 判断是否有小数    num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimals    let len = num.length    if (len <= 3) {        return num    } else {        let temp = ''        let remainder = len % 3        decimals ? temp = '.' + decimals : temp        if (remainder > 0) { // 不是3的整数倍            return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp        } else { // 是3的整数倍            return num.slice(0, len).match(/\d{3}/g).join(',') + temp         }    }}format(12323.33)  // '12,323.33'

数字无小数版本:

let format = n => {    let num = n.toString()     let len = num.length    if (len <= 3) {        return num    } else {        let remainder = len % 3        if (remainder > 0) { // 不是3的整数倍            return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',')         } else { // 是3的整数倍            return num.slice(0, len).match(/\d{3}/g).join(',')         }    }}format(1232323)  // '1,232,323'

实现数组元素求和

  • arr=[1,2,3,4,5,6,7,8,9,10],求和
let arr=[1,2,3,4,5,6,7,8,9,10]let sum = arr.reduce( (total,i) => total += i,0);console.log(sum);
  • arr=[1,2,3,[[4,5],6],7,8,9],求和
var = arr=[1,2,3,[[4,5],6],7,8,9]let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);console.log(arr);

递归实现:

let arr = [1, 2, 3, 4, 5, 6] function add(arr) {    if (arr.length == 1) return arr[0]     return arr[0] + add(arr.slice(1)) }console.log(add(arr)) // 21

实现防抖函数(debounce)

防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则从新计时。

那么与节流函数的区别间接看这个动画实现即可。

手写简化版:

// 防抖函数const debounce = (fn, delay) => {  let timer = null;  return (...args) => {    clearTimeout(timer);    timer = setTimeout(() => {      fn.apply(this, args);    }, delay);  };};

实用场景:

  • 按钮提交场景:避免屡次提交按钮,只执行最初提交的一次
  • 服务端验证场景:表单验证须要服务端配合,只执行一段间断的输出事件的最初一次,还有搜寻联想词性能相似

生存环境请用lodash.debounce

Array.prototype.map()

Array.prototype.map = function(callback, thisArg) {  if (this == undefined) {    throw new TypeError('this is null or not defined');  }  if (typeof callback !== 'function') {    throw new TypeError(callback + ' is not a function');  }  const res = [];  // 同理  const O = Object(this);  const len = O.length >>> 0;  for (let i = 0; i < len; i++) {    if (i in O) {      // 调用回调函数并传入新数组      res[i] = callback.call(thisArg, O[i], i, this);    }  }  return res;}

查找文章中呈现频率最高的单词

function findMostWord(article) {  // 合法性判断  if (!article) return;  // 参数解决  article = article.trim().toLowerCase();  let wordList = article.match(/[a-z]+/g),    visited = [],    maxNum = 0,    maxWord = "";  article = " " + wordList.join("  ") + " ";  // 遍历判断单词呈现次数  wordList.forEach(function(item) {    if (visited.indexOf(item) < 0) {      // 退出 visited       visited.push(item);      let word = new RegExp(" " + item + " ", "g"),        num = article.match(word).length;      if (num > maxNum) {        maxNum = num;        maxWord = item;      }    }  });  return maxWord + "  " + maxNum;}

应用Promise封装AJAX申请

// promise 封装实现:function getJSON(url) {  // 创立一个 promise 对象  let promise = new Promise(function(resolve, reject) {    let xhr = new XMLHttpRequest();    // 新建一个 http 申请    xhr.open("GET", url, true);    // 设置状态的监听函数    xhr.onreadystatechange = function() {      if (this.readyState !== 4) return;      // 当申请胜利或失败时,扭转 promise 的状态      if (this.status === 200) {        resolve(this.response);      } else {        reject(new Error(this.statusText));      }    };    // 设置谬误监听函数    xhr.onerror = function() {      reject(new Error(this.statusText));    };    // 设置响应的数据类型    xhr.responseType = "json";    // 设置申请头信息    xhr.setRequestHeader("Accept", "application/json");    // 发送 http 申请    xhr.send(null);  });  return promise;}

实现数组的flat办法

function _flat(arr, depth) {  if(!Array.isArray(arr) || depth <= 0) {    return arr;  }  return arr.reduce((prev, cur) => {    if (Array.isArray(cur)) {      return prev.concat(_flat(cur, depth - 1))    } else {      return prev.concat(cur);    }  }, []);}

将js对象转化为树形构造

// 转换前:source = [{            id: 1,            pid: 0,            name: 'body'          }, {            id: 2,            pid: 1,            name: 'title'          }, {            id: 3,            pid: 2,            name: 'div'          }]// 转换为: tree = [{          id: 1,          pid: 0,          name: 'body',          children: [{            id: 2,            pid: 1,            name: 'title',            children: [{              id: 3,              pid: 1,              name: 'div'            }]          }        }]

代码实现:

function jsonToTree(data) {  // 初始化后果数组,并判断输出数据的格局  let result = []  if(!Array.isArray(data)) {    return result  }  // 应用map,将以后对象的id与以后对象对应存储起来  let map = {};  data.forEach(item => {    map[item.id] = item;  });  //   data.forEach(item => {    let parent = map[item.pid];    if(parent) {      (parent.children || (parent.children = [])).push(item);    } else {      result.push(item);    }  });  return result;}

用正则写一个依据name获取cookie中的值的办法

function getCookie(name) {  var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)'));  if (match) return unescape(match[2]);}
  1. 获取页面上的cookie能够应用 document.cookie

这里获取到的是相似于这样的字符串:

'username=poetry; user-id=12345; user-roles=home, me, setting'

能够看到这么几个信息:

  • 每一个cookie都是由 name=value 这样的模式存储的
  • 每一项的结尾可能是一个空串''(比方username的结尾其实就是), 也可能是一个空字符串' '(比方user-id的结尾就是)
  • 每一项用";"来辨别
  • 如果某项中有多个值的时候,是用","来连贯的(比方user-roles的值)
  • 每一项的结尾可能是有";"的(比方username的结尾),也可能是没有的(比方user-roles的结尾)
  • 所以咱们将这里的正则拆分一下:
  • '(^| )'示意的就是获取每一项的结尾,因为咱们晓得如果^不是放在[]里的话就是示意结尾匹配。所以这里(^| )的意思其实就被拆分为(^)示意的匹配username这种状况,它后面什么都没有是一个空串(你能够把(^)了解为^它前面还有一个暗藏的'');而|示意的就是或者是一个" "(为了匹配user-id结尾的这种状况)
  • +name+这没什么好说的
  • =([^;]*)这里匹配的就是=前面的值了,比方poetry;刚刚说了^要是放在[]里的话就示意"除了^前面的内容都能匹配",也就是非的意思。所以这里([^;]*)示意的是除了";"这个字符串别的都匹配(*应该都晓得什么意思吧,匹配0次或屡次)
  • 有的大佬等号前面是这样写的'=([^;]*)(;|$)',而最初为什么能够把'(;|$)'给省略呢?因为其实最初一个cookie项是没有';'的,所以它能够合并到=([^;]*)这一步。
  • 最初获取到的match其实是一个长度为4的数组。比方:
[  "username=poetry;",  "",  "poetry",  ";"]
  • 第0项:全量
  • 第1项:结尾
  • 第2项:两头的值
  • 第3项:结尾

所以咱们是要拿第2项match[2]的值。

  1. 为了避免获取到的值是%xxx这样的字符序列,须要用unescape()办法解码。

实现Array.of办法

Array.of()办法用于将一组值,转换为数组
  • 这个办法的次要目标,是补救数组构造函数Array()的有余。因为参数个数的不同,会导致Array()的行为有差别。
  • Array.of()基本上能够用来代替Array()new Array(),并且不存在因为参数不同而导致的重载。它的行为十分对立
Array.of(3, 11, 8) // [3,11,8]Array.of(3) // [3]Array.of(3).length // 1

实现

function ArrayOf(){  return [].slice.call(arguments);}