前端八股文javascript

闭包

对闭包的理解:一个普通的函数function,如果它可以访问外层作用域的变量,那么这个函数就是一个闭包。
特点: 可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存再内存中,不会被垃圾回收机制回收。
使用场景: 防抖,节流,函数嵌套函数避免全局污染的时候

function outer(){
    const a = 10
    function f(){
        console.log(a)
    }
    f()
}
outer()

上面的变量a 和函数f 就是一个闭包。

// 这样可以实现数据私有,无法直接修改i
function fn(){
    let i = 0
    function count(){
        i++
        console.log(`函数被调用了${i}`)
    }
    return count
}
const result = fn()
result()   // 1次
result()   // 2次

闭包的内存泄漏

在上面的代码中:

result是一个全局变量,代码执行完毕不会立即销毁
result 使用fn函数
fn用到 count函数
count函数里面用到 i
i被引用就不会被回收,所以一直存在

此时:闭包引起了内存泄漏

前端的内存泄漏

JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。

1. 闭包引起的内存泄露
2. 意外的全局变量引起的内存泄露
3. 事件绑定未解绑引起的内存泄露
4. 定时器未清除引起的内存泄露
5. 循环引用引起的内存泄露:当两个或更多对象相互引用时,就会出现循环引用。如果其中一个对象被删除,它仍然被其他对象引用,这可能会导致内存泄漏

Vue中的内存泄漏

6. 未销毁的组件:当组件被销毁时,Vue会自动清理与该组件相关的内存。但是,如果组件没有被正确地销毁,那么与该组件相关的内存将不会被释放。
7. 未取消的异步操作:如果在Vue组件中使用异步操作,例如通过axios库发送HTTP请求,应该在组件销毁时取消这些操作。否则,这些操作将继续运行并占用内存。
8. 事件监听器:当组件被销毁时,应该手动移除与该组件相关的所有事件监听器。否则,这些事件监听器将继续占用内存并导致内存泄漏。
9. 循环引用:两个或多个对象之间相互引用。 

事件循环——eventloop

JS是一个单线程的脚本语言,同一时间只能做一件事。为了防止代码阻塞,把代码分为同步和异步。
同步代码:立即放入JS引擎(JS主线程)执行,并原地等待结果。比如打印语句
异步代码:先放入宿主环境(浏览器/Node),不必原地等待结果,并不阻塞主线程继续往下执行,异步结果在将来执行。比如setTimeout (一次性定时器)、setlnterval(定时器)、Ajax/Fetch、事件绑定等。
1.同步代码给js引擎执行,异步代码交给宿主环境
2.同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
3.执行栈执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行,这个过程是事件循环(eventloop)

在这里插入图片描述

宏任务和微任务

JS把异步任务分为宏任务和微任务。
宏任务 是由宿主(浏览器、Node)发起,微任务 JS引擎发起的任务。
Promise本身同步, then/catch的回调函数是异步的

代码可能有3种:

1.同步代码(js 执行栈/回调栈)
2.微任务的异步代码(js引擎)
	process.nextTick (node)
	Promise.then() catch()
	Async/Await
	object.observe等等
3.宏任务的异步代码(宿主环境)
	script(代码块)
	setTimeout / setlnterval定时
	setlmmediate定时器
	l/o
	ul render

