JS知识点汇总

jS中几种常见的类型判断方法****

  • 基本数据类型:string,number,boolean,null,undefinded,bigint,symbol
  • 引用数据类型:对象,函数,数组,日期
  1. Typeof 判断原始数据类型
  2. Instanceof 来判断引用数据类型
  3. Object.prototype.toString.call(this) 判断所有类型
  4. 判断数组 Array.isArray()

==和===

==(宽松相等):会在比较两个操作数时执行 类型转换尝试将两者转换为相同类型后再比较。 ===(严格相等):不会执行类型转换,仅在类型和值完全相同的情况下返回 true。 推荐使用 ===:因为它更严格、更符合预期,能避免潜在的错误。尤其是在需要精确判断值和类型时。

深拷贝和浅拷贝区别

浅拷贝,只拷贝了最外面的一层 ,只相当赋值给了 新的,新的改值,旧的值不会改变 * 但是如果对象里面包对象,那么也只能拷贝最外面的一层,新的改值,旧的值也会改变!!!因为拷贝的还是地址,而不是对象****

  • 深拷贝和浅拷贝只针对引用类型
  • 浅拷贝:拷贝的是地址
  • 深拷贝:拷贝的是对象,不是地址

数组实现浅拷贝:concat 【...arr】 slice()

对象实现浅拷贝:Object.assgin / ...展开运算 {。。。Obj}

深拷贝:Stringify lodash/clonedeep 递归实现深拷贝

变量提升

变量提升是 JavaScript 的一种机制,在该机制下,变量和函数的声明会被提升到当前作用域的顶部。这意味着,无论变量和函数在代码中实际声明的位置如何,它们都可以在声明之前被访问。

函数声明提升

函数声明会被完整提升,这意味着在函数声明之前就可以调用该函数。

scss 复制代码
// 可以在函数声明之前调用函数
sayHello();

function sayHello() {
    console.log('Hello!');
}
变量声明提升

使用 var 声明的变量会被提升,但不会赋值,在变量声明之前访问该变量,其值为 undefined

ini 复制代码
// 变量提升,输出 undefined
console.log(message); 
var message = 'Hello, world!';

暂时性死区

暂时性死区是 ES6 引入 letconst 后出现的概念。使用 letconst 声明的变量不会像 var 那样被提升到作用域顶部并赋值为 undefined,而是存在于暂时性死区内。在变量声明之前访问这些变量会导致 ReferenceError

ini 复制代码
/ 报错:ReferenceError
console.log(myVariable); 
let myVariable = 'Hello';

在上面的代码中,myVariable 从块级作用域开始到它被声明之前都处于暂时性死区内。只有在声明之后,变量才能被正常访问

两者的区别

  • 变量提升 :使用 var 声明的变量和函数声明会被提升到作用域顶部,函数可以在声明前调用,变量在声明前值为 undefined
  • 暂时性死区 :使用 letconst 声明的变量不会被赋值为 undefined,在声明之前访问会导致 ReferenceError

var、let、const区别

var没有块级作⽤域,只有函数作⽤域。var只有在function{ }内部才有作⽤域的概念,其他地⽅没有。意味着函数以外⽤var定义的变量是同⼀个,我们所有的修改都是针对他的

  1. let和const增加块级作⽤域(JS没有块级作⽤域)

  2. let和const存在暂时性死区,不存在变量提升,不能在初始化前引⽤,调⽤ 返回 uninitialized

  3. let和const禁⽌重复声明,不能重新声明

  4. let和const不会成为全局对象属性,var声明的变量⾃动成为全局对象属性

  5. var 存在变量提升(执⾏前,编译器对代码预编译,当前作⽤域的变量/函数提升到作⽤域顶部),let约束变量提升。let和var都发⽣了变量提升,只是es6进⾏了约束,在我们看来,就像let禁⽌了变量提升

  6. 使⽤var,我们能对变量多次声明,后⾯声明的变量会覆盖前⾯的声明

WeakMap与Map的区别是什么

1. 什么是WeakMap

WeakMap 是 JavaScript 中的一种集合类型,它存储键值对,且键必须是对象,并且键是弱引用的。这意味着,如果键对象没有其他引用,它会被垃圾回收器回收,对应的键值对也会被自动删除。

2. 与Map的区别

键的类型

  • Map:键可以是任意类型,包括基本数据类型(像字符串、数字等)和引用类型(如对象、函数)。
  • WeakMap:键只能是对象,不能使用基本数据类型作为键。

垃圾回收机制

  • Map :对键所引用的对象是强引用。只要 Map 存在,键引用的对象就不会被垃圾回收,即便其他地方无该对象的引用。
  • WeakMap :对键所引用的对象是弱引用。若对象没有其他强引用,垃圾回收时对象会被回收,WeakMap 里对应的键值对也会自动移除。

