实现类的继承

实现类的继承-简版

类的继承在几年前是重点内容,有n种继承形式各有优劣,es6遍及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深刻了解的去看红宝书即可,咱们目前只实现一种最现实的继承形式。
// 寄生组合继承function Parent(name) {  this.name = name}Parent.prototype.say = function() {  console.log(this.name + ` say`);}Parent.prototype.play = function() {  console.log(this.name + ` play`);}function Child(name, parent) {  // 将父类的构造函数绑定在子类上  Parent.call(this, parent)  this.name = name}/**  1. 这一步不必Child.prototype = Parent.prototype的起因是怕共享内存,批改父类原型对象就会影响子类 2. 不必Child.prototype = new Parent()的起因是会调用2次父类的构造方法(另一次是call),会存在一份多余的父类实例属性3. Object.create是创立了父类原型的正本,与父类原型齐全隔离*/Child.prototype = Object.create(Parent.prototype);Child.prototype.say = function() {  console.log(this.name + ` say`);}// 留神记得把子类的结构指向子类自身Child.prototype.constructor = Child;
// 测试var parent = new Parent('parent');parent.say() var child = new Child('child');child.say() child.play(); // 继承父类的办法

ES5实现继承-具体

第一种形式是借助call实现继承

function Parent1(){    this.name = 'parent1';}function Child1(){    Parent1.call(this);    this.type = 'child1'    }console.log(new Child1);
这样写的时候子类尽管可能拿到父类的属性值,然而问题是父类中一旦存在办法那么子类无奈继承。那么引出上面的办法

第二种形式借助原型链实现继承:

function Parent2() {    this.name = 'parent2';    this.play = [1, 2, 3]  }  function Child2() {    this.type = 'child2';  }  Child2.prototype = new Parent2();  console.log(new Child2());

看似没有问题,父类的办法和属性都可能拜访,但实际上有一个潜在的有余。举个例子:

var s1 = new Child2();  var s2 = new Child2();  s1.play.push(4);  console.log(s1.play, s2.play); // [1,2,3,4] [1,2,3,4]

明明我只扭转了s1的play属性,为什么s2也跟着变了呢?很简略,因为两个实例应用的是同一个原型对象

第三种形式:将前两种组合:

function Parent3 () {    this.name = 'parent3';    this.play = [1, 2, 3];  }  function Child3() {    Parent3.call(this);    this.type = 'child3';  }  Child3.prototype = new Parent3();  var s3 = new Child3();  var s4 = new Child3();  s3.play.push(4);  console.log(s3.play, s4.play); // [1,2,3,4] [1,2,3]
之前的问题都得以解决。然而这里又徒增了一个新问题,那就是Parent3的构造函数会多执行了一次(Child3.prototype = new Parent3();)。这是咱们不愿看到的。那么如何解决这个问题?

第四种形式: 组合继承的优化1

function Parent4 () {    this.name = 'parent4';    this.play = [1, 2, 3];  }  function Child4() {    Parent4.call(this);    this.type = 'child4';  }  Child4.prototype = Parent4.prototype;
这里让将父类原型对象间接给到子类,父类构造函数只执行一次,而且父类属性和办法均能拜访,然而咱们来测试一下
var s3 = new Child4();  var s4 = new Child4();  console.log(s3)
子类实例的构造函数是Parent4,显然这是不对的,应该是Child4。

第五种形式(最举荐应用):优化2

function Parent5 () {    this.name = 'parent5';    this.play = [1, 2, 3];  }  function Child5() {    Parent5.call(this);    this.type = 'child5';  }  Child5.prototype = Object.create(Parent5.prototype);  Child5.prototype.constructor = Child5;
这是最举荐的一种形式,靠近完满的继承。

实现instanceOf

思路:

  • 步骤1:先获得以后类的原型,以后实例对象的原型链
  • 步骤2:始终循环(执行原型链的查找机制)

    • 获得以后实例对象原型链的原型链(proto = proto.__proto__,沿着原型链始终向上查找)
    • 如果 以后实例的原型链__proto__上找到了以后类的原型prototype,则返回 true
    • 如果 始终找到Object.prototype.__proto__ == nullObject的基类(null)下面都没找到,则返回 false
// 实例.__ptoto__ === 类.prototypefunction _instanceof(example, classFunc) {    // 因为instance要检测的是某对象,须要有一个前置判断条件    //根本数据类型间接返回false    if(typeof example !== 'object' || example === null) return false;    let proto = Object.getPrototypeOf(example);    while(true) {        if(proto == null) return false;        // 在以后实例对象的原型链上,找到了以后类        if(proto == classFunc.prototype) return true;        // 沿着原型链__ptoto__一层一层向上查        proto = Object.getPrototypeof(proto); // 等于proto.__ptoto__    }}console.log('test', _instanceof(null, Array)) // falseconsole.log('test', _instanceof([], Array)) // trueconsole.log('test', _instanceof('', Array)) // falseconsole.log('test', _instanceof({}, Object)) // true

实现一个队列

基于链表构造实现队列
const LinkedList = require('./实现一个链表构造')// 用链表默认应用数组来模仿队列,性能更佳class Queue {  constructor() {    this.ll = new LinkedList()  }  // 向队列中增加  offer(elem) {    this.ll.add(elem)  }  // 查看第一个  peek() {    return this.ll.get(0)  }  // 队列只能从头部删除  remove() {    return this.ll.remove(0)  }}var queue = new Queue()queue.offer(1)queue.offer(2)queue.offer(3)var removeVal = queue.remove(3)console.log(queue.ll,'queue.ll')console.log(removeVal,'queue.remove')console.log(queue.peek(),'queue.peek')

实现every办法

Array.prototype.myEvery=function(callback, context = window){    var len=this.length,        flag=true,        i = 0;    for(;i < len; i++){      if(!callback.apply(context,[this[i], i , this])){        flag=false;        break;      }     }    return flag;  }  // var obj = {num: 1}  // var aa=arr.myEvery(function(v,index,arr){  //     return v.num>=12;  // },obj)  // console.log(aa)

实现LRU淘汰算法

LRU 缓存算法是一个十分经典的算法,在很多面试中常常问道,不仅仅包含前端面试

LRU 英文全称是 Least Recently Used,英译过去就是” 最近起码应用 “的意思。LRU 是一种罕用的页面置换算法,抉择最近最久未应用的页面予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰

艰深的解释:

如果咱们有一块内存,专门用来缓存咱们最近发拜访的网页,拜访一个新网页,咱们就会往内存中增加一个网页地址,随着网页的一直减少,内存存满了,这个时候咱们就须要思考删除一些网页了。这个时候咱们找到内存中最早拜访的那个网页地址,而后把它删掉。这一整个过程就能够称之为 LRU 算法