宏任务、微任务-执行顺序
1.同步代码
2.微任务的异步代码( promise等)
3.宏任务的异步代码(settimeout、setinterval等

节流和防抖

防抖(避免事件重复触发,高频事件n秒内执行一次):就是指连续触发事件但是在设定的一段时间内中只执行最后一次。
当一个事件被触发,防抖会等待一段时间,如果在这段时间内没有更多事件触发,那么事件处理函数将被执行。比如:设定1s执行,当触发事件,它会在1s后执行,但是如果在0.5s时再次触发事件,那就会重新开始1s后再执行。
应用场景:1.搜索框搜索输入 2.文本编辑器实时保存
防抖实现:

let timerId = null
document.querySelector('input').oninput = function () {
    console.log('input')
    // 防抖
    clearTimeout(timerId)
    timerId = setTimeout(() => {
        console.log('我是防抖')
    }, 1000)
}

节流(把频繁触发的事件减少,每隔一段时间执行):指连续触发事件但是在设定的一段时间内中只执行一次函数。
当一个事件被触发,节流会立即执行事件处理函数,并在指定的时间间隔内忽略任何后续触发的相同事件。例如:设定1秒执行,那你在1秒触发在多次,也只在1秒后执行一次。
应用场景:1.高频事件例如快速点击、鼠标滑动、resize事件、scroll 事件;2.下拉加载;3.视频播放记录时间等。
节流实现:

let timerId = null
document.querySelector('.ipt ').onmouseover = function () {
    if (timerId !== null) {
        return
    }
    timerId = setTimeout(() => {
        console.log('我是节流')
        timerId = null
    }, 100)
}

原型和原型链

原型: 在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。原型可以放一些属性和方法,共享给实例对象使用;原型可以做继承。
作用:1.存放一些属性和方法 2.在JavaScript中实现继承
每一个javascript对象(除null外)创建的时候,都会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。

let obj = new Object()

_ proto _ :这是每个对象(除null外)都会有的属性,叫做 _ proto _,这个属性会指向该对象的原型。

let obj = Object()
console.log(obj.__proto__ === Object.prototype)  // true

原型链:对象都有_proto_属性,这个属性指向它的原型对象,原型对象也是对象,也有_proto_属性,指向原型对象的原型对象,这样一层一层形成的链式结构称为原型链,最顶层找不到则返回null。

在这里插入图片描述

new操作符

new操作符具体做了什么?

1.创建一个空对象
2.绑定原型:新创建的对象会继承构造函数的原型对象上的方法和属性。
3.把构造函数的this绑定到新的空对象身上
4.根据构建函数返回的类型判断,如果是值类型,则返回对象,如果是引用类型,就要返回这个引用类型 

JS如何实现继承

  1. 原型链继承:原型链继承是指将父类实例作为子类的原型。这种方式下,子类实例可以共享父类实例的属性和方法,但是无法向父类构造函数传递参数。
  2. 借用构造函数继承:借用构造函数继承是指在子类构造函数中调用父类构造函数,并使用 call 或 apply 方法将父类的 this 指向子类实例。这种方式的缺点是无法继承父类原型上的方法。
  3. 组合式继承:组合继承是指将原型链继承和借用构造函数继承结合起来。这种方式可以继承父类实例和原型上的属性和方法,但是会调用两次父类构造函数,且父类原型上的属性和方法会被继承两次。
  4. ES6的class类继承 : ES6 中,可以使用 class 和 extends 关键字来实现继承。即定一个父类(也称为基类)和一个子类(也称为派生类),并通过 extends 关键字让子类继承父类的属性和方法。

JS中this指向问题

1. 全局对象中的this指向
    指向的是window
2. 全局作用域或者普通函数中的this
    指向全局window
3. this永远指向最后调用它的那个对象
     在不是箭头函数的情况下
4. new 关键词改变了this的指向
5. apply,call,bind
     可以改变this指向,不是箭头函数
6. 箭头函数中的this 
     它的指向在定义的时候就已经确定了
     箭头函数它没有this,看外层是否有函数,有就是外层函数的this,没有就是window
7. 匿名函数中的this
     永远指向了window,匿名函数的执行环境具有全局性,因此this指向window

call、apply、bind的区别

三者都是用来改变函数的this对象的指向的。
call方法可以调用函数,传的是一个参数列表
apply可以调用函数,传递的是一个数组
bind不可以调用函数,会返回一个改变了this指向的函数,传参方式和call相同

let dog = {
    name: '小白',
    sayName() {
        console.log('我是' + this.name)
    },
    eat(food1, food2) {
        console.log('我爱吃' + food1 + '和' + food2)
    }
}
let cat = {name: '小花'}

function fn() {
    console.log(this.name)
}

// call可以调用函数,call可以改变函数中的this
fn.call(cat)    // 小花
dog.sayName.call(cat)   // 我是小花
dog.eat.call(cat, '鱼', '猫条')   // 我爱吃鱼和猫条
dog.eat.apply(cat, ['鱼', '猫条'])    // 我爱吃鱼和猫条
let fun = dog.eat.bind(cat, '鱼', '猫条')
fun()   // 我爱吃鱼和猫条

setTimeout最小执行时间

HTML5规定的内容:
setTimeout最小执行时间是4ms
setInterval最小执行时间是10ms

深拷贝和浅拷贝

浅拷贝:对于基本类型数据来说,拷贝的是值;对于引用数据来说,拷贝了地址,因此拷贝后的对象和原对象会共用一个内存地址,因此,属性值会同步变化。
深拷贝:对于引用数据类型来说,就是拷贝原始对象的所有属性与方法,在内存中重新开辟一块内存空间进行存储。

基本数据类型:
			Number  数值型
			String  字符串型
			Boolean  布尔型
			Symbol  
			BigInt
			Null
			Underfined
引用数据类型:
			对象(Object)
			数组(Array)
			函数(Function)

如何实现一个深拷贝

1.使用第三方库lodash中的cloneDeep()方法
2JSON.parse(JSON.stringify(obj)):无法处理函数和循环引用等情况
3,利用递归函数实现
递归实现深拷贝

const personA = {
    name: '小红',
    age: 18,
    school: 'SWPU',
    friend: {
        name: '小白',
    }
}

function deepClone(obj) {
    // 使用typeof,数组、对象、null都会被判断为object
    if (typeof obj !== 'object' || obj === null) {
        return obj
    }
    let result
    // instanceof可以正确判断对象的类型
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepClone(obj[key])
        }
    }
    return result
}