可遍历性

  • Map :是可迭代的,能使用 for...of 循环、forEach 方法等遍历其键值对。
  • WeakMap :不可迭代,没有 keys()values()entries() 这些迭代方法,也不能用 for...offorEach 遍历。

方法和属性

  • Map :有 size 属性来获取键值对数量,还有 set()get()has()delete()clear() 等方法。
  • WeakMap :只有 set()get()has()delete() 方法,没有 size 属性和 clear() 方法。

使用场景

  • Map:适用于需存储任意类型键值对,且要对这些键值对进行遍历和操作的场景,如缓存数据。
  • WeakMap :常用于避免内存泄漏的场景,例如给对象添加私有数据,当对象被销毁时,WeakMap 里相关数据也会被清理。

This指向

// this指向的情况

  1. 函数调用模式 fn(),this 指向 window(默认绑定)

  2. 方法调用模式 obj.fn(),this 指向调用者(隐式绑定),虽然没有刻意的绑定,但是在执行时会自动将函数的 this 指向该对象。

  3. 上下文调用模式:想指向谁就指向谁(显式绑定/硬绑定)

// call, apply, bind

fn.call(this指向的内容, 参数1, 参数2, ...)

fn.apply(this指向的内容, [参数1, 参数2, ...])

const newFn = fn.bind(this指向的内容)

call apply bind 总结

  • 相同点:都可以改变函数内部的this指向,
  • 区别点:
  1. call 和 apply 会调用函数,并且改变函数内部this指向,
  2. call 和 apply 传递的参数不一样,call 传递参数 aru1, aru2..形式
  3. apply 必须数组形式[arg]bind 不会调用函数,可以改变函数内部this指向
  • 主要应用场景:
  1. call 调用函数并且可以传递参数
  2. apply 经常跟数组有关系.比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变this指向.比如改变定时器内部的this指向

闭包

闭包:内层函数+外层函数的变量

闭包的核心特性:

  • 访问外部函数作用域的变量
  • 即使外部函数执行结束,变量依然被保留
  • 不会被垃圾回收,直到闭包不再被引用

闭包的应用场景:

  • 私有变量(模拟封装)
js 复制代码
function createcount(){
let count=0
return{
add:()=>++count }
}

const count=createcount()
count.add()  // 1
count.count  undefinded  外部无法直接访问
  • 回调 & 事件监听
  • 定时器 & 异步操作

闭包的缺点:

  • 可能导致内存泄漏:
  • 闭包会持有外部变量的引用,导致变量无法被垃圾回收
  • 解决方案:手动将变量置为 null 或谨慎管理作用域

内存泄露

内存泄露是指在程序运行过程中,程序未能释放不再使用的内存空间,导致内存资源被浪费

JS 内存泄露的常见原因

l 意外的全局变量:忘记使用 var,let,const 声明变量时,变量会被挂载到全局对象上

l 闭包:闭包中引用了不再需要的外部变量,导致这些变量无法被垃圾回收

l 未清理的 DOM 引用:删除 DOM 元素时,未能清理相关的事件监听器或引用

l 定时器和回调:未能清理不再需要的 setInterval 或 setTimeout 回调

JS 作用域和作用域链

作用域:变量的可访问范围,分为 全局作用域、函数作用域、块级作用域。

作用域链:变量查找机制,从当前作用域 逐级向上查找,直到全局作用域或 ReferenceError。

原型和原型链

  1. 原型(Prototype)

每个 函数(构造函数)都有一个 prototype 属性,指向其 原型对象。

每个 对象 都有一个 proto 指向其构造函数的 prototype,形成继承关系。 2. 原型链(Prototype Chain)

查找⼀个属性先在⾃身查找,如果找不到,就沿着 proto 属性在原型对象上进⾏查找,如果还找不到,就沿着原型对象的 proto 属性进⾏查找,直到查找到直到找到Object的原型对象,如果还没有找到就会返回undefined

Object.prototype.proto === null,原型链的顶端是 是 Object.prototype.proto

  • 使⽤ hasOwnProperty() ⽅法来判断属性是否属于原型链的属性:

每个实例对象都有私有属性(proto)指向它构造函数的原型对象。

每个构造函数都有prototype原型对象

prototype原型对象的constructor指向构造函数本身

有默认constructor属性,记录实例由哪个构造函数创建

ES6新特性

  1. const、let

  2. 模板字符串

  3. 箭头函数

  4. 函数参数默认值

  5. 解构赋值

  6. for...of ⽤于数组,for...in⽤于对象

  7. Promise

  8. 展开运算符(...)

  9. 对象字⾯量、class(原型链的语法糖)

