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