相关自定义函数

;(function () {
  // 一个函数无论我们 bind 几次, this 永远由第一次 bind 决定
  const a = {}
  const fn = function () {
    console.log(this)
  }
  fn.bind().bind(a)()

  // 换种写法
  const fn2 = function () {
    return function () {
      return fn.apply()
    }.apply(a)
  }
  fn2()
})()

// 闭包第一题
;(function () {
  function createIncrement() {
    let count = 0
    function increment() {
      count++
    }

    let message = `Count is ${count}`
    function log() {
      console.log(message)
    }

    return [increment, log]
  }

  const [increment, log] = createIncrement()
  increment()
  increment()
  increment()
  log() // => ?
})()

// 闭包第二题
;(function () {
  function fn() {
    a = 0
    return function (b) {
      return b + a++
    }
  }
  var f = fn()
  console.log(f(5)) // 5
  console.log(fn()(5)) // 5
  console.log(f(5)) // 6
  console.log(a) // 2
})()
;(function () {
  /**
   * 简易版深拷贝函数
   */
  const deepClone = obj => {
    if (typeof obj !== 'object') {
      throw new Error('发生错误')
    }

    const newObj = obj instanceof Array ? [] : {}

    for (const key in obj) {
      if (Object.hasOwnProperty.call(obj, key)) {
        const value = obj[key]
        newObj[key] = typeof value === 'object' ? deepClone(value) : value
      }
    }
    return newObj
  }
})()
;(function () {
  // 变量提升转换前
  console.log(a) // undefined
  var a = 1
  function b() {
    console.log(a)
  }
  b() // 1

  // 变量提升转换前后
  var a = undefined
  console.log(a) // undefined
  a = 1
  function b() {
    console.log(a)
  }
  b() // 1
})()
;(function () {
  /**
   * 组合继承:子类的构造函数中通过 Parent.call(this) 继承父类的属性,
   * 然后改变子类的原型为 new Parent() 来继承父类的函数
   */
  function Parent(value) {
    this.value = value
  }

  Parent.prototype.getValue = function () {
    console.log(this.value)
  }

  function Child(value) {
    Parent.call(this, value) // 构造函数可以传参,不会与父类引用属性共享
  }

  Child.prototype = new Parent() // 可以复用父类的函数,但是子类原型上多了不需要的父类的属性

  const child = new Child(1)

  child.getValue() // 1

  child instanceof Parent // true

  /**
   * Class 继承,关键点在于 extends、super
   */
  class Parent {
    constructor(value) {
      this.value = value
    }

    getValue() {
      console.log(this.value)
    }
  }

  class Child extends Parent {
    constructor(value) {
      super(value)
      this.value = value
    }
  }

  const child = new Child(1)

  child.getValue() // 1

  child instanceof Parent // true
})()
;(function () {
  /**
   * 通过自定义 set 和 get 函数的方式,在原本的逻辑中插入了我们的函数
   * 逻辑(回调函数),实现了在对对象任何属性进行读写时发出通知
   */
  let obj = { a: 1 }

  let onWatch = (target, setCallback, getCallback) => {
    return new Proxy(target, {
      set(target, key, value, receiver) {
        setCallback(key, value)
        return Reflect.set(target, key, value, receiver)
      },
      get(target, key, receiver) {
        getCallback(target, key)

        // return target[key]
        return Reflect.get(target, key, receiver)
      },
    })
  }

  let p = onWtch(
    obj,
    (k, v) => {
      // 数据变化,响应式监听
      console.log(`监测到属性${k}改变为${v}`)
    },
    (t, k) => {
      // 数据读取,响应式派发
      console.log(`'${k}' = ${t[k]}`)
    }
  )

  p.a = 3 // 监测到属性a改变为3
  p.a // 'a' = 3
})()
;(function () {
  /**
   * 手写 Promise
   * 作用:1、消灭嵌套调用;2、合并多个任务的请求结果
   * API: Promise.resolve, Promise.reject, Promise.prototype.catch,
   * Promise.prototype.finally, Promise.all, Promise.race
   */
  const PENDING = 'pending'
  const RESOLVED = 'resolved'
  const REJECTED = 'rejected'
  function MyPromise(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined

    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = value => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED

        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED

        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  MyPromise.prototype.then = function (onResolved, onRejected) {
    onResolved = typeof onResolved === 'function' ? onResolved : v => v
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : err => {
            throw err
          }

    if (this.status === RESOLVED) {
      onResolved(this.value)
    }

    if (this.status === REJECTED) {
      onRejected(this.reason)
    }

    if (this.status === PENDING) {
      this.onResolvedCallbacks.push(() => {
        onResolved(this.value)
      })

      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
})()
;(function () {
  function mySetInterval(callback, interval) {
    let timer
    const now = Date.now
    let startTime = now()
    let endTime = startTime

    const loop = () => {
      timer = window.requestAnimationFrame(loop)
      endTime = now()
      if (endTime - startTime >= interval) {
        startTime = endTime = now()
        callback(timer)
      }
    }

    timer = window.requestAnimationFrame(loop)
    return timer
  }

  let a = 0
  mySetInterval(timer => {
    console.log('1')
    a++
    if (a === 3) {
      window.cancelAnimationFrame(timer)
    }
  }, 1000)
})()
;(function () {
  /**
   * Event loop(执行一个宏任务,执行所有微任务,再继续如此循环)
   * log:1,4,8,7,3,9,6,5,2
   */
  ;(function () {
    function test() {
      console.log(1)
      setTimeout(function () {
        console.log(2)
      }, 1000)
    }

    test()

    setTimeout(function () {
      Promise.resolve().then(() => {
        console.log(9)
      })
      console.log(3)
    })

    new Promise(function (resolve) {
      console.log(4)
      setTimeout(function () {
        console.log(5)
      }, 100)
      resolve()
    }).then(function () {
      setTimeout(function () {
        console.log(6)
      }, 0)
      console.log(7)
    })

    console.log(8)
  })()
})()
;(function () {
  /**
   * apply、call 的模拟实现,这两个方法被调用时,函数会立即执行,并返回结果
   */
  Function.prototype.myCall = function (context) {
    const context = context || window
    context.fn = this
    const args = []
    for (let i = 1; i < arguments.length; i++) {
      args.push('arguments[' + i + ']') // 由于后面会使用 eval 表达式,所以不能直接 push 具体的值
    }
    const result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
  }

  Function.prototype.myApply = function (context, arr) {
    const context = Object(context) || window
    context.fn = this
    let result
    if (!arr) {
      result = context.fn()
    } else {
      const args = []
      for (let i = 0; i < arr.length; i++) {
        args.push('arr[' + i + ']')
      }
      result = eval('context.fn(' + args + ')')
    }
    delete context.fn
    return result
  }
  /**
   * bind 的模拟实现。bind 方法会创建一个新函数,这个函数并不会立即执行。当这个新函数被调用时,bind的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
   */
  Function.prototype.myBind = function (context) {
    if (typeof this !== 'function') {
      throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
    }

    const self = this
    const args = Array.prototype.slice.call(arguments, 1) // 此处的 arguments 为 bind 时传递的参数
    const fNOP = function () {}

    const fbound = function () {
      /**
       * 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面修改了 fbound.prototype 为 绑定函数的
       * prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
       *
       * 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,
       * this 指向绑定的 context。
       */
      self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))) // 此处的 arguments 返回的函数执行时的参数,两处参数合并起来成为 bind 函数完整的参数
    }

    fNOP.prototype = this.prototype // 空函数中转,防止改变 fbound 函数的 prototype 时改变了原来函数的原型
    fbound.prototype = new fNOP()

    return fbound
  }
})()
;(function () {
  /**
   * 模拟实现 new 操作。e.g. myNew(Person,18)
   * @returns 新对象
   */
  function myNew() {
    const obj = new Object(), // 用new Object() 的方式新建了一个对象 obj
      Constructor = [].shift.call(arguments) // 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数

    obj.__proto__ = Constructor.prototype

    // function Person(name, age) {
    //   this.strength = 60
    //   this.age = age

    //   return {
    //     name: name,
    //     habit: 'Games',
    //   }
    // }

    // var person = new Person('Kevin', '18')

    // console.log(person.name) // Kevin
    // console.log(person.habit) // Games
    // console.log(person.strength) // undefined
    // console.log(person.age) // undefined

    const result = Constructor.apply(obj, arguments)

    return typeof result === 'object' ? result : obj // 构造函数返回值如果是一个对象,就返回这个对象,如果不是,就该返回什么就返回什么
  }
})()
;(function () {
  /**
   * 自定义instanceof函数
   */
  function myInstanceof(left, right) {
    const prototype = right.prototype
    let left = left.__proto__

    while (true) {
      if (left === null || left === undefined) {
        return false
      }

      if (prototype === left) {
        return true
      }

      left = left.__proto__
    }
  }
})()
;(function () {
  // 以下会先打印冒泡然后是捕获
  node.addEventListener(
    'click',
    event => {
      console.log('冒泡')
    },
    false
  )

  node.addEventListener(
    'click',
    event => {
      console.log('捕获')
    },
    true
  )
})()
;(function () {
  /**
   * url 链接返回的是:[callbackName]({code:0, data:[], msg: ''})
   * 在执行栈这个函数会立即执行
   */
  const jsonp = (url, callbackName, success) => {
    let script = document.createElement('script')
    script.src = url
    script.async = true
    script.type = 'text/script'
    window[callbackName] = function (data) {
      success && success(data)
    }
    document.body.appendChild(script)
  }

  jsonp('https://xxx', 'callback', function (value) {
    console.log(value)
  })
})()
;(function () {
  /**
   * 防抖:事件触发,N秒之后执行。期间再次触发,则重新计算
   */
  const debounce = (fn, wait, immediate) => {
    let timer

    const debounced = function (...args) {
      timer && clearTimeout(timer)
      if (immediate) {
        const callNow = !timer

        if (callNow) {
          fn.apply(this, args)
        }

        timer = setTimeout(() => {
          timer = null
        }, wait)
      } else {
        timer = setTimeout(() => {
          fn.apply(this, args)
        }, wait)
      }
    }

    return debounced
  }
})()
;(function () {
  /**
   * 节流:事件触发,马上执行,N秒之内,事件不再执行,N秒结束之时,再执行一次
   */
  const throttle = (fn, wait) => {
    let timer,
      previous = 0
    const throttled = function (...args) {
      const now = +new Date()
      const remaining = wait - (now - previous)

      if (remaining <= 0 || remaining > wait) {
        if (timer) {
          clearTimeout(timer)
          timer = null
        }
        previous = now
        fn.apply(this, args)
      } else if (!timer) {
        timer = setTimeout(() => {
          fn.apply(this, args)
          previous = +new Date()
          timer = null
        }, remaining)
      }
    }

    return throttled
  }
})()
;(function () {
  // 通过Dep解耦属性的依赖和更新操作
  class Dep {
    constructor() {
      this.subs = []
    }
    // 收集依赖
    add(sub) {
      this.subs.push(sub)
    }
    // 通知更新
    notify() {
      this.subs.forEach(sub => {
        sub.update()
      })
    }
  }
  Dep.target = null // 全局属性,通过该属性配置watcher

  class Watcher {
    constructor(obj, key, callback) {
      // 将 Dep.target 指向自己
      // 然后触发属性的 getter 触发监听
      // 最后将 Dep.target 置为空
      Dep.target = this
      this.callback = callback
      this.obj = obj
      this.key = key
      this.val = obj[key]
      Dep.target = null
    }

    update() {
      // 获得最新值
      this.val = this.obj[this.key]
      // 调用 update 方法 更新 dom
      this.callback(this.val)
    }
  }

  // 观察、监听
  function observe(obj) {
    if (!obj || typeof obj !== 'object') {
      return
    }

    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    })
  }
  function defineReactive(obj, key, val) {
    // 递归子属性
    observe(val)
    let dp = new Dep()

    Object.defineProperty(obj, key, {
      enumerable: true, // 可枚举
      configurable: true, // 可配置
      // 自定义函数
      set: function reactiveSetter(newVal) {
        val = newVal
        dp.notify() // 值变了以后才去通知
      },
      get: function reactiveGetter() {
        if (Dep.target) {
          // 只有在实例化watcher的时候依赖才会被添加进去
          dp.add(Dep.target)
        }
        return val
      },
    })
  }

  var data = { name: 'xiaoming' }
  observe(data)
  function update(value) {
    console.log(`watch 到新的值:${value}`)
  }

  new Watcher(data, 'name', update)

  data.name = 'xiaowang'
})()
;(function () {
  // 给 localstorage 设置过期时间
  Storage.prototype.setExpire = (key, value, expire) => {
    let obj = {
      data: value,
      time: Date.now(),
      expire: expire,
    }
    localStorage.setItem(key, JSON.stringify(obj))
  }

  Storage.prototype.getExpire = key => {
    let val = localStorage.getItem(key)
    if (!val) {
      return val
    }
    val = JSON.parse(val)
    if (Date.now() - val.time > val.expire) {
      localStorage.removeItem(key)
      return null
    }
    return val.data
  }
})()