# 代码实现

# LazyMan

设计 LazyMan 类,实现以下功能。

LazyMan('Tony');
// Hi I am Tony

LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

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

实现思路就是将所有要执行的任务放一个队列中,然后再实现一个方法来有序得执行队列中任务

方法一:

function LazyManClass(name){
    console.log(`Hi I am ${name}`)
    this.stack = []
    setTimeout(() => {
        this.next()
    })
    return this
}
LazyManClass.prototype.next = async function(time){
    let fn = this.stack.shift()
    while(fn) {
        await fn()
        fn = this.stack.shift()
    }
    return this
}
LazyManClass.prototype.sleep = function(time){
    this.stack.push(async function(){
        await new Promise((resolve) => {
        setTimeout(resolve, time*1000)
     })
   })
   return this
}
LazyManClass.prototype.sleepFirst = function(time){
    this.stack.unshift(async function(){
        await new Promise((resolve) => {
        setTimeout(() => {
            resolve()
        }, time*1000)
    })
    })
    return this
}
LazyManClass.prototype.eat = function(foot){
    this.stack.push(function(){
        console.log(`I am eating ${foot}`)
    })
    return this
}
function LazyMan(name) {
    return new LazyManClass(name)
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')

注意上面 next 函数对 this.stack 遍历的时候,不能使用 forEacth,因为这种特环方式不能正常执行异步函数

方式二:

class LazyManClass {
    constructor(name) {
        this.taskList = [];
        this.name = name;
        console.log(`Hi I am ${this.name}`);
        setTimeout(() => {
            this.next();
        }, 0);
    }
    eat (name) {
        var that = this;
        var fn = (function (n) {
            return function () {
                console.log(`I am eating ${n}`)
                that.next();
            }
        })(name);
        this.taskList.push(fn);
        return this;
    }
    sleepFirst (time) {
        var that = this;
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000);  
            }
        })(time);
        this.taskList.unshift(fn);
        return this;
    }
    sleep (time) {
        var that = this
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000); 
            }
        })(time);
        this.taskList.push(fn);
        return this;
    }
    next () {
        var fn = this.taskList.shift();
        fn && fn();
    }
}
function LazyMan(name) {
    return new LazyManClass(name);
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(4).eat('junk food');

# 实现 new

new做了什么?

  1. 创建一个新对象

  2. this 指向这个新对象

  3. 将创建的对象的原型指向构造函数的原型

  4. 返回一个对象

  • 新对象具有构造函数的所有属性和方法
function _New(fn, ...args){
	const obj = Object.create(fn.prototype)  // 完成了步骤1、2
	const ret = fn.apply(obj, args)
    return obj
}

Object.create()

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction(); // My name is Matthew. Am I human? tru

# 网络请求失败自动重试功能函数封装

发起一个网络请求,遇到失败的情况,在规定时间(timeout)内,可以重复尝试(count)次.

  1. 当请求成功时返回正确信息
  2. 当超过规定时间时,返回超时
  3. 若重复尝试次数为0,且未超过超时时间时,返回错误信息。
  4. 可使用es7,8,9语法
function isTimeOut(time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('超时')
      }, time)
    })
  }
  function repeatFetch(fn, count) {
    return fn()
      .catch(err => {
        if(count === 0) {
          return Promise.reject(err)
        }
        return repeatFetch(fn, --count)
      })
  }
  /**
   * @param { Function } fn : 请求方法
   * @param { Number } count : 重复次数
   * @param { Number } time : 过期时间
   * */
  function doFetch(fn, count, time) {
    return Promise.race([isTimeOut(time), repeatFetch(fn, count)])
  }

# 防抖

  function de(fn, delay){
    let timer = null
    return function () {
      clearTimeout(timer,)
      timer = setTimeout(fn.bind(null, ...arguments), delay)
    }
  }

使用粟子:

// test
function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 给防抖函数传参
}

# 劫流

  function debounce(fn, delay) {
    let running = false
    return function () {
      if(running) return
      running = true
      fn.apply(this, arguments); // 用apply指向调用debounce的对象,相当于_this.fn(args);
      setTimeout(function () {
        running = false
      }, delay);
    };
  }

使用粟子:

// test
function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 给防抖函数传参
}

# 实现instanceOf

instanceof 运算符用来检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

// 定义构造函数
function C(){}
var o = new C();
o instanceof C; // true,
o instanceof Object; // true,
  function isInstanceOf(obj, parent) {
    while (obj.__proto__){
      if(obj.__proto__ === parent.prototype){
        return true
      }
      obj = obj.__proto__
    }
    return false
  }
isInstanceOf([], Array) // true
isInstanceOf('abc', Array) // false

# 实现call、apply、bind

