前端八股文笔记【三】

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 循环:

遍历可迭代对象的值 ArrayMapSetarguments 等

let iterable= ['a', 'b', 'c']
for (const value of iterable) {console.log(value) //输出a b c
}

5 .setTimeout 和 setInterval

  1. setTimeout 接收一个回调函数和一个时间参数,创建一个滞后执行的任务,并返回一个Id,可通过clearTimeout取消任务
  2. 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 张三

特点:

  1.  通过class关键字定义类。在类中,可以定义构造函数(使用constructor方法)、实例属性、实例方法、静态属性和静态方法(static关键字声明,静态方法属于类本身,不属于实例)

  2. 可以封装对象的属性和方法,使得外部不能直接访问或修改对象的内部状态,只能通过类提供的方法来进行交互
  3. 通过extends关键字实现继承。相比ES6以前的继承机制,简化了继承的语法,使得继承变得更加直观和简单。

  4. 在子类的构造函数中,必须首先调用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 张三

特点:

  1. 使用Child.prototype = new Parent(),会创建一个新的Parent实例,并执行Parent构造函数中的所有代码。Parent()中定义的任何绑定到this的属性都将存在于Child.prototype,其通过这种方式来实现Parent()内实例属性的继承。
  2. 在使用这种继承方式时,无法直接通过子类的构造函数向父类构造函数传递参数。这限制了父类构造函数根据子类实例的不同需求进行初始化的能力。

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

特点:

  1. 使用Parent.call(this, name)可以通过子类的构造函数向父类构造函数传递参数,增强了父类构造函数随子类需求初始化的能力
  2. 无法继承父类原型上的方法,只能继承父类构造函数Parent()内定义的实例属性和方法
  3. 父类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 张三

特点:

  1. 用原型链来继承父类原型上的方法,用借用构造函数来继承父类属性。
  2.  Child.prototype = Object.create(Parent.prototype)代替Child.prototype = new Parent(),创建一个新的空对象,其原型设置为Parent.prototype,并让Child.prototype指向这个对象。这并不会创建父类的实例,也不会执行Parent()内的代码,Child.prototype上也就不会存在Parent()内定义的实例属性。
  3. 在子类中调用父类构造函数,依然会在每个子类实例中都创建父类的实例属性和方法,但属性通常会被子类实例初始化的属性覆盖。而方法,只要将其定义在父类原型上,便不会在子类实例中重复创建。也就不会造成内存浪费。

5.其他继承

除了上述4种继承方式,还有寄生组合式继承,寄生式继承,混合继承等其他继承方式,此处不再一一列举。

其他题型

1.var,let和const

声明提升
  1. 声明提升(包括变量声明和函数声明): JS中,代码执行时,浏览器或会首先处理所有的声明,并将它们提升到它们所在作用域(全局作用域或函数作用域)的顶部。这个过程就是声明提升。
  2. 变量提升(特指变量声明的提升): 在变量提升中,只有变量的声明部分被提升,而变量的赋值不会提升。这意味着,如果你在声明变量之前就尝试访问它,你将得到一个undefined的值,因为此时变量已经被声明,但还没有被赋值。
  3. 需要注意,虽然函数声明也会被提升,但与变量提升不同的是,函数声明的整个定义(包括函数体和变量名)都会被提升到作用域顶部,而不仅仅是函数的声明部分。这意味着你可以在函数声明之前的代码中调用该函数。
// 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字符串转换成对象;这种方法能处理绝大多数深克隆问题。但

注意这种方法不能复制函数、undefinedsymbol等,且不能处理循环引用

        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
  • 先执行全局代码(同步,解析时遇到就执行),再执行异步代码(微队列优先,其他队列靠后)
  • 解析顺序   ==>   任务执行顺序

-----------------------------------------------------------------------------------------------------------------------------

若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397086.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十三、代理模式

文章目录 1 基本介绍2 案例2.1 Sortable 接口2.2 BubbleSort 类2.3 SortTimer 类2.4 Client 类2.5 Client 类的运行结果2.6 总结 3 各角色之间的关系3.1 角色3.1.1 Subject ( 主体 )3.1.2 RealObject ( 目标对象 )3.1.3 Proxy ( 代理 )3.1.4 Client ( 客户端 ) 3.2 类图 4 动态…

Java网络编程、TCP、UDP、Socket通信---初识版

标题 InetAddress----IP地址端口号协议&#xff08;UDP/TCP&#xff09;JAVA操作-UDP一发一收模式多发多收 JAVA操作-TCP一发一收多发多收 实现群聊功能BS架构线程池优化 InetAddress----IP地址 端口号 协议&#xff08;UDP/TCP&#xff09; JAVA操作-UDP 一发一收模式 多发多收…

React 性能优化

使用 useMemo 缓存数据 &#xff08;类似 vue 的 computed&#xff09;使用 useCallback 缓存函数异步组件 ( lazy )路由懒加载( lazy )服务器渲染 SSR用 CSS 模拟 v-show 循环渲染添加 key使用 Fragment &#xff08;空标签&#xff09;减少层级 不在JSX 中定义函数&#xff0…

一篇教会搭建ELK日志分析平台

日志分析的概述 日志分析是运维工程师解决系统故障&#xff0c;发现问题的主要手段日志主要包括系统日志、应用程序日志和安全日志系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因经常分析日志可以了解服务器的负荷&#xff0c;性…

使用本地大模型从论文PDF中提取结构化信息

1 安装ollama 点击前往网站 https://ollama.com/ &#xff0c;下载ollama软件&#xff0c;支持win、Mac、linux 2 下载LLM ollama软件目前支持多种大模型&#xff0c; 如阿里的&#xff08;qwen、qwen2&#xff09;、meta的(llama3、llama3.1)&#xff0c; 读者根据自己电脑…