上图就很好的解释了 LRU 算法在干嘛了,其实非常简单,无非就是咱们往内存外面增加或者删除元素的时候,遵循最近起码应用准则

应用场景

LRU 算法应用的场景十分多,这里简略举几个例子即可:

  • 咱们操作系统底层的内存治理,其中就包含有 LRU 算法
  • 咱们常见的缓存服务,比方 redis 等等
  • 比方浏览器的最近浏览记录存储
  • vue中的keep-alive组件应用了LRU算法

梳理实现 LRU 思路

  • 特点剖析:

    • 咱们须要一块无限的存储空间,因为有限的化就没必要应用LRU算发删除数据了。
    • 咱们这块存储空间外面存储的数据须要是有序的,因为咱们必须要程序来删除数据,所以能够思考应用 ArrayMap 数据结构来存储,不能应用 Object,因为它是无序的。
    • 咱们可能删除或者增加以及获取到这块存储空间中的指定数据。
    • 存储空间存满之后,在增加数据时,会主动删除工夫最长远的那条数据。
  • 实现需求:

    • 实现一个 LRUCache 类型,用来充当存储空间
    • 采纳 Map 数据结构存储数据,因为它的存取时间复杂度为 O(1),数组为 O(n)
    • 实现 getset 办法,用来获取和增加数据
    • 咱们的存储空间有长度限度,所以无需提供删除办法,存储满之后,主动删除最长远的那条数据
    • 当应用 get 获取数据后,该条数据须要更新到最后面

具体实现

