闭包,Dom和Bom,手写深拷贝函数,手写防抖和节流,手写事件总线

1.闭包

2.Dom

3.Bom

4.手写深拷贝函数

5.防抖

6.节流

7.手写事件总线

1.闭包

1)个人理解含义:闭包就是可以访问外层作用域的自由变量的函数,广义的来说,函数就是闭包,侠义的来说访问了外层作用域变量的函数就是闭包。

2)mdn含义:mdn的解释是一个函数和周围状态的引用捆绑的组合就叫闭包

3)闭包作用:闭包可以让我们访问外层变量

4)闭包和函数的区别是:当捕捉闭包的时候,它的 自由变量 会在补充时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行。

5)闭包的缺点:会造成内存泄漏,因为上层作用域的变量被函数引用,函数又被上层作用域引用,导致,解决方法是将上层作用域的函数赋值为null,但是对于上层作用域不需要用到的变量是会被销毁的

2.Dom

继承自EventTarget,所以也可以使用EventTarget的方法
1)Node节点:
nodeName:node节点的名称。
nodeType:可以区分节点的类型。 
nodeValue:node节点的值; div节点的值是没有的 div标签里面的值需要div的子节点的nodeValue 来获取
childNodes:所有的子节点;
2)Document节点:
a.创建元素:
const imageEl = document.createElement("img")

const imageEl2 = new HTMLImageElement()

b.获取元素

const divEl1 = document.getElementById("box")//返回html元素

const divEl2 = document.getElementsByTagName("div") //返回包含多个节点的对象

const divEl3 = document.getElementsByName("hh")//返回节点数组

const divEl4 = document.querySelector(".content")//返回包含多个节点的对象

const divEl5 = document.querySelectorAll(".content")//返回html元素

c.方法

const value = divEl.getAttribute("age")

divEl.setAttribute("height", 1.88)

3.Bom

就是浏览器对象模型:包括window,history,location,document

1)window对象:

即是全局对象,又是浏览器窗口对象,其上面有属性,方法,事件,以及从EventTarget继承过来的方法,可以在MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Window进行查看,删除符号(已经废弃),点踩符号(不属于W3C规范),实验符号(实验性特性)

a)常用属性有screenX(浏览器相对于屏幕的距离)  screenY  scrollX  scrollY  outerHeight(整个浏览器高度,包括工具栏)  innerHeight(不包括工具栏)

b)常见方法scrollTo  close  open(打开新网页)

c)常见事件onload(加载完毕触发)  onfocus(点击屏幕内部触发,不包括浏览器工具栏等)  onblur (点击非展示内容触发) onhashchange

d)继承自EventTarget的方法:dispatchEvent(new Event("coderwhy"))  addEventListener  removeEventListener

e)补充:各种跳转得区别window.open(地址,打开方式),location.assign("http://www.baidu.com")、location.href = "http://www.baidu.com"  location.replace("http://www.baidu.com")  history.pushState({name: "coderwhy"}, "", "/detail")  history.replaceState({name: "coderwhy"}, "", "/detail"):

(a)是否可以回退:

一开始就执行会有可以回退到前一页:window.open

点击事件可以回退到前一页:location.assign  location.href  history.pushState

不可以回退:location.replace   history.replaceState

(b)是否可以/detail跳转:除了location.href都可以跳到当前域名下detail页面

(c)是否会发起请求:除了history对应的几个方法,其他都会向服务器发起请求

2)location对象:

例如:http://www.baidu.com:8000/web?search=1#12

href: 当前window对应的超链接URL, 整个URL;     http://www.baidu.com:8000/web?search=1#12
origin:http://www.baidu.com:8000/web
host: 主机地址;  www.baidu.com:8000
protocol: 当前的协议;   http:
hostname: 主机地址(不带端口);   www.baidu.com
port: 端口;默认是80    8000
pathname: 路径; /web
search: 查询字符串; search=1
hash: 哈希值;   #12
username:URL中的username(很多浏览器已经禁用);
password:URL中的password(很多浏览器已经禁用)
方法:assign   replace   reload(可以传入一个Boolean类型; true是代表强制加载 false是先检查 缓存 如果有缓存 先从缓存里 ) 
3)history
属性:length:会话中的记录条数;
           state:当前保留的状态值;如果是新打开就是null
方法:back():返回上一页,等价于history.go(-1);
           forward():前进下一页,等价于history.go(1);
           go():加载历史中的某一页;
           pushState():打开一个指定的地址; 第一个参数是state,第二个参数是username 第三个       是地址
           replaceState():打开一个新的地址,并且使用replace; 同pushState

4.手写深拷贝函数

function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === "object" || valueType === "function")
}