C语言:求最大数不用数组

&#xff08;1&#xff09;题目&#xff1a; 输入一批正数用空格隔开&#xff0c;个数不限&#xff0c;输入0时结束循环&#xff0c;并且输出这批整数的最大值。 &#xff08;2&#xff09;代码&#xff1a; #include "stdio.h" int main() {int max 0; // 假设输入…

Qt——多线程

一、QThread类 如果要设计多线程程序&#xff0c;一般是从QThread继承定义一个线程类&#xff0c;并重新定义QThread的虚函数 run() &#xff0c;在函数 run() 里处理线程的事件循环。 应用程序的线程称为主线程&#xff0c;创建的其他线程称为工作线程。主线程的 start() 函数…

计算机网络408考研 2014

1 计算机网络408考研2014年真题解析_哔哩哔哩_bilibili 1 111 1 11 1

MyBatis:Maven,Git,TortoiseGit,Gradle

1&#xff0c;Maven Maven是一个非常优秀的项目管理工具&#xff0c;采用一种“约定优于配置&#xff08;CoC&#xff09;”的策略来管理项目。使用Maven不仅可以把源代码构建成可发布的项目&#xff08;包括编译、打包、测试和分发&#xff09;&#xff0c;还可以生成报告、生…

短视频SDK,支持Flutter跨平台框架,加速产品上线进程

在数字内容爆炸式增长的今天&#xff0c;短视频已成为连接用户、传递情感、展现创意的重要桥梁。为助力开发者快速融入这股潮流&#xff0c;美摄科技匠心打造了一款专为Flutter框架优化的短视频SDK解决方案&#xff0c;旨在降低技术门槛&#xff0c;加速产品迭代&#xff0c;让…

主题与分区

主题和分区是Kafka的两个核心概念&#xff0c;分区的划分不仅为Kafka提供了可伸缩性、水平扩展的功能&#xff0c;还通过多副本机制来为Kafka提供数据冗余以提高数据可靠性。 主题创建 主题和分区都是提供给上层用户的抽象&#xff0c;而在副本层面或更加准确地说是Log层面&a…

Unity效果优化之抗锯齿

Unityde 基于HDRP渲染管线的抗锯齿处理的设置参考图&#xff1a; 前提&#xff1a;需要导入HDRP的插件包才行&#xff0c; 该参数设置能保证在PC版上抗锯齿效果非常好&#xff0c; 英文版&#xff1a;

AWS Lambda 十年回顾:功能总览、更新记录与入门指南

这次&#xff0c;我为2014年11月发布的AWS Lambda创建了一个历史时间表。AWS Lambda 是一项无服务器、全托管的代码执行服务&#xff0c;今年2024年11月将迎来其宣布发布的十周年纪念。虽然提前了一些&#xff0c;但为了提前庆祝这一重要时刻&#xff0c;我写了这篇文章。 文章…

论文解读 | ACL 2024:自我蒸馏在语言模型微调中架起分布差异的桥梁

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 杨兆瑞 浙江大学CAD&CG全国重点实验室博士生 导师为陈为教授 概述 大型语言模型&#xff08;LLMs&#xff09;的兴起彻底改变了自然语言处理领域&#xff0c;但对它们进行特定任务的微调常常面临在平衡性能…

C++ | (一)C++入门基础

从本篇文章开始&#xff0c;我们正式进行C的系统学习。C是在C语言的基础上添加了面向对象编程的特性&#xff0c;是C语言的延伸&#xff0c;并遵循C语言的绝大多数语法。如果想学习C&#xff0c;必须要有一定的C语言基础&#xff0c;这样学起来才不会太过痛苦。 本文章即假设读…

【实战】Spring Security Oauth2自定义授权模式接入手机验证

文章目录 前言技术积累Oauth2简介Oauth2的四种模式授权码模式简化模式密码模式客户端模式自定义模式 实战演示1、mavan依赖引入2、自定义手机用户3、自定义手机用户信息获取服务4、自定义认证令牌5、自定义授权模式6、自定义实际认证提供者7、认证服务配置8、Oauth2配置9、资源…

【数据结构】—— 队列

1、队列的概念2、队列的结构如何选择合适的数据结构实现队列&#xff08;数组or链表&#xff09; 3、队列的链式存储3.1 队列的链式存储结构3.2 队列的常见接口3.3 队列的接口实现初始化判空入队列出队列获取队头元素获取队尾元素获取节点个数销毁 3.4 源代码 4、队列的顺序存储…

k8s持久化存储PV和PVC

一、PV和PVC 1.PersistentVolume (PV) PersistentVolume (PV) 是外部存储系统中的⼀块存储空间&#xff0c;由管理员创建和维护。与 Volume⼀样&#xff0c; PV 具有持久性&#xff0c;⽣命周期独⽴于 Pod&#xff1b; 2.PersistentVolumeClaim (PVC) PersistentVolumeClaim…

散点图、折线图 -- 通过javascript实现

散点图 散点图适合用于探索数据大局、比较值、发现趋势、模式和变量间关系&#xff0c;是数据分析中直观展示和初步探索的有力工具。 代码&#xff1a; <!DOCTYPE html> <html> <script src"https://cdn.plot.ly/plotly-latest.min.js"><…

如何快速实现MODBUS TCP转Profinet——泗博网关EPN-330

泗博网关EPN-330可作为PROFINET从站&#xff0c;支持与西门子S7-200 SMART/300/400/1200/1500全系列PLC以及具有PROFINET主站的系统无缝对接&#xff0c;而Modbus TCP端&#xff0c;可以与Modbus TCP从站设备、主站PLC、DCS系统以及组态软件等进行数据交互。 通过EPN-330&…