前端开发核心知识进阶 读书笔记
01、this
02、闭包
调用栈
function fn1() { fn2() } function fn2() { fn3() } function fn3() { fn4() } function fn4() { debugger console.log('fn4') } fn1() 复制代码
对应可以使用上面的调试按钮,看一看函数出栈的过程,压栈和弹栈这个过程就非常清楚了。
闭包
比较通俗的理解:函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,这就形成了闭包。
如下代码匿名函数
访问了外层函数的num
变量,并且在全局作用域
中可以访问到这个匿名函数
。
function numGenerator() { let num = 1 return () => { console.log(num++) } } let getNum = numGenerator() getNum() 复制代码
所以numGenerator
函数执行完毕以后,相关调用栈出栈以后,变量num
不会消失,仍然有机会被外界访问到。
内存管理
var foo = 'bar' // 分配内存 alert(foo) // 读写内存 foo = null // 释放内存 复制代码
内存管理泄露案例
第一个
<div id="element"> <div>test</div> </div> var element = document.getElementById('element') element.mark = 'marked' // 移除 element 节点 function remove() { element.parentNode.removeChild(element) } 复制代码
第二个
<div id="element"> </div> var element = document.getElementById('element') element.innerHTML = '<button id="button">点击</button>' var button = document.getElementById('button') button.addEventListener('click', function() { //..... }) element.innerHTML = '' 复制代码
所以还需要调用removeEventListaner
函数,以防止内存泄露。
第三个
function foo() { var name = '小明' window.setInterval(function(){ console.log(name) }, 1000) } foo() 复制代码
调用这段代码之后name
变量空间始终无法释放。一定要使用clearInterval
来进行清理。
第四个
function foo() { let value = Math.random() return () =>{ console.log(value) } } let bar = foo() bar() bar = null 复制代码
devtool 排查问题
可以看出这两者都是随着时间在一直上升的。
分析内存, 对占比比较大的进行分析。---
03、我们只实现API
jQuery 中的offset()
如何获取文档中任意一个元素与文档顶部的距离?
这里我们使用getBoundingClientRect()
这个方法来实现。递归的方式有兴趣可以去研究。
var id = document.getElementById("app"); var clientTop = id.clientTop; function offset(element) { let result = element.getBoundingClientRect(); let docElement = element.ownerDocument.documentElement; return { left: result.left + window.pageXOffset - docElement.clientLeft, top: result.top + window.pageYOffset - doc4Element.clientTop, }; } // element.ownerDocument 是DOM节点的一个属性,返回当前节点的顶层 document对象 // window.pageXOffset // x轴滚动的距离 // window.pageYOffset // y轴滚动的距离 let result = offset(id); 复制代码
实现一个reduce()函数
ing 复制代码
compose() 函数
主要用于执行一连串长度不定的任务(方法)
看如下代码
const fn1 = x => x + 1 const fn2 = x => x * 3 const fn3 = x => x / 2 let ret = fn1(fn2(fn3(2))) // 2/2 * 3 + 1 console.log(ret) // 4 复制代码
这段代码的功能是把每一个函数执行的结果交给下一个函数当做参数。现在我们有三个函数,我们可以把代码写成这样,假设我们有更多的函数呢?
所以我们期待有一个这样的函数, 从右往左依次执行。 let funcs = [fn1,fn2,fn3] let operate = compose(funcs) // 这个函数里面实现串行任务的执行。 operate(2) // 用于接收第一个函数的 入参 复制代码
于是我们想到了reduce
函数
function compose(...funcs) { // x 存储的就是第一个函数执行的时候的实参 。就是最内部的函数执行的参数 // 翻转顺序 funcs = funcs.reverse() return function(x) { return funcs.reduce((accumulator, currValue)=>{ // 把上一个函数执行的结果交给当前函数 return currValue(accumulator) }, x) } } 复制代码
实现pipe 函数
pipe
和 compose
函数 只是执行的顺序不同,如果用recude
实现的话就是把内部的reverse
去掉就可以了。用数组本身的顺序去执行。
// compose fn1(fn2(fn3(2))) // pipe fn3(fn2(fn1(2))) 复制代码
实现一个bind函数
ing 复制代码
04、js高频考点及基础只是题库
数据类型分类
5中基本数据类型:number
,string
,boolean
,undefined
,null
其余都是object类型。
typeof
typeof 'a' // 'string' typeof 1 // 'number' typeof undefined // 'undefined' typeof true // 'boolean' 复制代码
null
比较特殊
typeof null // 'object' 复制代码
判断复杂类型
const fn = () => {} typeof fn // 'function' let obj = {} typeof obj // 'object' let arr = [] typeof arr // 'object' let date = new Date() typeof date // 'object' let foo = Symbol('foo') typeof foo // 'symbol' 复制代码
总结:使用
typeof
可以精确地判断出除了null
之外的基本数据类型,以及function
类型symbol
类型。null
会被typeof
判断为object
。
instanceof 判断
a instanceof
B 判断的是 a 是否为 B 的实例 , 也就是 在 a 的原型链上是否存在 B 的构造函数。
function Person(name){ this.name = name } const a = new Person('小敏') console.log(a instanceof Person) // true console.log(a.__proto__.constructor === Person) // true console.log(a.__proto__.constructor === Person.prototype.constructor) // true console.log(Person.prototype.__proto__ === Object.prototype) // true console.log(a instanceof Object) // true console.log(a instanceof Person) // true 复制代码
我们发现在我们写一个函数的时候js
在内部会给这个函数添加一个prototype
的属性 如下代码
function Person(name){ this.name = name } console.log(Person.prototype) 并且这个prorotype 还有个 constructor的是属性指向函数本身。 复制代码
实现 instanceof
function instanceOfMock(L, R) { // 先对左边的进行类型判断 if(typeof L !== 'object') return false while(true) { // 找到了 Object.prototype.__proto__ (最顶部) if(L === null) return false if(R.prototype === L.__proto__) return true L = L.__proto__ } } // 记住一个 实例的 __proto__ === 构造函数的 prorotype (也就是new 的原理了) console.log(a.__proto__ === Person.prototype) console.log(instanceOfMock(a, String)) 复制代码
终极方法 Object.prorotype.toString.call()
作者:一咻
链接:https://juejin.cn/post/7038248485967101983
伪原创工具 SEO网站优化 https://www.237it.com/