箭头函数和普通函数区别

  • 1.语法更加简洁,没有function =》
  • 2.没有动态参数,但是有剩余参数
  • 3.没有自己的this,会继承外层的this
  • 4.不支持bind/apply/call
  • 5.不能用作构造函数,没有prototype属性

js数组⽅法

  • forEach map
  • push pop shift unshift
  • splice slice concat join
  • sort reverse some every filter
js 复制代码
### 改变原数组的方法

-   `push`:把一个或多个元素添加到数组末尾,并且返回新的数组长度。
-   `pop`:移除数组的最后一个元素,同时返回该元素。
-   `shift`:移除数组的第一个元素,然后返回该元素。
-   `unshift`:在数组开头添加一个或多个元素,并且返回新的数组长度。
-   `splice`:从数组中添加或删除元素,然后返回被删除的元素组成的数组。
-   `sort`:对数组的元素进行排序,并且返回排序后的数组。
-   `reverse`:颠倒数组中元素的顺序,并且返回颠倒后的数组。
js 复制代码
`map`、`slice`、`concat`、`join`、`some`、`every`、`filter` 这些方法不会改变原数组  会返回新数组
- `map`:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
- `slice`:返回一个新的数组对象,这一对象是一个由 `begin` 和 `end` 决定的原数组的浅拷贝(包括 `begin`,不包括 `end`)。
`concat`:用于合并两个或多个数组,此方法不会更改现有数组,而是返回一个新数组
`join`:将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串
- `some`:测试数组中是不是至少有 1 个元素通过了被提供的函数测试,它返回的是一个布尔值。
`every`:测试一个数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值
- `filter`:创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

防抖节流

  • 防抖:单位时间内,频繁触发事件,只执行最后一次
  • 使用场景:手机号,邮箱输入检测,用户最后一次输入完,再检测
  • 节流:单位时间内,频繁触发事件,只执行一次
  • 使用场景:鼠标移动,滚动条滚动
  • 手撕:
  1. 防抖函数:声明定时器变量,判断是否有定时器,如果有,清除定时器,重新声明定时器,执行函数
  2. 节流函数:声明定时器变量为空,判断如果没有定时器,声明定时器,执行函数,定时器清空
  3. Lodash库的 防抖和节流函数 _.debounce _.throttle

如何理解 JS 单线程?

  • js是一门单线程的语言。因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。
  • 渲染主线程要执行很多任务,解析html,执行js等。
  • 如果主线程采用同步的方式执行代码,可能会阻塞主线程的执行,导致消息队列中的其他任务无法执行。会浪费时间,以及导致页面无法及时更新卡死。
  • 所以采用异步的方式去解决。当遇到一些异步的任务,渲染主线程将这些任务交给其他线程去处理,自身立即结束执行任务,执行后续代码。当其他线程完成,将事先传递的回调函数包装成任务,放入消息队列的末尾,等待主线程的调度执行。
  • 在这种异步模式下,浏览器就不会阻塞,单线程也可以流畅运行

js的异步理解

  • js是一门单线程的语言。因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。
  • 渲染主线程要执行很多任务,解析html,执行js等。
  • 如果主线程采用同步的方式执行代码,可能会阻塞主线程的执行,导致消息队列中的其他任务无法执行。会浪费时间,以及导致页面无法及时更新卡死。
  • 所以采用异步的方式去解决。当遇到一些异步的任务,渲染主线程将这些任务交给其他线程去处理,自身立即结束执行任务,执行后续代码。当其他线程完成,将事先传递的回调函数包装成任务,放入消息队列的末尾,等待主线程的调度执行。
  • 在这种异步模式下,浏览器就不会阻塞,单线程也可以流畅运行。

事件循环

事件循环又称之为消息循环,是浏览器渲染主线程的工作方式。 在chrome的源码中,它开启一个死循环,每次循环从消息队列中取出第一个任务执行,其他线程只需要在合适的时候把任务放入消息队列的末尾即可。 过去就是微任务队列和宏任务队列。目前的浏览器情况更加复杂,灵活的处理。 最新的W3c解释如下:

  • 每个任务都有一种任务类型,相同类型的任务必须放在一个队列中,不同类型的任务放在不同的队列中。
  • 不同的任务队列有不同的优先级
  • 在一次事件循环中,浏览器可根据实际情况选取队列执行任务。
  • 但是浏览器必须准备好一个微任务队列,微任务队列中的任务优先其他所有任务执行。必须优先调度。