function deepClone(originValue, map = new WeakMap()) {
  // 判断是否是一个Set类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // 判断如果是Symbol的value, 那么创建一个新的Symbol
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description)
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 判断传入的originValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue
  }
  if (map.has(originValue)) {
    return map.get(originValue)
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}
  map.set(originValue, newObject)
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    // const newSKey = Symbol(sKey.description)
    newObject[sKey] = deepClone(originValue[sKey], map)
  }
  
  return newObject
}


// deepClone({name: "why"})


// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")

const obj = {
  name: "why",
  age: 18,
  friend: {
    name: "james",
    address: {
      city: "广州"
    }
  },
  // 数组类型
  hobbies: ["abc", "cba", "nba"],
  // 函数类型
  foo: function(m, n) {
    console.log("foo function")
    console.log("100代码逻辑")
    return 123
  },
  // Symbol作为key和value
  [s1]: "abc",
  s2: s2,
  // Set/Map
  set: new Set(["aaa", "bbb", "ccc"]),
  map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}

obj.info = obj

const newObj = deepClone(obj)
console.log(newObj === obj)

obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2)

console.log(newObj.info.info.info)

5.防抖

最后一次触发

function debounce(fn, delay, immediate = false, resultCallback) {
  // 1.定义一个定时器, 保存上一次的定时器
  let timer = null
  let isInvoke = false

  // 2.真正执行的函数
  const _debounce = function(...args) {
    return new Promise((resolve, reject) => {
      // 取消上一次的定时器
      if (timer) clearTimeout(timer)

      // 判断是否需要立即执行
      if (immediate && !isInvoke) {
        const result = fn.apply(this, args)
        if (resultCallback) resultCallback(result)
        resolve(result)
        isInvoke = true
      } else {
        // 延迟执行
        timer = setTimeout(() => {
          // 外部传入的真正要执行的函数
          const result = fn.apply(this, args)
          if (resultCallback) resultCallback(result)
          resolve(result)
          isInvoke = false
          timer = null
        }, delay)
      }
    })
  }

  // 封装取消功能
  _debounce.cancel = function() {
    if (timer) clearTimeout(timer)
    timer = null
    isInvoke = false
  }

  return _debounce
}

6.节流

第一次触发

function throttle(fn, interval, options = { leading: true, trailing: false }) {
  // 1.记录上一次的开始时间
  const { leading, trailing, resultCallback } = options
  let lastTime = 0
  let timer = null

  // 2.事件触发时, 真正执行的函数
  const _throttle = function(...args) {
    return new Promise((resolve, reject) => {
      // 2.1.获取当前事件触发时的时间
      const nowTime = new Date().getTime()
      if (!lastTime && !leading) lastTime = nowTime

      // 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
      const remainTime = interval - (nowTime - lastTime)
      if (remainTime <= 0) {
        if (timer) {
          clearTimeout(timer)
          timer = null
        }

        // 2.3.真正触发函数
        const result = fn.apply(this, args)
        if (resultCallback) resultCallback(result)
        resolve(result)
        // 2.4.保留上次触发的时间
        lastTime = nowTime
        return
      }

      if (trailing && !timer) {
        timer = setTimeout(() => {
          timer = null
          lastTime = !leading ? 0: new Date().getTime()
          const result = fn.apply(this, args)
          if (resultCallback) resultCallback(result)
          resolve(result)
        }, remainTime)
      }
    })
  }

  _throttle.cancel = function() {
    if(timer) clearTimeout(timer)
    timer = null
    lastTime = 0
  }

  return _throttle
}

7.手写事件总线

class HYEventBus {
  constructor() {
    this.eventBus = {}
  }

  on(eventName, eventCallback, thisArg) {
    let handlers = this.eventBus[eventName]
    if (!handlers) {
      handlers = []
      this.eventBus[eventName] = handlers
    }
    handlers.push({
      eventCallback,
      thisArg
    })
  }

  off(eventName, eventCallback) {
    const handlers = this.eventBus[eventName]
    if (!handlers) return
    const newHandlers = [...handlers]
    for (let i = 0; i < newHandlers.length; i++) {
      const handler = newHandlers[i]
      if (handler.eventCallback === eventCallback) {
        const index = handlers.indexOf(handler)
        handlers.splice(index, 1)
      }
    }
  }

  emit(eventName, ...payload) {
    const handlers = this.eventBus[eventName]
    if (!handlers) return
    handlers.forEach(handler => {
      handler.eventCallback.apply(handler.thisArg, payload)
    })
  }
}

const eventBus = new HYEventBus()

// main.js
eventBus.on("abc", function() {
  console.log("监听abc1", this)
}, {name: "why"})

const handleCallback = function() {
  console.log("监听abc2", this)
}
eventBus.on("abc", handleCallback, {name: "why"})

// utils.js
eventBus.emit("abc", 123)

// 移除监听
eventBus.off("abc", handleCallback)
eventBus.emit("abc", 123)

你可能感兴趣的:(JavaScript,javascript,前端)