callapplybind 的作用就是改变的函数执行时的 this 指向

回顾一个函数的 this 是谁调用了函数,那么 this 就指向谁,所以可以根据这个特性来手机实现

call

// 方式一
function myCall(fn, obj, ...arg) {
   obj.doFn = fn
   return obj.doFn(...arg)
}
// 方式二
Function.prototype.myCall= function (ctx, ...arg) {
    ctx.doFn = this 
    ctx.doFn(...arg)
    delete ctx.doFn
}

apply

// 方式一
function myApply(fn, obj, arg) {
  obj.doFn = fn
  return obj.doFn(...arg)
}
// 方式二
Function.prototype.myApply= function (ctx, arg) {
    ctx.doFn = this 
    ctx.doFn(...arg)
    delete ctx.doFn
}

bind

// 方式二
  function myBind(fn, obj, ...arg1) {
    return (...arg2) => {
      obj.doFn = fn
      return obj.doFn(...arg1, ...arg2)
    }
  }
// 方式二
Function.prototype.myBind= function (ctx, arg1) {
    return (...arg2) => {
      ctx.doFn = this
      let val = obj.doFn(...arg1, ...arg2)
      delete ctx.doFn
      return val
    }
}

# 实现JSON.stringify()

JSON.stringify()特性:

  1. 数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。

  2. 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。

  3. undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)

  4. symbol 作为 key 的属性也会被忽略

  5. 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。

  6. 不可枚举的属性会被忽略

  function myStringify(obj) {
    let weak = new WeakSet()
    let walk = (data) => {
      if(typeof data !== 'object'){
        return data
      }
      // 实现特性 5
      if(weak.has(data)){
        throw new Error('出现了循环了')
        return
      }
      weak.add(data)
      let type = Object.prototype.toString.call(data)
      if(type !== '[object Array]' && type !== '[object Object]'){ // 简单实现特性 2
        return data.toString()
      }
      let tem = []
      // Object.keys 会自动过滤的 symbol 的key,实现特性5
      Object.keys(data).forEach(item => {
        let getTypeOf = typeof data[item]
        if(getTypeOf === 'symbol' || getTypeOf === 'function'||getTypeOf===undefined) return // 实现特性 4
        if(type === '[object Array]'){
          tem.push(walk(data[item]))
        } else  if(type === '[object Object]'){
          tem.push(`${item}:`+walk(data[item]))
        }
      })
      if(type === '[object Array]'){
        return `[${tem.join()}]`
      } else  if(type === '[object Object]'){
        return `{${tem.join()}}`
      }

    }
    return walk(obj)
  }
  let testMyStringify = {
    a: new Number(1234),
    b: 'ac',
    arr: [1,2,3,4],
  }
  console.log('myStringify({})', myStringify(testMyStringify)) //  {a:1234,b:ac,arr:[1,2,3,4]}
  console.log('myStringify({})', JSON.stringify(testMyStringify)) //{"a":1234,"b":"ac","arr":[1,2,3,4]}

# 实现一个JSONparse

eval方式

function strToJson(str) {
  return eval('('+str+')')
}

new Functoin()方式

function strToJson(str) {
  return (new Function('return' + str))()
}

# 模拟Object.create

function create(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}

# Promise

  function _promise(fn) {
    this.doResolve = null
    this.doReject = null
    this.sonResolve = null // then 返回的 promise
    fn(this._resolve.bind(this), this._reject.bind(this))
  }
  _promise.prototype._resolve = function (data) {
    if(!this.doResolve) return
    queueMicrotask(() => {
        let res = this.doResolve(data)
        this.sonResolve(res)
    })

  }
  _promise.prototype._reject = function (data) {
    if(!this.doReject) return
    queueMicrotask(() => {
        let res = this.doReject(data)
        this.sonResolve(res)
    })
  }
  _promise.prototype.then = function (fn) {
    this.doResolve = fn
    return new _promise(resolve => {
      this.sonResolve = resolve
    })
  }
  _promise.prototype.catch = function (fn) {
    this.doReject = fn
    return new _promise(resolve => {
      this.sonResolve = resolve
    })
  }
  let pro = new _promise((resolve, reject) => {
    setTimeout(() => {
      resolve('哈哈')
    }, 2000)
  })
  pro.then(res => {
    console.log('第一个then', res)
    return '自定义Promise', res
  })
  .then(res => {
    console.log('第二个then',res)
  })

# 手写-setTimeout 模拟实现 setInterval

  function myInterval(fn, timer) {
    let timeOut = null
    let action = () =>{
      timeOut = setTimeout(() => {
        fn()
        action()
      }, timer)
    }
    action()
    return function () {
      clearTimeout(timeOut)
    }
  }