class LRUCache {  constructor(length) {    this.length = length; // 存储长度    this.data = new Map(); // 存储数据  }  // 存储数据,通过键值对的形式  set(key, value) {    const data = this.data;    if (data.has(key)) {      data.delete(key)    }    data.set(key, value);    // 如果超出了容量,则须要删除最久的数据    if (data.size > this.length) {      const delKey = data.keys().next().value;      data.delete(delKey);    }  }  // 获取数据  get(key) {    const data = this.data;    // 未找到    if (!data.has(key)) {      return null;    }    const value = data.get(key); // 获取元素    data.delete(key); // 删除元素    data.set(key, value); // 从新插入元素    return value // 返回获取的值  }}var lruCache = new LRUCache(5);
  • set 办法:往 map 外面增加新数据,如果增加的数据存在了,则先删除该条数据,而后再增加。如果增加数据后超长了,则须要删除最长远的一条数据。data.keys().next().value 便是获取最初一条数据的意思。
  • get 办法:首先从 map 对象中拿出该条数据,而后删除该条数据,最初再从新插入该条数据,确保将该条数据挪动到最后面
// 测试// 存储数据 set:lruCache.set('name', 'test');lruCache.set('age', 10);lruCache.set('sex', '男');lruCache.set('height', 180);lruCache.set('weight', '120');console.log(lruCache);

持续插入数据,此时会超长,代码如下:

lruCache.set('grade', '100');console.log(lruCache);

此时咱们发现存储工夫最久的 name 曾经被移除了,新插入的数据变为了最后面的一个。

咱们应用 get 获取数据,代码如下:

咱们发现此时 sex 字段曾经跑到最后面去了

总结

LRU 算法其实逻辑十分的简略,明确了原理之后实现起来十分的简略。最次要的是咱们须要应用什么数据结构来存储数据,因为 map 的存取十分快,所以咱们采纳了它,当然数组其实也能够实现的。还有一些小伙伴应用链表来实现 LRU,这当然也是能够的。

实现Promise相干办法

实现Promise的resolve

实现 resolve 静态方法有三个要点:
  • 传参为一个 Promise, 则间接返回它。
  • 传参为一个 thenable 对象,返回的 Promise 会追随这个对象,采纳它的最终状态作为本人的状态。
  • 其余状况,间接返回以该值为胜利状态的promise对象。
Promise.resolve = (param) => {  if(param instanceof Promise) return param;  return new Promise((resolve, reject) => {    if(param && param.then && typeof param.then === 'function') {      // param 状态变为胜利会调用resolve,将新 Promise 的状态变为胜利,反之亦然      param.then(resolve, reject);    }else {      resolve(param);    }  })}

实现 Promise.reject

Promise.reject 中传入的参数会作为一个 reason 一成不变地往下传, 实现如下:
Promise.reject = function (reason) {    return new Promise((resolve, reject) => {        reject(reason);    });}

实现 Promise.prototype.finally

后面的promise不论胜利还是失败,都会走到finally中,并且finally之后,还能够持续then(阐明它还是一个then办法是要害),并且会将初始的promise值一成不变的传递给前面的then.

Promise.prototype.finally最大的作用

  • finally里的函数,无论如何都会执行,并会把后面的值一成不变传递给下一个then办法中
  • 如果finally函数中有promise等异步工作,会等它们全副执行结束,再联合之前的胜利与否状态,返回值

Promise.prototype.finally六大状况用法

// 状况1Promise.resolve(123).finally((data) => { // 这里传入的函数,无论如何都会执行  console.log(data); // undefined})// 状况2 (这里,finally办法相当于做了两头解决,起一个过渡的作用)Promise.resolve(123).finally((data) => {  console.log(data); // undefined}).then(data => {  console.log(data); // 123})// 状况3 (这里只有reject,都会走到下一个then的err中)Promise.reject(123).finally((data) => {  console.log(data); // undefined}).then(data => {  console.log(data);}, err => {  console.log(err, 'err'); // 123 err})// 状况4 (一开始就胜利之后,会期待finally里的promise执行结束后,再把后面的data传递到下一个then中)Promise.resolve(123).finally((data) => {  console.log(data); // undefined  return new Promise((resolve, reject) => {    setTimeout(() => {      resolve('ok');    }, 3000)  })}).then(data => {  console.log(data, 'success'); // 123 success}, err => {  console.log(err, 'err');})// 状况5 (尽管一开始胜利,然而只有finally函数中的promise失败了,就会把其失败的值传递到下一个then的err中)Promise.resolve(123).finally((data) => {  console.log(data); // undefined  return new Promise((resolve, reject) => {    setTimeout(() => {      reject('rejected');    }, 3000)  })}).then(data => {  console.log(data, 'success');}, err => {  console.log(err, 'err'); // rejected err})// 状况6 (尽管一开始失败,然而也要等finally中的promise执行完,能力把一开始的err传递到err的回调中)Promise.reject(123).finally((data) => {  console.log(data); // undefined  return new Promise((resolve, reject) => {    setTimeout(() => {      resolve('resolve');    }, 3000)  })}).then(data => {  console.log(data, 'success');}, err => {  console.log(err, 'err'); // 123 err})

源码实现

Promise.prototype.finally = function (callback) {  return this.then((data) => {    // 让函数执行 外部会调用办法,如果办法是promise,须要期待它实现    // 如果以后promise执行时失败了,会把err传递到,err的回调函数中    return Promise.resolve(callback()).then(() => data); // data 上一个promise的胜利态  }, err => {    return Promise.resolve(callback()).then(() => {      throw err; // 把之前的失败的err,抛出去    });  })}

实现 Promise.all

对于 all 办法而言,须要实现上面的外围性能:
  • 传入参数为一个空的可迭代对象,则间接进行resolve
  • 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败。
  • 在任何状况下,Promise.all 返回的 promise 的实现状态的后果都是一个数组
Promise.all = function(promises) {  return new Promise((resolve, reject) => {    let result = [];    let index = 0;    let len = promises.length;    if(len === 0) {      resolve(result);      return;    }    for(let i = 0; i < len; i++) {      // 为什么不间接 promise[i].then, 因为promise[i]可能不是一个promise      Promise.resolve(promise[i]).then(data => {        result[i] = data;        index++;        if(index === len) resolve(result);      }).catch(err => {        reject(err);      })    }  })}

实现promise.allsettle

MDN: Promise.allSettled()办法返回一个在所有给定的promise曾经fulfilledrejected后的promise,并带有一个对象数组,每个对象示意对应的promise`后果

当您有多个彼此不依赖的异步工作胜利实现时,或者您总是想晓得每个promise的后果时,通常应用它。

【译】Promise.allSettledPromise.all 相似, 其参数承受一个Promise的数组, 返回一个新的Promise, 惟一的不同在于, 其不会进行短路, 也就是说当Promise全副解决实现后咱们能够拿到每个Promise的状态, 而不论其是否解决胜利。

用法 | 测试用例

let fs = require('fs').promises;let getName = fs.readFile('./name.txt', 'utf8'); // 读取文件胜利let getAge = fs.readFile('./age.txt', 'utf8');Promise.allSettled([1, getName, getAge, 2]).then(data => {    console.log(data);});// 输入后果/*    [    { status: 'fulfilled', value: 1 },    { status: 'fulfilled', value: 'zf' },    { status: 'fulfilled', value: '11' },    { status: 'fulfilled', value: 2 }    ]*/let getName = fs.readFile('./name123.txt', 'utf8'); // 读取文件失败let getAge = fs.readFile('./age.txt', 'utf8');// 输入后果/*    [    { status: 'fulfilled', value: 1 },    {      status: 'rejected',      value: [Error: ENOENT: no such file or directory, open './name123.txt'] {        errno: -2,        code: 'ENOENT',        syscall: 'open',        path: './name123.txt'      }    },    { status: 'fulfilled', value: '11' },    { status: 'fulfilled', value: 2 }  ]*/

实现

function isPromise (val) {  return typeof val.then === 'function'; // (123).then => undefined}Promise.allSettled = function(promises) {  return new Promise((resolve, reject) => {    let arr = [];    let times = 0;    const setData = (index, data) => {      arr[index] = data;      if (++times === promises.length) {        resolve(arr);      }      console.log('times', times)    }    for (let i = 0; i < promises.length; i++) {      let current = promises[i];      if (isPromise(current)) {        current.then((data) => {          setData(i, { status: 'fulfilled', value: data });        }, err => {          setData(i, { status: 'rejected', value: err })        })      } else {        setData(i, { status: 'fulfilled', value: current })      }    }  })}

实现 Promise.race

race 的实现相比之下就简略一些,只有有一个 promise 执行完,间接 resolve 并进行执行
Promise.race = function(promises) {  return new Promise((resolve, reject) => {    let len = promises.length;    if(len === 0) return;    for(let i = 0; i < len; i++) {      Promise.resolve(promise[i]).then(data => {        resolve(data);        return;      }).catch(err => {        reject(err);        return;      })    }  })}

实现一个简版Promise

// 应用var promise = new Promise((resolve,reject) => {    if (操作胜利) {        resolve(value)    } else {        reject(error)    }})promise.then(function (value) {    // success},function (value) {    // failure})
function myPromise(constructor) {    let self = this;    self.status = "pending"   // 定义状态扭转前的初始状态    self.value = undefined;   // 定义状态为resolved的时候的状态    self.reason = undefined;  // 定义状态为rejected的时候的状态    function resolve(value) {       if(self.status === "pending") {          self.value = value;          self.status = "resolved";       }    }    function reject(reason) {       if(self.status === "pending") {          self.reason = reason;          self.status = "rejected";       }    }    // 捕捉结构异样    try {       constructor(resolve,reject);    } catch(e) {       reject(e);    }}
// 增加 then 办法myPromise.prototype.then = function(onFullfilled,onRejected) {   let self = this;   switch(self.status) {      case "resolved":        onFullfilled(self.value);        break;      case "rejected":        onRejected(self.reason);        break;      default:          }}var p = new myPromise(function(resolve,reject) {    resolve(1)});p.then(function(x) {    console.log(x) // 1})

应用class实现

class MyPromise {  constructor(fn) {    this.resolvedCallbacks = [];    this.rejectedCallbacks = [];    this.state = 'PENDING';    this.value = '';    fn(this.resolve.bind(this), this.reject.bind(this));  }  resolve(value) {    if (this.state === 'PENDING') {      this.state = 'RESOLVED';      this.value = value;      this.resolvedCallbacks.map(cb => cb(value));       }  }  reject(value) {    if (this.state === 'PENDING') {      this.state = 'REJECTED';      this.value = value;      this.rejectedCallbacks.map(cb => cb(value));    }  }  then(onFulfilled, onRejected) {    if (this.state === 'PENDING') {      this.resolvedCallbacks.push(onFulfilled);      this.rejectedCallbacks.push(onRejected);    }    if (this.state === 'RESOLVED') {      onFulfilled(this.value);    }    if (this.state === 'REJECTED') {      onRejected(this.value);    }  }}

Promise 实现-具体

  • 能够把 Promise 看成一个状态机。初始是 pending 状态,能够通过函数 resolvereject ,将状态转变为 resolved或者 rejected 状态,状态一旦扭转就不能再次变动。
  • then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 标准规定除了 pending 状态,其余状态是不能够扭转的,如果返回的是一个雷同实例的话,多个 then 调用就失去意义了。
  • 对于 then来说,实质上能够把它看成是 flatMap
// 三种状态const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";// promise 接管一个函数参数,该函数会立刻执行function MyPromise(fn) {  let _this = this;  _this.currentState = PENDING;  _this.value = undefined;  // 用于保留 then 中的回调,只有当 promise  // 状态为 pending 时才会缓存,并且每个实例至少缓存一个  _this.resolvedCallbacks = [];  _this.rejectedCallbacks = [];  _this.resolve = function (value) {    if (value instanceof MyPromise) {      // 如果 value 是个 Promise,递归执行      return value.then(_this.resolve, _this.reject)    }    setTimeout(() => { // 异步执行,保障执行程序      if (_this.currentState === PENDING) {        _this.currentState = RESOLVED;        _this.value = value;        _this.resolvedCallbacks.forEach(cb => cb());      }    })  };  _this.reject = function (reason) {    setTimeout(() => { // 异步执行,保障执行程序      if (_this.currentState === PENDING) {        _this.currentState = REJECTED;        _this.value = reason;        _this.rejectedCallbacks.forEach(cb => cb());      }    })  }  // 用于解决以下问题  // new Promise(() => throw Error('error))  try {    fn(_this.resolve, _this.reject);  } catch (e) {    _this.reject(e);  }}MyPromise.prototype.then = function (onResolved, onRejected) {  var self = this;  // 标准 2.2.7,then 必须返回一个新的 promise  var promise2;  // 标准 2.2.onResolved 和 onRejected 都为可选参数  // 如果类型不是函数须要疏忽,同时也实现了透传  // Promise.resolve(4).then().then((value) => console.log(value))  onResolved = typeof onResolved === 'function' ? onResolved : v => v;  onRejected = typeof onRejected === 'function' ? onRejected : r => throw r;  if (self.currentState === RESOLVED) {    return (promise2 = new MyPromise(function (resolve, reject) {      // 标准 2.2.4,保障 onFulfilled,onRjected 异步执行      // 所以用了 setTimeout 包裹下      setTimeout(function () {        try {          var x = onResolved(self.value);          resolutionProcedure(promise2, x, resolve, reject);        } catch (reason) {          reject(reason);        }      });    }));  }  if (self.currentState === REJECTED) {    return (promise2 = new MyPromise(function (resolve, reject) {      setTimeout(function () {        // 异步执行onRejected        try {          var x = onRejected(self.value);          resolutionProcedure(promise2, x, resolve, reject);        } catch (reason) {          reject(reason);        }      });    }));  }  if (self.currentState === PENDING) {    return (promise2 = new MyPromise(function (resolve, reject) {      self.resolvedCallbacks.push(function () {        // 思考到可能会有报错,所以应用 try/catch 包裹        try {          var x = onResolved(self.value);          resolutionProcedure(promise2, x, resolve, reject);        } catch (r) {          reject(r);        }      });      self.rejectedCallbacks.push(function () {        try {          var x = onRejected(self.value);          resolutionProcedure(promise2, x, resolve, reject);        } catch (r) {          reject(r);        }      });    }));  }};// 标准 2.3function resolutionProcedure(promise2, x, resolve, reject) {  // 标准 2.3.1,x 不能和 promise2 雷同,防止循环援用  if (promise2 === x) {    return reject(new TypeError("Error"));  }  // 标准 2.3.2  // 如果 x 为 Promise,状态为 pending 须要持续期待否则执行  if (x instanceof MyPromise) {    if (x.currentState === PENDING) {      x.then(function (value) {        // 再次调用该函数是为了确认 x resolve 的        // 参数是什么类型,如果是根本类型就再次 resolve        // 把值传给下个 then        resolutionProcedure(promise2, value, resolve, reject);      }, reject);    } else {      x.then(resolve, reject);    }    return;  }  // 标准 2.3.3.3.3  // reject 或者 resolve 其中一个执行过得话,疏忽其余的  let called = false;  // 标准 2.3.3,判断 x 是否为对象或者函数  if (x !== null && (typeof x === "object" || typeof x === "function")) {    // 标准 2.3.3.2,如果不能取出 then,就 reject    try {      // 标准 2.3.3.1      let then = x.then;      // 如果 then 是函数,调用 x.then      if (typeof then === "function") {        // 标准 2.3.3.3        then.call(          x,          y => {            if (called) return;            called = true;            // 标准 2.3.3.3.1            resolutionProcedure(promise2, y, resolve, reject);          },          e => {            if (called) return;            called = true;            reject(e);          }        );      } else {        // 标准 2.3.3.4        resolve(x);      }    } catch (e) {      if (called) return;      called = true;      reject(e);    }  } else {    // 标准 2.3.4,x 为根本类型    resolve(x);  }}

实现Promisify

const fs = require('fs')const path = require('path')// node中应用// const fs = require('fs').promises 12.18版// const promisify = require('util').promisify// 包装node api promise化 典型的高级函数const promisify = fn=>{  return (...args)=>{    return new Promise((resolve,reject)=>{      fn(...args, (err,data)=>{        if(err) {          reject(err)        }         resolve(data)      })    })  }}// const read = promisify(fs.readFile)// read(path.join(__dirname, './promise.js'), 'utf8').then(d=>{//   console.log(d)// })// promise化node所有apiconst promisifyAll = target=>{  Reflect.ownKeys(target).forEach(key=>{    if(typeof target[key] === 'function') {      target[key+'Async'] = promisify(target[key])    }  })  return target}// promise化fs下的函数const promisifyNew = promisifyAll(fs)promisifyNew.readFileAsync(path.join(__dirname, './promise.js'), 'utf8').then(d=>{  console.log(d)})module.exports = {  promisify,  promisifyAll}

残缺实现Promises/A+标准

/** * Promises/A+标准 实现一个promise * https://promisesaplus.com/*/const EMUM = {  PENDING: 'PENDING',  FULFILLED: 'FULFILLED',  REJECTED: 'REJECTED'}// x 返回值// promise2 then的时候new的promise// promise2的resolve, rejectconst resolvePromise = (x, promise2, resolve, reject)=>{  // 解析promise的值解析promise2是胜利还是失败 传递到上层then  if(x === promise2) {    reject(new TypeError('类型谬误'))  }  // 这里的x如果是一个promise的话 可能是其余的promise,可能调用了胜利 又调用了失败  // 避免resolve的时候 又throw err抛出异样到reject了  let called  // 如果x是promise 那么就采纳他的状态  // 有then办法是promise  if(typeof x === 'object' && typeof x!== null || typeof x === 'function') {    // x是对象或函数    try {      let then = x.then // 缓存,不必屡次取值      if(typeof then === 'function') {        // 是promise,调用then办法外面有this,须要传入this为x能力取到then办法外面的值this.value        then.call(x, y=>{// 胜利          // y值可能也是一个promise 如resolve(new Promise()) 此时的y==new Promise()          // 递归解析y,直到拿到一般的值resolve(x进来)          if(called) return;          called = true;          resolvePromise(y, promise2, resolve, reject)        },r=>{// 一旦失败间接失败          if(called) return;          called = true;          reject(r)        })      } else {        // 一般对象不是promise        resolve(x)      }    } catch (e) {      // 对象取值可能报错,用defineProperty定义get 抛出异样      if(called) return;      called = true;      reject(e)    }  } else {    // x是一般值    resolve(x) // 间接胜利  }}class myPromise {  constructor(executor) {    this.status = EMUM.PENDING // 以后状态    this.value = undefined // resolve接管值    this.reason = undefined // reject失败返回值    /**     * 同一个promise能够then屡次(公布订阅模式)     * 调用then时 以后状态是期待态,须要将以后胜利或失败的回调寄存起来(订阅)     * 调用resolve时 将订阅函数进行执行(公布)    */    // 胜利队列    this.onResolvedCallbacks = []    // 失败队列    this.onRejectedCallbacks = []    const resolve = value =>{      // 如果value是一个promise,须要递归解析      // 如 myPromise.resolve(new myPromise()) 须要解析value      if(value instanceof myPromise) {        // 不停的解析 直到值不是promise        return value.then(resolve,reject)      }      if(this.status === EMUM.PENDING) {        this.status = EMUM.FULFILLED        this.value = value        this.onResolvedCallbacks.forEach(fn=>fn())      }    }    const reject = reason =>{      if(this.status === EMUM.PENDING) {        this.status = EMUM.REJECTED        this.reason = reason        this.onRejectedCallbacks.forEach(fn=>fn())      }    }    try {      executor(resolve,reject)    } catch(e) {      reject(e)    }  }  then(onFulFilled, onRejected) {    // 透传 解决默认不传的状况    // new Promise((resolve,reject)=>{    //   resolve(1)    // }).then().then().then(d=>{})    // new Promise((resolve,reject)=>{    //   resolve(1)    // }).then(v=>v).then(v=>v).then(d=>{})    // new Promise((resolve,reject)=>{    //   reject(1)    // }).then().then().then(null, e=>{console.log(e)})    // new Promise((resolve,reject)=>{    //   reject(1)    // }).then(null,e=>{throw e}).then(null,e=>{throw e}).then(null,e=>{console.log(e)})    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : v => v    onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}    // 调用then 创立一个新的promise    let promise2 = new myPromise((resolve,reject)=>{      // 依据value判断是resolve 还是reject value也可能是promise      if(this.status === EMUM.FULFILLED) {        setTimeout(() => {          try {            // 胜利回调后果            let x = onFulFilled(this.value)            // 解析promise            resolvePromise(x, promise2,resolve,reject)          } catch (error) {            reject(error)          }        }, 0);      }      if(this.status === EMUM.REJECTED) {        setTimeout(() => {          try {            let x = onRejected(this.reason)            // 解析promise            resolvePromise(x, promise2,resolve,reject)          } catch (error) {            reject(error)          }        }, 0);      }      // 用户还未调用resolve或reject办法      if(this.status === EMUM.PENDING) {        this.onResolvedCallbacks.push(()=>{          try {            let x = onFulFilled(this.value)            // 解析promise            resolvePromise(x, promise2,resolve,reject)          } catch (error) {            reject(error)          }        })        this.onRejectedCallbacks.push(()=>{          try {            let x = onRejected(this.reason)            // 解析promise            resolvePromise(x, promise2,resolve,reject)          } catch (error) {            reject(error)          }        })      }    })    return promise2  }  catch(errCallback) {    // 等同于没有胜利,把失败放进去而已    return this.then(null, errCallback)  }  // myPromise.resolve 具备期待性能的 如果参数的promise会期待promise解析结束在向下执行  static resolve(val) {    return new myPromise((resolve,reject)=>{      resolve(val)    })  }  // myPromise.reject 间接将值返回  static reject(reason) {    return new myPromise((resolve,reject)=>{      reject(reason)    })  }  // finally传入的函数 无论胜利或失败都执行  // Promise.reject(100).finally(()=>{console.log(1)}).then(d=>console.log('success',d)).catch(er=>console.log('faild',er))  // Promise.reject(100).finally(()=>new Promise()).then(d=>console.log(d)).catch(er=>)  finally(callback) {    return this.then((val)=>{      return myPromise.resolve(callback()).then(()=>val)    },(err)=>{      return myPromise.resolve(callback()).then(()=>{throw err})    })  }  // Promise.all  static all(values) {    return new myPromise((resolve,reject)=>{      let resultArr = []      let orderIndex = 0      const processResultByKey = (value,index)=>{        resultArr[index] = value         // 解决齐全部        if(++orderIndex === values.length) {          resolve(resultArr) // 解决实现的后果返回去        }      }      for (let i = 0; i < values.length; i++) {        const value = values[i];        // 是promise        if(value && typeof value.then === 'function') {          value.then((val)=>{            processResultByKey(val,i)          },reject)        } else {          // 不是promise状况          processResultByKey(value,i)        }      }    })  }  static race(promises) {    // 采纳最新胜利或失败的作为后果    return new myPromise((resolve,reject)=>{      for (let i = 0; i < promises.length; i++) {        let val = promises[i]        if(val && typeof val.then === 'function') {          // 任何一个promise先调用resolve或reject就返回后果了 也就是返回执行最快的那个promise的后果          val.then(resolve,reject)        }else{          // 一般值          resolve(val)        }      }    })  }}/** * =====测试用例-==== */// let promise1 = new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('胜利')//   }, 900);// })// promise1.then(val=>{//   console.log('success', val)// },reason=>{//   console.log('fail', reason)// })/** * then的应用形式 一般值象征不是promise *  * 1、then中的回调有两个办法 胜利或失败 他们的后果返回(一般值)会传递给外层的下一个then中 * 2、能够在胜利或失败中抛出异样,走到下一次then的失败中 * 3、返回的是一个promsie,那么会用这个promise的状态作为后果,会用promise的后果向下传递 * 4、错误处理,会默认先找离本人最新的错误处理,找不到就向下查找,找打了就执行 */// read('./name.txt').then(data=>{//   return '123'// }).then(data=>{// }).then(null,err=>{// })// // .catch(err=>{ // catch就是没有胜利的promise// // })/** * promise.then实现原理:通过每次返回一个新的promise来实现(promise一旦胜利就不能失败,失败就不能胜利) *  */// function read(data) {//   return new myPromise((resolve,reject)=>{//     setTimeout(() => {//       resolve(new myPromise((resolve,reject)=>resolve(data)))//     }, 1000);//   })// }// let promise2 = read({name: 'poetry'}).then(data=>{//   return data// }).then().then().then(data=>{//   console.log(data,'-data-')// },(err)=>{//   console.log(err,'-err-')// })// finally测试// myPromise//   .resolve(100)//   .finally(()=>{//     return new myPromise((resolve,reject)=>setTimeout(() => {//       resolve(100)//     }, 100))//   })//   .then(d=>console.log('finally success',d))//   .catch(er=>console.log(er, 'finally err'))/** * promise.all 测试 *  * myPromise.all 解决并发问题 多个异步并发获取最终的后果*/// myPromise.all([1,2,3,4,new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('ok1')//   }, 1000);// }),new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('ok2')//   }, 1000);// })]).then(d=>{//   console.log(d,'myPromise.all.resolve')// }).catch(err=>{//   console.log(err,'myPromise.all.reject')// })// 实现promise中断请求let promise = new Promise((resolve,reject)=>{  setTimeout(() => {    // 模仿接口调用 ajax调用超时    resolve('胜利')   }, 10000);})function promiseWrap(promise) {  // 包装一个promise 能够管制原来的promise是胜利 还是失败  let abort  let newPromsie = new myPromise((resolve,reject)=>{    abort = reject  })  // 只有管制newPromsie失败,就能够管制被包装的promise走向失败  // Promise.race 任何一个先胜利或者失败 就能够取得后果  let p = myPromise.race([promise, newPromsie])  p.abort = abort  return p}let newPromise = promiseWrap(promise)setTimeout(() => {  // 超过3秒超时  newPromise.abort('申请超时')}, 3000);newPromise.then(d=>{  console.log('d',d)}).catch(err=>{  console.log('err',err)})// 应用promises-aplus-tests 测试写的promise是否标准// 全局装置 cnpm i -g promises-aplus-tests// 命令行执行 promises-aplus-tests promise.js// 测试入口 产生提早对象myPromise.defer = myPromise.deferred = function () {  let dfd = {}  dfd.promise = new myPromise((resolve,reject)=>{    dfd.resolve = resolve    dfd.reject = reject  })  return dfd}// 提早对象用户// ![](http://img-repo.poetries.top/images/20210509172817.png)// promise解决嵌套问题// function readData(url) {//   let dfd = myPromise.defer()//   fs.readFile(url, 'utf8', function (err,data) {//     if(err) {//       dfd.reject()//     }//     dfd.resolve(data)//   })//   return dfd.promise// }// readData().then(d=>{//   return d// })module.exports = myPromise

参考 前端进阶面试题具体解答

实现一个繁难的MVVM

实现一个繁难的MVVM我会分为这么几步来:
  1. 首先我会定义一个类Vue,这个类接管的是一个options,那么其中可能有须要挂载的根元素的id,也就是el属性;而后应该还有一个data属性,示意须要双向绑定的数据
  2. 其次我会定义一个Dep类,这个类产生的实例对象中会定义一个subs数组用来寄存所依赖这个属性的依赖,曾经增加依赖的办法addSub,删除办法removeSub,还有一个notify办法用来遍历更新它subs中的所有依赖,同时Dep类有一个动态属性target它用来示意以后的观察者,当后续进行依赖收集的时候能够将它增加到dep.subs中。
  3. 而后设计一个observe办法,这个办法接管的是传进来的data,也就是options.data,外面会遍历data中的每一个属性,并应用Object.defineProperty()来重写它的getset,那么这外面呢能够应用new Dep()实例化一个dep对象,在get的时候调用其addSub办法增加以后的观察者Dep.target实现依赖收集,并且在set的时候调用dep.notify办法来告诉每一个依赖它的观察者进行更新
  4. 实现这些之后,咱们还须要一个compile办法来将HTML模版和数据联合起来。在这个办法中首先传入的是一个node节点,而后遍历它的所有子级,判断是否有firstElmentChild,有的话则进行递归调用compile办法,没有firstElementChild的话且该child.innderHTML用正则匹配满足有/\{\{(.*)\}\}/项的话则示意有须要双向绑定的数据,那么就将用正则new Reg('\\{\\{\\s*' + key + '\\s*\\}\\}', 'gm')替换掉是其为msg变量。
  5. 实现变量替换的同时,还须要将Dep.target指向以后的这个child,且调用一下this.opt.data[key],也就是为了触发这个数据的get来对以后的child进行依赖收集,这样下次数据变动的时候就能告诉child进行视图更新了,不过在最初要记得将Dep.target指为null哦(其实在Vue中是有一个targetStack栈用来寄存target的指向的)
  6. 那么最初咱们只须要监听documentDOMContentLoaded而后在回调函数中实例化这个Vue对象就能够了

coding :

须要留神的点:

  • childNodes会获取到所有的子节点以及文本节点(包含元素标签中的空白节点)
  • firstElementChild示意获取元素的第一个字元素节点,以此来辨别是不是元素节点,如果是的话则调用compile进行递归调用,否则用正则匹配
  • 这外面的正则真的不难,大家能够看一下

残缺代码如下:

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <meta http-equiv="X-UA-Compatible" content="ie=edge" />    <title>MVVM</title>  </head>  <body>    <div id="app">      <h3>姓名</h3>      <p>{{name}}</p>      <h3>年龄</h3>      <p>{{age}}</p>    </div>  </body></html><script>  document.addEventListener(    "DOMContentLoaded",    function () {      let opt = { el: "#app", data: { name: "期待批改...", age: 20 } };      let vm = new Vue(opt);      setTimeout(() => {        opt.data.name = "jing";      }, 2000);    },    false  );  class Vue {    constructor(opt) {      this.opt = opt;      this.observer(opt.data);      let root = document.querySelector(opt.el);      this.compile(root);    }    observer(data) {      Object.keys(data).forEach((key) => {        let obv = new Dep();        data["_" + key] = data[key];        Object.defineProperty(data, key, {          get() {            Dep.target && obv.addSubNode(Dep.target);            return data["_" + key];          },          set(newVal) {            obv.update(newVal);            data["_" + key] = newVal;          },        });      });    }    compile(node) {      [].forEach.call(node.childNodes, (child) => {        if (!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)) {          let key = RegExp.$1.trim();          child.innerHTML = child.innerHTML.replace(            new RegExp("\\{\\{\\s*" + key + "\\s*\\}\\}", "gm"),            this.opt.data[key]          );          Dep.target = child;          this.opt.data[key];          Dep.target = null;        } else if (child.firstElementChild) this.compile(child);      });    }  }  class Dep {    constructor() {      this.subNode = [];    }    addSubNode(node) {      this.subNode.push(node);    }    update(newVal) {      this.subNode.forEach((node) => {        node.innerHTML = newVal;      });    }  }</script>

简化版2

function update(){  console.log('数据变动~~~ mock update view')}let obj = [1,2,3]// 变异办法 push shift unshfit reverse sort splice pop// Object.definePropertylet oldProto = Array.prototype;let proto = Object.create(oldProto); // 克隆了一分['push','shift'].forEach(item=>{  proto[item] = function(){    update();    oldProto[item].apply(this,arguments);  }})function observer(value){ // proxy reflect  if(Array.isArray(value)){    // AOP    return value.__proto__ = proto;    // 重写 这个数组里的push shift unshfit reverse sort splice pop  }  if(typeof value !== 'object'){    return value;  }  for(let key in value){    defineReactive(value,key,value[key]);  }}function defineReactive(obj,key,value){  observer(value); // 如果是对象 持续减少getter和setter  Object.defineProperty(obj,key,{    get(){        return value;    },    set(newValue){        if(newValue !== value){            observer(newValue);            value = newValue;            update();        }    }  })}observer(obj); // AOP// obj.name = {n:200}; // 数据变了 须要更新视图 深度监控// obj.name.n = 100;obj.push(123);obj.push(456);console.log(obj);

实现JSONP办法

利用<script>标签不受跨域限度的特点,毛病是只能反对 get 申请
  • 创立script标签
  • 设置script标签的src属性,以问号传递参数,设置好回调函数callback名称
  • 插入到html文本中
  • 调用回调函数,res参数就是获取的数据
function jsonp({url,params,callback}) {  return new Promise((resolve,reject)=>{  let script = document.createElement('script')    window[callback] = function (data) {      resolve(data)      document.body.removeChild(script)    }    var arr = []    for(var key in params) {      arr.push(`${key}=${params[key]}`)    }    script.type = 'text/javascript'    script.src = `${url}?callback=${callback}&${arr.join('&')}`    document.body.appendChild(script)  })}
// 测试用例jsonp({  url: 'http://suggest.taobao.com/sug',  callback: 'getData',  params: {    q: 'iphone手机',    code: 'utf-8'  },}).then(data=>{console.log(data)})
  • 设置 CORS: Access-Control-Allow-Origin:*
  • postMessage

实现Ajax

步骤

  • 创立 XMLHttpRequest 实例
  • 收回 HTTP 申请
  • 服务器返回 XML 格局的字符串
  • JS 解析 XML,并更新部分页面
  • 不过随着历史进程的推动,XML 曾经被淘汰,取而代之的是 JSON。

理解了属性和办法之后,依据 AJAX 的步骤,手写最简略的 GET 申请。

对象数组列表转成树形构造(解决菜单)

[    {        id: 1,        text: '节点1',        parentId: 0 //这里用0示意为顶级节点    },    {        id: 2,        text: '节点1_1',        parentId: 1 //通过这个字段来确定子父级    }    ...]转成[    {        id: 1,        text: '节点1',        parentId: 0,        children: [            {                id:2,                text: '节点1_1',                parentId:1            }        ]    }]

实现代码如下:

function listToTree(data) {  let temp = {};  let treeData = [];  for (let i = 0; i < data.length; i++) {    temp[data[i].id] = data[i];  }  for (let i in temp) {    if (+temp[i].parentId != 0) {      if (!temp[temp[i].parentId].children) {        temp[temp[i].parentId].children = [];      }      temp[temp[i].parentId].children.push(temp[i]);    } else {      treeData.push(temp[i]);    }  }  return treeData;}

异步串行 | 异步并行

// 字节面试题,实现一个异步加法function asyncAdd(a, b, callback) {  setTimeout(function () {    callback(null, a + b);  }, 500);}// 解决方案// 1. promisifyconst promiseAdd = (a, b) => new Promise((resolve, reject) => {  asyncAdd(a, b, (err, res) => {    if (err) {      reject(err)    } else {      resolve(res)    }  })})// 2. 串行解决async function serialSum(...args) {  return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))}// 3. 并行处理async function parallelSum(...args) {  if (args.length === 1) return args[0]  const tasks = []  for (let i = 0; i < args.length; i += 2) {    tasks.push(promiseAdd(args[i], args[i + 1] || 0))  }  const results = await Promise.all(tasks)  return parallelSum(...results)}// 测试(async () => {  console.log('Running...');  const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)  console.log(res1)  const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)  console.log(res2)  console.log('Done');})()

手写深度比拟isEqual

思路:深度比拟两个对象,就是要深度比拟对象的每一个元素。=> 递归
  • 递归退出条件:

    • 被比拟的是两个值类型变量,间接用“===”判断
    • 被比拟的两个变量之一为null,直接判断另一个元素是否也为null
  • 提前结束递推:

    • 两个变量keys数量不同
    • 传入的两个参数是同一个变量
  • 递推工作:深度比拟每一个key
function isEqual(obj1, obj2){    //其中一个为值类型或null    if(!isObject(obj1) || !isObject(obj2)){        return obj1 === obj2;    }    //判断是否两个参数是同一个变量    if(obj1 === obj2){        return true;    }    //判断keys数是否相等    const obj1Keys = Object.keys(obj1);    const obj2Keys = Object.keys(obj2);    if(obj1Keys.length !== obj2Keys.length){        return false;    }    //深度比拟每一个key    for(let key in obj1){        if(!isEqual(obj1[key], obj2[key])){            return false;        }    }    return true;}

数组中的数据依据key去重

给定一个任意数组,实现一个通用函数,让数组中的数据依据 key 排重:

const dedup = (data, getKey = () => {} ) => {  // todo}let data = [  { id: 1, v: 1 },  { id: 2, v: 2 },  { id: 1, v: 1 },];// 以 id 作为排重 key,执行函数失去后果// data = [//   { id: 1, v: 1 },//   { id: 2, v: 2 },// ];

实现

const dedup = (data, getKey = () => { }) => {    const dateMap = data.reduce((pre, cur) => {        const key = getKey(cur)        if (!pre[key]) {            pre[key] = cur        }        return pre    }, {})    return Object.values(dateMap)}

应用

let data = [    { id: 1, v: 1 },    { id: 2, v: 2 },    { id: 1, v: 1 },];console.log(dedup(data, (item) => item.id))// 以 id 作为排重 key,执行函数失去后果// data = [//   { id: 1, v: 1 },//   { id: 2, v: 2 },// ];

实现节流函数(throttle)

节流函数原理:指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的工夫才会执行回调函数。总结起来就是: 事件,依照一段时间的距离来进行触发

像dom的拖拽,如果用消抖的话,就会呈现卡顿的感觉,因为只在进行的时候执行了一次,这个时候就应该用节流,在肯定工夫内屡次执行,会晦涩很多

手写简版

应用工夫戳的节流函数会在第一次触发事件时立刻执行,当前每过 wait 秒之后才执行一次,并且最初一次触发事件不会被执行

工夫戳形式:

// func是用户传入须要防抖的函数// wait是等待时间const throttle = (func, wait = 50) => {  // 上一次执行该函数的工夫  let lastTime = 0  return function(...args) {    // 以后工夫    let now = +new Date()    // 将以后工夫和上一次执行函数工夫比照    // 如果差值大于设置的等待时间就执行函数    if (now - lastTime > wait) {      lastTime = now      func.apply(this, args)    }  }}setInterval(  throttle(() => {    console.log(1)  }, 500),  1)

定时器形式:

应用定时器的节流函数在第一次触发时不会执行,而是在 delay 秒之后才执行,当最初一次进行触发后,还会再执行一次函数
function throttle(func, delay){  var timer = null;  returnfunction(){    var context = this;    var args = arguments;    if(!timer){      timer = setTimeout(function(){        func.apply(context, args);        timer = null;      },delay);    }  }}

实用场景:

  • DOM 元素的拖拽性能实现(mousemove
  • 搜寻联想(keyup
  • 计算鼠标挪动的间隔(mousemove
  • Canvas 模仿画板性能(mousemove
  • 监听滚动事件判断是否到页面底部主动加载更多
  • 拖拽场景:固定工夫内只执行一次,避免超高频次触发地位变动
  • 缩放场景:监控浏览器resize
  • 动画场景:防止短时间内屡次触发动画引起性能问题

总结

  • 函数防抖 :将几次操作合并为一次操作进行。原理是保护一个计时器,规定在delay工夫后触发函数,然而在delay工夫内再次触发的话,就会勾销之前的计时器而从新设置。这样一来,只有最初一次操作能被触发。
  • 函数节流 :使得肯定工夫内只触发一次函数。原理是通过判断是否达到肯定工夫来触发函数。

实现一个 sleep 函数,比方 sleep(1000) 意味着期待1000毫秒

// 应用 promise来实现 sleepconst sleep = (time) => {  return new Promise(resolve => setTimeout(resolve, time))}sleep(1000).then(() => {  // 这里写你的骚操作})

树形构造转成列表(解决菜单)

[    {        id: 1,        text: '节点1',        parentId: 0,        children: [            {                id:2,                text: '节点1_1',                parentId:1            }        ]    }]转成[    {        id: 1,        text: '节点1',        parentId: 0 //这里用0示意为顶级节点    },    {        id: 2,        text: '节点1_1',        parentId: 1 //通过这个字段来确定子父级    }    ...]

实现代码如下:

function treeToList(data) {  let res = [];  const dfs = (tree) => {    tree.forEach((item) => {      if (item.children) {        dfs(item.children);        delete item.children;      }      res.push(item);    });  };  dfs(data);  return res;}

实现lodash的chunk办法--数组按指定长度拆分

题目

/** * @param input * @param size * @returns {Array} */_.chunk(['a', 'b', 'c', 'd'], 2)// => [['a', 'b'], ['c', 'd']]_.chunk(['a', 'b', 'c', 'd'], 3)// => [['a', 'b', 'c'], ['d']]_.chunk(['a', 'b', 'c', 'd'], 5)// => [['a', 'b', 'c', 'd']]_.chunk(['a', 'b', 'c', 'd'], 0)// => []

实现

function chunk(arr, length) {  let newArr = [];  for (let i = 0; i < arr.length; i += length) {    newArr.push(arr.slice(i, i + length));  }  return newArr;}

设计一个办法提取对象中所有value大于2的键值对并返回最新的对象

实现:

var obj = { a: 1, b: 3, c: 4 }foo(obj) // { b: 3, c: 4 }

办法有很多种,这里提供一种比拟简洁的写法,用到了ES10Object.fromEntries()

var obj = { a: 1, b: 3, c: 4 }function foo (obj) {  return Object.fromEntries(    Object.entries(obj).filter(([key, value]) => value > 2)  )}var obj2 = foo(obj) // { b: 3, c: 4 }console.log(obj2)
// ES8中 Object.entries()的作用:var obj = { a: 1, b: 2 }var entries = Object.entries(obj); // [['a', 1], ['b', 2]]// ES10中 Object.fromEntries()的作用:Object.fromEntries(entries); // { a: 1, b: 2 }

实现一个链表构造

链表构造

看图了解next层级

// 链表 从头尾删除、减少 性能比拟好// 分为很多类 罕用单向链表、双向链表// js模仿链表构造:增删改查// node节点class Node {  constructor(element,next) {    this.element = element    this.next = next  } }class LinkedList { constructor() {   this.head = null // 默认应该指向第一个节点   this.size = 0 // 通过这个长度能够遍历这个链表 } // 减少O(n) add(index,element) {   if(arguments.length === 1) {     // 向开端增加     element = index // 以后元素等于传递的第一项     index = this.size // 索引指向最初一个元素   }  if(index < 0 || index > this.size) {    throw new Error('增加的索引不失常')  }  if(index === 0) {    // 间接找到头部 把头部改掉 性能更好    let head = this.head    this.head = new Node(element,head)  } else {    // 获取以后头指针    let current = this.head    // 不停遍历 直到找到最初一项 增加的索引是1就找到第0个的next赋值    for (let i = 0; i < index-1; i++) { // 找到它的前一个      current = current.next    }    // 让创立的元素指向上一个元素的下一个    // 看图了解next层级    current.next = new Node(element,current.next) // 让以后元素指向下一个元素的next  }  this.size++; } // 删除O(n) remove(index) {  if(index < 0 || index >= this.size) {    throw new Error('删除的索引不失常')  }  this.size--  if(index === 0) {    let head = this.head    this.head = this.head.next // 挪动指针地位    return head // 返回删除的元素  }else {    let current = this.head    for (let i = 0; i < index-1; i++) { // index-1找到它的前一个      current = current.next    }    let returnVal = current.next // 返回删除的元素    // 找到待删除的指针的上一个 current.next.next     // 如删除200, 100=>200=>300 找到200的上一个100的next的next为300,把300赋值给100的next即可    current.next = current.next.next     return returnVal  } } // 查找O(n) get(index) {  if(index < 0 || index >= this.size) {    throw new Error('查找的索引不失常')  }  let current = this.head  for (let i = 0; i < index; i++) {    current = current.next  }  return current }}var ll = new LinkedList()ll.add(0,100) // Node { ellement: 100, next: null }ll.add(0,200) // Node { element: 200, next: Node { element: 100, next: null } }ll.add(1,500) // Node {element: 200,next: Node { element: 100, next: Node { element: 500, next: null } } }ll.add(300)ll.remove(0)console.log(ll.get(2),'get')console.log(ll.head)module.exports = LinkedList

数组去重办法汇总

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

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