前端八股文笔记【三】
JavaScript
基础题型
1.JS的基本数据类型有哪些
基本数据类型:String,Number,Boolean,Nndefined,NULL,Symbol,Bigint
引用数据类型:object
NaN是一个数值类型,但不是一个具体的数字
Symbol 是一种不可变且唯一的原始数据类型,它可以用作对象的键,以创建私有属性或在某些情况下避免命名冲突。每个 Symbol 值都是唯一的,即使它们具有相同的描述。
let sym1 = Symbol('mySymbol');
let sym2 = Symbol('mySymbol'); console.log(sym1 === sym2); // false,因为每个Symbol都是唯一的 let obj = {};
obj[sym1] = 'Hello';
console.log(obj[sym1]); // "Hello"
console.log(obj[sym2]); // undefined,因为sym1和sym2是不同的Symbol
Bigint 是一种新的数据类型,用于当整数值大于 Number 数据类型支持的范围时。这种数据类型允许我们 安全地对 大整数 执行算术操作,表示高分辨率的时间戳,使用大整数 id ,等等,而不需要使用库。
console.log(999999999999999); //=>10000000000000000
9007199254740992 === 9007199254740993; // → true 居然是true!//创建方式
//1.在数字末尾追加 n 即可
console.log( 9007199254740995n ); // → 9007199254740995n
console.log( 9007199254740995 ); // → 9007199254740996//2. BigInt() 构造函数
BigInt("9007199254740995"); // → 9007199254740995n
注意:
1.Symbol 和 Bigint 都是原始数据类型,但它们都是对象包装器类型(Symbol 和 Bigint )的实例。当你使用 typof操作符时,Symbol 会返回
'Symbol',而 Bigint 会返回'Object'2. Bigint 和普通的数字(Number)类型是不同,并且它们之间的算术运算需要显式转换
Object 在JS中Array,Founction,Map,Set等都是对象(Object)的一种,具有自己的属性和方法,但同时它们的原型对象也都继承自原型链顶点的
Object.prototype
2.null和undefined的区别
1.作者在设计JS时先设计的null(在设计时借鉴了Java)
2.null会被隐式的转换成0,很不容易发现错误。
3.后设计undefined,是为了填之前的坑
具体区别:
null是一个表示“无”的对象(空指针对象),转化数值时为0;
undefined表示一个“无”的原始值,转为数值时为NaN
3.==和===的区别
== 相等运算符:如果两边的数据类型不同,它会先尝试进行类型转换,再进行比较
=== 严格相等运算符或恒等运算符:不会进行类型转换,它会先比较两边的数据类型是否相同,如果数据类型不同,则直接返回false,如果相同在比较值是否相等
console.log(5 == '5') //true
console.log(5 === '5') //false
4.for...in和for...of的区别
都是用来遍历对象(Object)的
for...in 循环:
遍历对象的可枚举属性(包括对象原型链上继承的,除非用 Object.prototype.hasOwnProperty() 来过滤)。对数组来说也就是其索引
let obj = {0: 'a',1: 'b',2: 'c'
}
for (const variable in obj) {console.log(variable) //输出0 1 2
}
for...of 循环:
遍历可迭代对象的值。如 Array、Map、Set、arguments 等
let iterable= ['a', 'b', 'c']
for (const value of iterable) {console.log(value) //输出a b c
}
5 .setTimeout 和 setInterval
- setTimeout 接收一个回调函数和一个时间参数,创建一个滞后执行的任务,并返回一个Id,可通过clearTimeout取消任务
- setInterval 接收一个回调函数和一个时间参数,创建一个周期执行的任务,并返回一个Id,可通过clearInterval取消任务
let intervalId = setInterval(() => {console.log('每隔2s执行一次')
}, 2000)let timeotId = setTimeout(() => {clearInterval(intervalId) //根据id取消setIntervalconsole.log('滞后8s,根据id取消setInterval')
}, 8000)
// clearTimeout(timeotId) //取消定时器
JavaScript数组
1.JS数组常用方法及其区别
推荐参考 JavaScript数组的常用函数
2.判断变量是不是数组
1.isArray
- 注意:isArray() 方法并不是原型Array.prototype上的方法,它属于构造函数本身。这意味着不能通过数组实例来调用,,而是,应该通过 Array 构造函数来调用它。
const testArray= []
console.log(Array.isArray(testArray)) //true
2.constructor
-
原型Array.prototype上的constructor,它指向构造函数
const testArray= []
console.log(testArray.constructor === Array) //true
3.isPrototypeOf
- 原型object.prototype上的方法isPrototypeOf(object):检查一个对象是否存在于另一个对象的原型链中
const testArray= []
console.log(Array.prototype.isPrototypeOf(testArray)) //true
4.toString
- 使用 Object.prototype.toString.call(testArray) 来获取 testArray 的类型字符串
- 接着使用 indexOf('Array') 来检查该字符串是否包含 'Array'
- 如果包含,则 indexOf 返回的值会大于 -1,条件为真,输出 true
- 如果不包含,则 indexOf 返回 -1,条件为假,输出 false
const testArray = []
console.log(Object.prototype.toString.call(testArray).indexOf('Array') > -1 //true
)
5.注意:typeof()
const testArray = []
console.log(typeof testArray) //object
console.log(typeof Array) //Founction
JavaScript对象
1.对象的常用方法
推荐参考 JavaScript对象
2.new操作符具体做了什么
1.创建一个空对象:并赋予其一个特殊内部属性__proto__( 或[[ptototype]] )
function Person() {}
console.log(typeof new Person())//Object
2.设置原型链:将空对象的原型(__proto__)指向构造函数的原型(prototype),以便新对象能够访问构造函数原型上定义的属性方法
function Person() {}
console.log(Person.prototype === new Person().__proto__) //true
3.调用构造函数:将空对象作为构造函数this的上下文(即构造函数内的this指向这个新对象)
function Person(name) {this.name = name
}
console.log(new Person('张三').name)//张三
4.返回新对象:对构造函数的返回值处理判断,如果构造函数没有返回或返回基本数据类型,则返回步骤1中创建的那个对象。如果构造函数返回引用数据类型,则 new 表达式的结果就是这个被返回的Object
function Person(name) {this.name = namereturn { name: '李四' }
}
console.log(new Person('张三').name) //李四
使用自定义函数模拟new操作符行为
function Person(name, age) {this.name = namethis.age = age
}
function create(fn, ...args) {// 1.创建一个空对象var obj = {}// 2.将空对象的原型指向构造函数的原型Object.setPrototypeOf(obj, fn.prototype)// 3.改变this指向// args剩余参数数组var result = fn.apply(obj, args)//4.对构造函数返回值处理判断// 检查 result 是否为 Object 的实例return result instanceof Object ? result : obj
}
//简单检查两种方式创建的对象内容是否一样
console.log(JSON.stringify(create(Person, '张三', 18)) ===JSON.stringify(new Person('张三', 18))
) //true
JavaScript函数
1.推荐参考
JavaScript函数
2.JS继承的方式有哪些
1.ES6的class和extend关键字
清晰,易于理解。更接近传统面向对象编程语言的语法。但实际上,class的底层仍然是基于ES5的原型继承机制。
class Parent {constructor(name) {this.name = name}sayName() {console.log(`hello! my name is ${this.name}`)}
}
class Child extends Parent {constructor(name, age) {super(name) // 调用父类的构造函数this.age = age}sayAge() {console.log(`I am ${this.age} years old`)}//可覆盖父类方法
}
// 实例化
const childInstance = new Child('张三', 18)
//子类原型上的方法
childInstance.sayAge() //I am 18 years old
//父类原型上的方法
childInstance.sayName() //hello! my name is 张三
特点:
-
通过class关键字定义类。在类中,可以定义构造函数(使用constructor方法)、实例属性、实例方法、静态属性和静态方法(static关键字声明,静态方法属于类本身,不属于实例)
- 可以封装对象的属性和方法,使得外部不能直接访问或修改对象的内部状态,只能通过类提供的方法来进行交互
-
通过extends关键字实现继承。相比ES6以前的继承机制,简化了继承的语法,使得继承变得更加直观和简单。
-
在子类的构造函数中,必须首先调用
super()方法,它表示父类的构造函数。这是因为子类在实例化时会先创建自身的this,然后继承父类的属性和方法。如果不调用super(),子类就得不到this对象,从而导致错误。
2.原型链继承
主要是通过将子类的原型设置为父类的一个实例对象,来继承父类的属性和方法。
function Parent() {this.name = '张三' //name无法随子类实例需求而变化
}
//父类原型方法
Parent.prototype.sayName = function () {console.log(`hello! my name is ${this.name}`)
}
function Child(name, age) {this.age = age
}
// 设置Child的原型为Person的一个实例
Child.prototype = new Parent()
//子类原型方法
Child.prototype.sayAge = function () {console.log(`I am ${this.age} years old`)
}
// 实例化
const childInstance = new Child('张三', 18)
//子类原型上的方法
childInstance.sayAge() //I am 18 years old
//父类原型上的方法
childInstance.sayName() //hello! my name is 张三
特点:
- 使用Child.prototype = new Parent(),会创建一个新的Parent实例,并执行Parent构造函数中的所有代码。Parent()中定义的任何绑定到this的属性都将存在于Child.prototype,其通过这种方式来实现Parent()内实例属性的继承。
- 在使用这种继承方式时,无法直接通过子类的构造函数向父类构造函数传递参数。这限制了父类构造函数根据子类实例的不同需求进行初始化的能力。
3.借用构造函数
主要是通过在子类的构造函数中使用call()或apply()方法调用父类的构造函数,并将this作为第一个参数传递给父构造函数,从而实现子类继承父类的属性和方法。
function Parent(name) {this.name = '张三' //name无法随子类实例需求而变化//父类实例方法this.sayName = function () {console.log(`hello! my name is ${this.name}`)}
}
//父类原型方法
Parent.prototype.testSayName = function () {console.log(`hello! my name is ${this.name}`)
}
function Child(name, age) {Parent.call(this, name) // 借用构造函数继承Parentthis.age = age
}
//子类原型方法
Child.prototype.sayAge = function () {console.log(`I am ${this.age} years old`)
}
// 实例化
const childInstance = new Child('张三', 18)
//子类原型上的方法
childInstance.sayAge() //I am 18 years old
//父类实例方法
childInstance.sayName() //hello! my name is 张三
// 父类原型上的方法
childInstance.testSayName() //TypeError: childInstance.sayName is not a function
特点:
- 使用Parent.call(this, name)可以通过子类的构造函数向父类构造函数传递参数,增强了父类构造函数随子类需求初始化的能力
- 无法继承父类原型上的方法,只能继承父类构造函数Parent()内定义的实例属性和方法
- 父类Parent()内定义的实例方法会在每个实例内重复创建。因为该继承承是通过在子类的构造函数中调用父类的构造函数来实现的,所以创建的多个子类实例,虽然功能可能相同,但实例内存的方法却不同(内存地址不同,浪费空间)
4.组合式继承
结合了原型链继承和借用构造函数继承的继承模式。它解决了,原型链继承父类初始化能力有限制,而借用构造函数继承会导致方法在每个实例上重复创建的问题。它主要通过使用借用构造函数继承属性,使用原型链继承方法来实现
function Parent(name) {this.name = '张三' //name无法随子类实例需求而变化
}
//父类原型方法
Parent.prototype.SayName = function () {console.log(`hello! my name is ${this.name}`)
}
function Child(name, age) {Parent.call(this, name) // 借用构造函数继承Parentthis.age = age
}
//指定一个Parent.prototype作为Child.prototype的原型
Child.prototype = Object.create(Parent.prototype)
//子类原型方法
Child.prototype.sayAge = function () {console.log(`I am ${this.age} years old`)
}
// 实例化
const childInstance = new Child('张三', 18)
//子类原型上的方法
childInstance.sayAge() //I am 18 years old
//父类原型上的方法
childInstance.sayName() //hello! my name is 张三
特点:
- 用原型链来继承父类原型上的方法,用借用构造函数来继承父类属性。
- 用 Child.prototype = Object.create(Parent.prototype)代替Child.prototype = new Parent(),创建一个新的空对象,其原型设置为Parent.prototype,并让Child.prototype指向这个对象。这并不会创建父类的实例,也不会执行Parent()内的代码,Child.prototype上也就不会存在Parent()内定义的实例属性。
- 在子类中调用父类构造函数,依然会在每个子类实例中都创建父类的实例属性和方法,但属性通常会被子类实例初始化的属性覆盖。而方法,只要将其定义在父类原型上,便不会在子类实例中重复创建。也就不会造成内存浪费。
5.其他继承
除了上述4种继承方式,还有寄生组合式继承,寄生式继承,混合继承等其他继承方式,此处不再一一列举。
其他题型
1.var,let和const
声明提升
- 声明提升(包括变量声明和函数声明): JS中,代码执行时,浏览器或会首先处理所有的声明,并将它们提升到它们所在作用域(全局作用域或函数作用域)的顶部。这个过程就是声明提升。
- 变量提升(特指变量声明的提升): 在变量提升中,只有变量的声明部分被提升,而变量的赋值不会提升。这意味着,如果你在声明变量之前就尝试访问它,你将得到一个undefined的值,因为此时变量已经被声明,但还没有被赋值。
- 需要注意,虽然函数声明也会被提升,但与变量提升不同的是,函数声明的整个定义(包括函数体和变量名)都会被提升到作用域顶部,而不仅仅是函数的声明部分。这意味着你可以在函数声明之前的代码中调用该函数。
// function函数声明定义提升
fun() //输出 fun执行
// 箭头函数fn2变量还未被赋值
fun2() //抛出错误
function fun() {console.log('fun执行')
}
// 箭头函数是一个赋值的过程
var fun2 = () => {console.log('fun2执行')
}
var,const,let 的区别
1.作用域区别
const和let有块级作用域. var没有块级作用域,可以跨块访问, 不能跨函数访问
// var可以跨块访问
if (true) {var x = 10
}
console.log('var可以跨块访问', x) //10// var 变量在函数内部声明时,它们是函数作用域的,故不能跨函数访问(闭包)
function foo() {var y = 20console.log(y) // 输出 20
}
console.log(y) //ReferenceError: y is not defined,因为y是函数foo的作用域内的局部变量
2.变量提升
var存在变量的声明提升 let和const不存在变量提升(会抛出错误)
在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区
// '声明提升,赋值未提升'
console.log(num1) //输出666
var num1 = 666
// name 在一个特殊的环境中(如window.name在浏览器环境中)可能是已经存在的
// console.log(name);//这里不会报错
3.全局对象属性
浏览器的全局对象是window。var在函数外声明的变量为全局变量,并且会将该变量添加为全局对象的属性。
let和const在script标签下最外层声明的变量虽然也是全局的,但不会在window对象的枚举属性中出现
if (true) {var varNum = 0
}
const constNum = 0
let letNum = 0console.log(window.varNum, window.constNum, window.letNum)//输出 0 undefined undefined

4.重复声明
var 会声明覆盖(不会提示) let const 不可重复声明(会抛出错误)
var num2 = 31
var num2 = 10
console.log('声明覆盖为最后一次', num1) //10
5.初始值设置
const声明的变量必须设置初始值(否则会抛出错误)
而let和var声明的变量则不必
6.指针指向
var和let创建的变量是可以更改指针指向(可以重新赋值)
const声明的变量是不允许改变指针的指向(不可以重新赋值),但const定义的引用数据类型Object的内容可变(因为这并不会改变变量存储的地址值)
总结:声明变量用let,声明常量用const,尽量不用var
2. 解构赋值
在JS中,传统的属性访问方法可能会涉及到大量的点操作符(.)或方括号([]),并且可能需要多行代码来声明和初始化变量。
而解构,它是一种表达式,允许你快速地将数组或对象的属性值提取到不同的变量中。这减少了编写重复和冗余代码的需要,使代码更加简洁和易读。
数组解构:
const arr1 = [1, 2, 3]
const [a, b, c] = arr1
console.log(a, b, c)//输1 2 3
对象解构:
const obj = {firstName: 'Alice',lastName: 'Bob',age: 18
}
const { firstName, lastName, age } = obj
console.log(firstName, lastName, age) //输出Alice Bob 18
// 将firstName重命名为aa
const { firstName: aa } = obj
console.log(aa) //输出Alice
例题:如何在不借助其他变量的情况下交换1和2的位置
let arr = [1, 2]
;[arr[0], arr[1]] = [arr[1], arr[0]]
console.log(arr) // 输出: [2, 1]
3.防抖与节流
防抖(Debouncing)和节流(Throttling)是两种常用的优化高频率触发事件的策略,它们主要用于控制函数执行的频率,以减少不必要的计算或DOM操作,从而提高页面性能。尽管它们的目的相似,但实现方式和应用场景有所不同。
防抖
概述: 防抖是指在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
防抖(Debounce) => 将多次操作变成一次(合并一定时间内的请求)
使用场景: 按钮防连点,搜索框实时搜索建议,文本输入的实时验证(验证(如检查邮箱、手机号格式等)),窗口大小调整时的重绘操作(文心一言窗口:页面大小不同时会有不同布局方案)
<input type="text" placeholder="搜索框实时搜索建议"id="frequencyInput" value=""/>
<script>const input = document.querySelector('#frequencyInput')// 防抖封装// 闭包私有环境-拥有各自独立的词法作用域const debounce = (fun, wait) => {let timeoutId = nullreturn () => {if (timeoutId) clearTimeout(timeoutId)timeoutId = setTimeout(fun, wait)}}const debounceTest = () => {console.log('发起请求')}//监听键盘输入事件 => 每次输入均触发debounce(debounceTest, 2000)// => 停止输入 2S 后 发起请求input.addEventListener('input', debounce(debounceTest, 2000))
</script>
节流
概述:节流是指规定在单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次能生效。(常见的节流实现方法包括使用定时器或时间戳)
节流(Throttle) => 在规定周期内仅执行一次
使用场景:页面滚动(懒加载图片或内容、无限滚动加载更多内容等),鼠标移动(鼠标跟随效果、鼠标悬停提示等),按钮点击(防止表单的重复提交、防止用户频繁点击导致的无效操作等)
<style>#box {width: 200px;height: 200px;margin-top: 20px;background-color: rgba(99, 181, 193, 0.5);border-radius: 10px;box-shadow: 2px 2px 2px 2px rgb(163, 181, 185);transition: 0.6s;}#box:active {scale: 0.8;background-color: rgba(83, 133, 141, 0.8);}
</style>
<div id="box"></div>
<script>const triggerBox = document.getElementById('box')// 节流封装// 闭包私有环境-拥有各自独立的词法作用域const throttle = (fun, time) => {let timer = nullreturn () => {// 如果timer为null则创建一个n s后执行的任务if (!timer) {timer = setTimeout(() => {fun()timer = null}, time)}}}const throttleTest = () => {console.log('发起请求')}//2s内,无论点击多频繁,都只执行一次triggerBox.addEventListener('click', throttle(throttleTest, 2000))triggerBox.addEventListener('click', () => {console.log('触发事件')})
</script>
4.深克隆与浅克隆
JavaScript中,浅克隆和深克隆的区分主要是针对引用数据类型(Object)。
对于基本数据类型(如Number, String, Boolean, Null, Undefined, Symbol, BigInt),赋值操作实际上是进行值的复制。因为基本数据类型存储的是值本身,所以当你将一个基本数据类型的变量赋值给另一个变量时,实际上是创建了一个新的变量,并将原始变量的值复制给了新变量。这个过程中,每个变量都拥有自己的内存空间,互不影响。
对于引用数据类型(如Object, Array, Function等),赋值操作实际上是复制了引用地址,而不是真正的数据。这意味着,如果你修改了新变量所指向的数据,原始变量所指向的数据也会发生变化,因为它们指向的是同一个内存地址。
let person = {name: 'IKUN',age: 18,intereste: ['唱', '跳', 'rap']}
浅克隆(Shallow Clone)
浅克隆只复制对象的第一层属性,如果对象的属性是基本类型,复制的就是基本类型的值;如果属性是引用类型,复制的就是内存地址,所以如果其中一个对象改变了这个地址的内容,就会影响到另一个对象。
1. 使用Object.assign()方法
//只复制了顶层属性
const newPerson = Object.assign({}, person)
newPerson.age = 20
newPerson.interest.push('篮球')
console.log(person.age) //18
//引用数据类型
console.log(person.interest) //['唱', '跳', 'rap','篮球']
只会复制顶层属性,如果属性中存在引用数据类型,则只会复制其地址,克隆的新对象会和原对象共享该引用数据的内存
2. 使用扩展运算符(...)
//复制数组或对象的顶层属性
const newPerson = { ...person }
newPerson.age = 20
newPerson.interest.push('篮球')
console.log(person.age) //18
//引用数据类型
console.log(person.interest) //['唱', '跳', 'rap','篮球']
将person(对象)扩展到一个新的对象中,但只会复制顶层属性,如果属性中存在引用数据类型,则只会复制其地址,克隆的新对象会和原对象共享该引用数据的内存
深克隆(Deep Clone)
深克隆会复制对象的所有层级,这样,无论多少层的对象,每一层的属性都是完全复制的,不会共享内存地址,从而实现了真正的独立复制。
1.使用JSON.parse()和JSON.stringify()
const newPerson = JSON.parse(JSON.stringify(person))
newPerson.age = 20
newPerson.interest.push('篮球')
console.log(person.age) //18
//引用数据类型
console.log(person.interest) //['唱', '跳', 'rap']
JSON.stringify()返回一个由对象转换的 JSON 字符串,JSON.stringify()再将JSON字符串转换成对象;这种方法能处理绝大多数深克隆问题。但
注意:这种方法不能复制函数、undefined、symbol等,且不能处理循环引用。
2.手动实现深克隆函数
function deepCopy(source) {const targetObj = source.constructor === Array ? [] : {}for (let keys in source) {//检查属性是直接定义在 source 对象上的,而非原型链继承的if (source.hasOwnProperty(keys)) {//引用数据类型if (source[keys] && typeof source[keys] == 'object') {//维护层代码targetObj[keys] =source[keys].constructor === Array ? [] : {}// console.log(source[keys]);//递归targetObj[keys] = deepCopy(source[keys])} else {//基本数据类型,直接 赋值targetObj[keys] = source[keys]}}}// console.log(targetObj);return targetObj
}
let newPerson = deepCopy(person)newPerson.interest.push('GEGE')
console.log(person) //输出['唱', '跳', 'rap']
使用递归函数来逐层复制来实现深拷贝。
3.使用一些第三方库,如lodash的_.cloneDeep()方法。
5.原生JS获取DOM
1.通过ID
这是获取单个元素的最快方式,因为ID在HTML文档中应该是唯一的。
let element = document.getElementById("myElementId");
2.通过类名
注意,这会返回一个DOM集合,即使只有一个元素匹配。
let elements = document.getElementsByClassName("myClassName");
3.通过标签名
和getElementsByClassName()一样,会返回一个DOM集合
let elements = document.getElementsByTagName("div");
4.通过css选择器
document.querySelector()和document.querySelectorAll()允许你使用CSS选择器来获取元素。querySelector()返回匹配选择器的第一个元素,而querySelectorAll()返回所有匹配选择器的元素的一个NodeList。
// 获取第一个匹配的元素
let firstElement = document.querySelector(".myClassName"); // 获取所有匹配的元素
let allElements = document.querySelectorAll(".myClassName");
5.通过name属性
虽然不常用,但也可以通过元素的name属性来获取元素集合,这主要用于获取表单元素,如<input>、<select>等。
let elements = document.getElementsByName("myElementName");
6.Dom修改元素样式
// 获取元素
let elem = document.getElementById("myElement");
1.dom.style
直接设置样式属性,长一点的属性名字用驼峰命名法
// 修改背景颜色
elem.style.backgroundColor = "blue"; // 修改字体大小
elem.style.fontSize = "20px"; // 隐藏元素
elem.style.display = "none";
2. setAttribute和removeAttribute
通常不推荐用于样式修改,因为它会覆盖元素的所有内联样式,而不是单独修改一个属性。
// 设置样式(不推荐)
elem.setAttribute("style", "background-color: red; color: white;"); // 移除style属性(不推荐)
// elem.removeAttribute("style");
3.className / id
这种方式很有可能会覆盖之前写好的样式(前面写的不生效)
//style
.addClass{
//.....样式
}
#addId{
//.....样式
}section.className = 'addClass'
section.id = 'addId'
4.classList 推荐使用
// 添加CSS类
elem.classList.add("newClass"); // 移除CSS类
elem.classList.remove("oldClass"); // 切换CSS类(如果存在则移除,如果不存在则添加)
elem.classList.toggle("toggleClass"); // 检查元素是否包含某个类
if (elem.classList.contains("someClass")) { console.log("元素包含someClass类");
}
7.Promise
推荐参考请参考异步编程(Promise详解)
8.微任务和宏任务
请参考 浏览器渲染基本原理
常见的微任务来源Promise的.then()或catch()方法
常见的宏任务来源setTimeout的回调函数
// 立即把一个函数添加到微队列// Promise.resolve().then(函数)// Promise的.then()方法中的回调是异步的// 代码解析顺序console.log('script start'); // 1. 同步代码,直接执行 setTimeout(function() { // 2. setTimeout()内宏任务,推入宏队列,等待执行console.log('setTimeout'); //# 9.宏任务执行}, 0); Promise.resolve().then(function() { //3. .then()内微任务,推入微队列等待执行 console.log('promise1'); //# 6.微任务队列内立即执行setTimeout(function() { // 7. 宏任务,推入任务队列,等待执行 console.log('setTimeout in promise'); //# 10.宏任务执行}, 0); }).then(function() { //4. .then()内微任务,推入微任务队列等待执行 console.log('promise2'); //# 8.微任务队列内立即执行}); console.log('script end'); // 5. 同步代码,直接执行 // 输出顺序:script start, script end, promise1, promise2, // setTimeout, setTimeout in promise
- 先执行全局代码(同步,解析时遇到就执行),再执行异步代码(微队列优先,其他队列靠后)
- 解析顺序 ==> 任务执行顺序
-----------------------------------------------------------------------------------------------------------------------------
若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃
相关文章:
前端八股文笔记【三】
JavaScript 基础题型 1.JS的基本数据类型有哪些 基本数据类型:String,Number,Boolean,Nndefined,NULL,Symbol,Bigint 引用数据类型:object NaN是一个数值类型,但不是…...
AI学习记录 - transformer的Embedding层
创作不易,免费的赞 前面有介绍了GPT2如何进行token化的过程,现在讲下transformer的Embedding层 Embedding层就是一个巨大的矩阵,边长分别是词汇表长度和词向量维度,矩阵里面的每一个数字都是一个随机初始化的,或者是…...
23-PCB封装名称的统一添加与管理
1.进入封装管理器 2. 选择对象,点击右侧添加按钮 3. 搜索所需要的封装 4.接受创建变更 5.执行变更 6.关闭...
【Python从入门到进阶】62、Pandas中DataFrame对象案例实践
接上篇《61、Pandas中DataFrame对象的操作(二)》 上一篇我们讲解DataFrame对象的统计分析、可视化以及数据导出与保存相关内容。本篇我们延续之前学习的DataFrame对象的知识,结合一个数据案例进行实践操作。 一、案例说明 我们将通过一个股…...
使用Python实现深度学习模型:智能环境监测与预警
介绍 智能环境监测与预警是保护生态环境和人类健康的重要手段。通过深度学习技术,我们可以实时获取环境数据,分析环境变化趋势,及时发出预警。本文将介绍如何使用Python和深度学习库TensorFlow与Keras来构建一个简单的环境监测与预警模型。 环境准备 首先,我们需要安装必…...
ThreadLocal的使用场景是什么
ThreadLocal 是 Java 中用于实现线程局部变量的工具,它提供了每个线程独立的变量副本,使得不同线程对该变量的操作不会相互干扰。以下是 ThreadLocal 的常见使用场景: 线程安全的对象共享: ThreadLocal 可以用来避免线程间共享状…...
【网络爬虫篇】逆向实战—某东:滑块验证码(逆向登录)2024.8.7最新发布,包干货,包详细
【网络爬虫篇】更多优秀文章借鉴: 1. 使用Selenium实现黑马头条滑块自动登录 2. 使用多线程采集爬取豆瓣top250电影榜 3. 使用Scrapy爬取去哪儿网游记数据 4. 数据采集技术综合项目实战1:国家水稻网数据采集与分析 5. 数据采集技术综合项目实战2&#x…...
为什么优质的酱香白酒都会带点苦味?
大家好,我是酱酒亮哥,不知大家有没有发现,在制作一杯美味的咖啡或是烘焙一块香脆的面包时,制作过程中都会有一些独特的味道和香气产生,对吧?同样地,酱香白酒的酿造过程也是一个复杂而精细的化学…...
软件测试常见面试题
软件测试阶段分为单元测试,集成测试,系统测试,验收测试。单元测试策略为对代码中的函数方法进行测试,目的是发现代码的问题。集成测试策略是模块中组合起来进行测试,要求发现与接口有关的问题。系统测试策略是子系统的…...
面试经典算法150题系列-接雨水
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,…...
【C++】 类型转换深度探索:揭开类型转换的奥秘
🌈 个人主页:Zfox_ 🔥 系列专栏:C从入门到精通 目录 一: 🚀 C语言中的类型转换 二: 🔥 为什么C需要四种类型转换 三: 🔥 C强制类型转换 🥝 3.1 st…...
深入探索Webkit的Web Authentication API:安全与便捷的融合
Web Authentication API,通常被称为WebAuthn,是一个新兴的Web标准,旨在通过提供更安全、更便捷的认证方式来改善用户的在线体验。随着Webkit对WebAuthn的支持日益增强,本文将深入探讨这一API的功能、实现方式以及如何在Webkit浏览…...
Vue - 关于v-wave 波浪动画组件
Vue - 关于v-wave 波浪动画组件 这个动画库可以在标签中添加新的v-wave属性,来让点击标签元素后添加漂亮的波纹效果,并且可以根据父元素自动形成波纹的颜色,也可以自定义波纹颜色,持续时间,透明度,触发的对…...
计算机网络408考研 2019
计算机网络408考研2019年真题解析_哔哩哔哩_bilibili 2019 1 1 1 1...
实时捕捉与追溯:得物基于 eBPF 打造云上网络连接异常摄像头
近期我们容器 SRE 团队基于 eBPF 技术建设网络连接异常感知能力,灰度上线过程中发现了生产环境 10 以上的应用配置错误、程序 Bug 等问题。在和应用负责同学同步风险过程中,大家都挺好奇我们如何实现在对应用无侵入的情况下发现服务连接异常的。本篇文档…...
ubuntu14.04图形界面配置
Ubuntu系统启动,输入用户密码后,屏幕显示彩色背景,但是始终不能进入图形界面。 如果你也遇到过这种情况,可以参照以下方法解决(在 ubuntu14.04 验证)。 同时按下 alt ctrl F1,屏幕出现 tty1&a…...
51单片机-第八节-蜂鸣器
一、什么是蜂鸣器? 蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号。 蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器: 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可…...
Windows命令查看WiFi密码
查看所有已保存的WiFi网络 (以管理员身份)输入以下命令 netsh wlan show profiles查看某个WiFi网络的密码 netsh wlan show profile name"你的网络名" keyclear在输出中,在关键内容(Key Content)字段下找…...
不同环境下RabbitMQ的安装-2 ARM架构、X86架构、Window系统环境下安装RabbitMQ
ARM架构、X86架构、Window系统环境下RabbitMQ的安装 RabbitMQ安装1 Erlang语言介绍2 安装Erlang2.1 ARM架构的CentOS虚拟机中安装Erlang2.2 X86架构的CentOS虚拟机中安装Erlang2.3 Windows系统安装Erlang2.3.1 下载Erlang2.3.2 安装Erlang2.3.3 配置Erlang2.3.4 检测Erlang 3.安…...
C++(week16): C++提高:(六) Qt提高
文章目录 四、Qt的元对象系统1.元对象和MOC(1)自省 和 反射(2)Qt是怎样支持元对象系统的?(3)支持元对象系统的三个要求(4)元对象系统的功能(5)动态属性 2.信号和槽机制(1)信号与槽机制的基本原理(2)自定义信号、自定义槽函数①自定义信号②自定义槽③关联 connect (…...
Linux I/O 演进史:从管道到零拷贝,一篇串起个服务端核心原语俅
前言 在使用 kubectl get $KIND -o yaml 查看 k8s 资源时,输出结果中包含大量由集群自动生成的元数据(如 managedFields、resourceVersion、uid 等)。这些信息在实际复用 yaml 清单时需要手动清理,增加了额外的工作量。 使用 kube…...
BthPS3驱动:突破Windows壁垒,让PS3控制器焕发新生
BthPS3驱动:突破Windows壁垒,让PS3控制器焕发新生 【免费下载链接】BthPS3 Windows kernel-mode Bluetooth Profile & Filter Drivers for PS3 peripherals 项目地址: https://gitcode.com/gh_mirrors/bt/BthPS3 当PS3控制器遇上Windows&…...
【GitHub开源项目专栏】TensorRT-LLM深度解析:NVIDIA推理优化引擎架构
摘要 TensorRT-LLM是NVIDIA官方推出的开源LLM推理优化框架,通过AOT编译、算子融合、FP8/INT4量化等核心技术,在H100 GPU上实现了6000 tokens/s的吞吐量。本文深入剖析其核心架构、插件系统、量化技术栈以及与vLLM的生态对比,为企业级LLM部署提…...
Python数据分析环境部署:Anaconda与Phi-3-mini协作指南
Python数据分析环境部署:Anaconda与Phi-3-mini协作指南 1. 为什么选择这个组合? 在开始动手之前,我们先聊聊为什么Anaconda和Phi-3-mini是数据科学家的好搭档。Anaconda就像是一个瑞士军刀,把Python环境管理和包依赖这些麻烦事都…...
5个步骤彻底解锁Cursor Pro:完整免费使用方案与设备重置指南
5个步骤彻底解锁Cursor Pro:完整免费使用方案与设备重置指南 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached yo…...
Spring AI 快速入门教程:基于VUE3与Spring AI技术实现的“流式聊天““打字机效果“功能
目录 前言 一、Spring AI 核心认知 1.1 技术定位与核心价值 1.2 版本支持与生态兼容性 1.3 与其他 AI 集成框架对比 二、效果展示 三、快速入门 3.1 环境准备 JDK 配置 AI 服务密钥准备 3.2 后端项目创建 主要技术栈 pom.xml 配置 application.yml 配置 Java 主…...
5分钟掌握Switch破解神器TegraRcmGUI:新手也能轻松上手的图形化注入教程
5分钟掌握Switch破解神器TegraRcmGUI:新手也能轻松上手的图形化注入教程 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI 还在为Switch破解的复杂…...
开源可部署+高算力适配:internlm2-chat-1.8b在Ollama中GPU利用率提升方案
开源可部署高算力适配:internlm2-chat-1.8b在Ollama中GPU利用率提升方案 1. 模型简介与部署准备 InternLM2-Chat-1.8B是第二代书生浦语系列中的18亿参数对话模型,专门针对聊天场景进行了深度优化。这个模型在指令遵循、对话体验和功能调用方面表现出色…...
保姆级教程:用Eclipse Paho库在Java中实现MQTT over WebSocket连接(以DJI无人机控制为例)
从零构建无人机控制系统:基于Eclipse Paho的MQTT over WebSocket实战指南 引言 想象一下,你正在开发一个需要实时控制无人机的Web应用。传统的TCP连接在浏览器环境中处处受限,而WebSocket技术恰好能解决这一痛点。本文将带你深入探索如何利用…...
基于拓展卡尔曼滤波的同步定位与地图构建全流程,通过自身运动模型和测距方位传感器,实时估计自身位姿并构建环境地标地图附matlab代码
✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…...