最开始的时候,渲染主线程会开启无限循环。

  • 每一次循环,都会判断消息队列中是否有任务存在,如果有,就执行取出第一个任务执行。执行完以后,进入下一次循环,没有的话,就会进入休眠状态。
  • 其他所有线程可以随时向消息队列里面添加任务,新任务会加入消息队列的末尾。如果是休眠状态,会唤醒主线程,继续循环执行任务。 整个过程,就是事件循环(消息循环)

事件循环机制

宏任务主要包括:setTimeout、setInterval

微任务主要包括:promise、process.nextTick()

执⾏规则:同步代码直接进⼊主线程执⾏,JS引擎判断主线程是否为空,如果为空,则读取 微任务Event

Queue 中所有的消息,并依次执⾏。主线程和微任务 Event Queue 都为空后,读取 宏任务Event Queue 中的第⼀个消息进⼊主线程执⾏,来回微宏。

async/await

async/await 是 ES2017(ES8)引入的 基于 Promise 的语法糖,用于更清晰地编写异步代码,使其看起来像同步代码,提高可读性。

async 关键字:用于声明一个异步函数,返回值始终是 Promise。

await 关键字:只能在 async 函数中使用,等待 Promise 解析(resolve)并返回结果,而不会阻塞线程。

Promise

  • Promise 对象是异步编程的⼀种解决⽅案。Promise 是⼀个构造函数,接收⼀个函数作为参数,返回⼀个 Promise 实例。
  • ⼀个 Promise 实例有三种状态,分别是pending、fulfilled 和 rejected。
  • 实例的状态只能由 pending 转变 fulfilled 或者 rejected 状态,并且状态⼀经改变,⽆法再被改变了。
  • 状态的改变是通过传⼊的 resolve() 和 reject() 函数来实现的,当我们调⽤resolve回调函数时,会执⾏Promise对象的then⽅法传⼊的第⼀个回调函数,当我们调⽤reject回调函数时,会执⾏Promise对象的then⽅法传⼊的第⼆个回调函数,或者catch⽅法传⼊的回调函数。

Promise的实例有两个过程:

pending -> fulfilled : Resolved(已完成)

pending -> rejected:Rejected(已拒绝)

⼀旦从进⾏状态变成为其他状态就永远不能更改状态了。

在通过new创建Promise对象时,我们需要传⼊⼀个回调函数,我们称之为executor

✓ 这个回调函数会被⽴即执⾏,并且给传⼊另外两个回调函数resolve、reject;

✓ 当我们调⽤resolve回调函数时,会执⾏Promise对象的then⽅法传⼊的回调函数;

✓ 当我们调⽤reject回调函数时,会执⾏Promise对象的catch⽅法传⼊的回调函数;

resolve的三种状态

情况⼀:如果resolve传⼊⼀个普通的值或者对象,那么这个值会作为then回调的参数;

情况⼆:如果resolve中传⼊的是另外⼀个Promise,那么这个新Promise会决定原Promise的状态:

情况三:如果resolve中传⼊的是⼀个对象,并且这个对象有实现then⽅法,那么会执⾏该then⽅法,并且根据then⽅法的结 果来决定Promise的状态:

then⽅法接受两个参数:

fulfilled的回调函数:当状态变成fulfilled时会回调的函数;

reject的回调函数:当状态变成reject时会回调的函数;

Promise 串行执行

多个异步操作依次执行(避免回调地狱)

js 复制代码
function step1() {
  return new Promise((resolve) => setTimeout(() => resolve('Step 1 完成'), 1000))
}
function step2() {
  return new Promise((resolve) => setTimeout(() => resolve('Step 2 完成'), 1000))
}

step1()
  .then((result) => {
    console.log(result)
    return step2() // 返回 Promise
  })
  .then((result) => console.log(result))
  .catch((error) => console.error('错误:', error))
  
  输出:
  Step 1 完成
  Step 2 完成

Promise 并行执行

多个异步任务同时执行,全部完成后再处理

typescript 复制代码
const p1 = new Promise((resolve) => setTimeout(() => resolve('任务 1'), 1000))
const p2 = new Promise((resolve) => setTimeout(() => resolve('任务 2'), 1500))

Promise.all([p1, p2])
  .then((results) => console.log('所有任务完成:', results))
  .catch((error) => console.error('任务失败:', error))

如果只要最快完成的结果

typescript 复制代码
Promise.race([p1, p2])
  .then((result) => console.log('最先完成的:', result))
  .catch((error) => console.error('失败:', error))

总结

Promise 解决异步回调问题,提供 .then().catch().finally() 处理状态变化。支持 Promise.all() 并行执行,Promise.race() 竞争执行。用 async/await 可以让异步代码更清晰。

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax