1)个人理解含义:闭包就是可以访问外层作用域的自由变量的函数,广义的来说,函数就是闭包,侠义的来说访问了外层作用域变量的函数就是闭包。
2)mdn含义:mdn的解释是一个函数和周围状态的引用捆绑的组合就叫闭包
3)闭包作用:闭包可以让我们访问外层变量
4)闭包和函数的区别是:当捕捉闭包的时候,它的 自由变量 会在补充时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行。
5)闭包的缺点:会造成内存泄漏,因为上层作用域的变量被函数引用,函数又被上层作用域引用,导致,解决方法是将上层作用域的函数赋值为null,但是对于上层作用域不需要用到的变量是会被销毁的
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)
就是浏览器对象模型:包括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
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)
最后一次触发
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
}
第一次触发
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
}
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)