前端开发核心知识进阶 读书笔记

656 阅读4分钟

01、this

02、闭包

调用栈

function fn1() {
  fn2()
}
function fn2() {
  fn3()
}
function fn3() {
  fn4()
}
function fn4() {
debugger
  console.log('fn4')
}

fn1()

对应可以使用上面的调试按钮,看一看函数出栈的过程,压栈和弹栈这个过程就非常清楚了。

image.png

闭包

比较通俗的理解:函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,这就形成了闭包。

如下代码匿名函数访问了外层函数的num变量,并且在全局作用域中可以访问到这个匿名函数

function numGenerator() {
  let num = 1
  return () => {
    console.log(num++)
  }
}

let getNum = numGenerator()

getNum()

所以numGenerator函数执行完毕以后,相关调用栈出栈以后,变量num不会消失,仍然有机会被外界访问到。

image.png

内存管理

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)
}

image.png 第二个

<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 = ''

image.png 所以还需要调用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

image.png

devtool 排查问题

可以看出这两者都是随着时间在一直上升的。 image.png

分析内存, 对占比比较大的进行分析。---

image.png

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 函数

pipecompose 函数 只是执行的顺序不同,如果用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的是属性指向函数本身。

image.png

实现 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()