const personB = deepClone(personA)
personB.name = '果子'
personB.friend.name = '胡子'
console.log(personA)   // { name: '小红', age: 18, school: 'SWPU', friend: { name: '小白' } }
console.log(personB)   // { name: '果子', age: 18, school: 'SWPU', friend: { name: '胡子' } }

事件委托

事件委托:也叫事件代理,原理就是利用了事件冒泡【事件是在冒泡阶段触发】的机制来实现,也就是说把子元素的事件绑定到了父元素的身上。
如果了元素阻止了事件冒泡,那么委托也就不成立。
阻止事件冒泡: event.stopPropagation()
addEventListener(‘click’,函数名,true/false) 默认是false(事件冒泡),true(事件捕获)
好处: 提高性能,减少事件的绑定,也就减少了内存的占用。

Ajax是什么

Ajax:是一种创建交互式网页应用的网页开发技术,可以在不重新加载整个网页的情况下,与服务器交换数据,并且更新部分网页。
实现过程

1. 创建 Ajax的核心对象, XMLHttpRequest对象
2. 通过 XMLHttpRequest 对象的 open() 方法与服务端建立连接
3. 构建请求所需的数据内容,并通过XMLHttpRequest 对象的 send() 方法发送给服务器端
4. 通过 XMLHttpRequest 对象提供的 onreadystatechange 事件监听服务器端你的通信状态
5. 接受并处理服务端向客户端响应的数据结果
6. 将处理结果更新到 HTML页面中

Promise

Promise对象,封装了一个异步操作并日还可以获取成功或失败的结果。
Promise主要就是解决回调地狱的问题,之前如果异步任务比较多,同时他们之间有相互依赖的关系,就只能使用回调函数处理,这样就容易形成回调地狱,代码的可读性差,可维护性也很差。

async和await

js的三种本地存储方式

  1. sessionStorage
  2. localStorage
  3. cookie

什么是json

JSON是一种纯字符串形式的数据,它本身不提供任何方法,适合在网络中进行传输。
JSON数据存储在 .json文件中,也可以将 JSON 数据以字符串的形式存储在数据库、Cookie、Session 中。
检索和解析 JSON 数据,Js提供了JSON.parse()JSON,stringify()
什么时候使用jison: 定义接口;序列化;生成token;配置文件package.json

  • 33
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值