2024前端面試真題【JS篇】

DOM

DOM:文本對象模型,是HTML和XML文檔的編程接口。提供了對文檔的結構化的表述,并定義可一種方式可以使從程序中對該結構進行訪問,從而改變文檔的結構、樣式和內容。

DOM操作

  1. 創建節點:document.createElement()document.createTextNode()document.createDocumentFragment()(創建文檔碎片,表示一種輕量級的文檔,主要用來存儲臨時節點,然后把文檔碎片的內容一次性添加到DOM中)、document.createAttribute()
  2. 查詢節點:querySelector()querySelectorAll()
  3. 更新節點:innerHTMLinnerTexttextContent
  4. 添加節點:innerHTMLappendChild()insertBefore()
  5. 刪除節點:removeChild()

如何判斷一個元素是否在可視區域內

  • offsetTop、scrollTop
  • getBoundingClientRect
  • Intersection Observe
  1. offsetTop、scrollTop實現
function isInViewPortOne(el) {const viewPortHeight = window.innerHeight || document.documentElement.clienHeight || document.body.clientHeightconst offsetTop = el.offsetTopconst scrollTop = document.documentElement.scrollTopconst top = offsetTop - scrollTopreturn top <= viewPortHeight
}
  1. getBoundingClientRect()實現
function isInViewPortOne(el) {const viewHeight = window.innerHeight || document.documentElement.clienHeight || document.body.clientHeightconst viewWidth = window.innerWidth || document.documentElement.clienWidth || document.body.clientWidthconst {top,right,bottom,left} = el.getBoundingClientRect()return (top > 0 && left > 0 && bottom > 0 && left > 0)
}

BOM

BOM:瀏覽器對象模型,頂級對象是window,表示瀏覽器的一個實例。

window對象

  • window.open(url, target)
  • window.close():僅用于關閉window.open()打開的窗口;
  • 窗口操作方法:
    • moveBy(x, y)
    • moveTo(x, y):移動窗體左上角到相對于屏幕左上角的(x,y)點;
    • resizeTo(x, y)
    • resizeBy(x, y)
    • scrollTo(x, y)
    • scrollBy(x, y)

location對象

  • location.hash:#后面的字符
  • location.host:服務器名稱+端口號
  • location.hostname:域名
  • location.href:完整url
  • location.port
  • location.pathname:服務器下文件路徑
  • location.protocol:使用協議
  • location.search:url查詢字符串,?后的內容

navigator對象

  • navigator.appName
  • navigator.geolocation
  • navigator.getUserMedia():可用媒體設備硬件關聯的流
  • navigator.mediaDevices:可用的媒體設備

screen對象

  • screen.height
  • screen.width
  • screen.pixelDepth

history對象

  • history.go()
  • history.back()
  • history.forward()
  • history.length

JS的數據類型

類型分類

  • 基本類型:包含NumberStringBooleanNull(空對象指針,typeof判斷時候會返回object)、UndefinedSymbol
  • 引用類型:包含ObjectArrayFunction(還有DateRegExpMapSet等)。

存儲方式

基本數據類型存儲在棧中(讀取棧中數據),引用數據類型存儲在堆中(索引棧地址,讀取堆中數據)。

常見問題

  • 聲明變量時不同的地址分配
    • 基本數據類型的值存放在棧中,在棧中存放的是對應的值
    • 引用數據類型的值存放在堆中,在棧中存放的是指向堆內存的地址
  • 不同的類型數據導致賦值變量時的不同
    • 基本數據類型賦值,是生成相同的值,兩個數據對應不同的地址;
    • 引用數據類型賦值,是將保存對象的內存地址復制給另一個變量,兩個數據對應指向同一個堆內存中的地址;

類型轉換機制

類型轉換分類

  • 顯式轉換:Number()、String()、parseInt()、Boolean()
  • 隱式轉換

隱式轉換注意點

  1. 對象與基本類型數據比較:對象會先調用valueOf方法(如果valueOf方法繼續返回對象,則調用toString方法),嘗試得到一個原始值來進行比較;
  2. nullundifined比較:==或者!=時是相等的,其它情況下不等;
  3. NaN的比較:與任何值都不等。(要檢查一個值是否是NaN,應使用Number.isNaN()函數)
  4. 布爾值與數字或字符串比較:布爾值會先轉換為數字,然后按照數字與數字或者字符串比較規則比較。
  5. 兩個值都為引用類型,則比較他們是否指向同一個對象;

常見問題

  • Number(undefined) // NaN
  • parseInt('234sdf2') // 234
  • null == undefined // true
  • [] == ![] // true
  • typeof null // object
  • undefinednull與自身嚴格相等

數據類型檢測:typeofinstanceof

typeof操作符返回一個字符串,表示未經計算的操作數的類型。使用方法:typeof val

typeof判斷引用類型數據,只能識別出function,其它均為object

instanceof用于檢測構造函數的prototype屬性是否出現在某個實例對象的原型鏈上。使用方法:object instanceof constructor

構造函數通過new可以實例對象,instanceof能判斷這個對象是否是之前那個構造函數生成的對象。

typeofinstanceof用于判斷數據類型,均存在弊端。因此,通用的數據類型檢測方法為:Object.prototype.toString(),該方法統一返回[object Xxx]字符串。

數據類型檢測方法封裝

const getValType = val => {let type = typeof val;if(type !== 'object') return type;return Object.prototype.toString.call(val).replace(/^\[object (\S+)\]$/, '$1');
}

常用的字符串操作方法

  • +${}拼接以及concat()等;
  • (創建新的副本):slice(start, end)substr(start, end)substring(start, num)
  • (創建新的副本):trim()toLowerCase()repeat()等;
  • chatAt()indexOf()includes()strartWith()等;
  • 轉換方法split()
  • 模板匹配match()search()replace()

數組的常用操作方法

  • push()unshift()splice()concat()
  • pop()shift()splice()slice()
  • splice()
  • indexOf()includes()find()
  • 排序sort()reverse()
  • 轉換join()
  • 迭代some()map()filter()forEachevery()

淺拷貝和深拷貝

淺拷貝和深拷貝的主要區別是在復制對象或數據結構時,拷貝的深度以及對原始數據內部結構的影響。

基本類型傳遞的是值,數據存儲在棧中,引用類型傳遞的是地址!數據存儲在堆中

  • 淺拷貝:基本類型拷貝的是基本類型的值;引用類型拷貝:創建一個新對象,只復制原始對象的基本數據類型的字段或引用地址,不復制引用指向的對象,即新對象和原始對象數據均指向同一個引用對象,數據修改會相互影響;
    • Object.assigin()
    • slice()/concat
    • 拓展運算符
  • 深拷貝:創建一個新對象,遞歸復制原始對象的所有字段和引用對象,即新對象和原始對象之間的數據相互獨立,復制后無直接影響關系。
    • JSON.stringfly():存在弊端,會忽略undefined、symbol和函數
    • 手寫循環遞歸

常見問題

  • 手寫一個淺拷貝
const shallowCopyFn = obj => {let result = null;const type = Object.prototype.toSting.call(obj)// 創建一個新對象if(type === '[Object Object]') {result = {}} else if(Object === '[Object Array]'){result = []} else {result = obj}// 對象數據 基本類型和字段賦值for(let prop in obj) {if(obj.hasOwnProperty(prop)) {result[prop] = obj[prop]}}return result
}

淺拷貝或者可以使用Object.assign()、ES6的展開運算符、concat()等實現。

  • 手寫一個深拷貝
function deepClone(obj, hash = new WeakMap()) {  if (obj === null) return null; // null 的情況  if (obj instanceof Date) return new Date(obj); // 日期對象直接返回一個新的日期對象  if (obj instanceof RegExp) return new RegExp(obj); // 正則對象直接返回一個新的正則對象  if(typeof obj !== "object") return obj;// 如果循環引用了就用 weakMap 解決  if (hash.has(obj)) return hash.get(obj);  let cloneObj = new obj.constructor;  // 找到所屬類型原型上的constructorhash.set(obj, cloneObj);  for (let key of obj) {  if(obj.hasOwnProperty(key))cloneObj[key] = deepClone(obj[key], hash)}  return cloneObj;  
}  // 示例  
const original = { a: 1, b: { c: 2 }, d: [3, 4], e: new Date(), f: /abc/g, g: function() {} };  
const cloned = deepClone(original);  
console.log(cloned);  
console.log(cloned === original); // false  
console.log(cloned.b === original.b); // false

JS的數據結構

數據結構:計算機存儲、組織數據的方式。

分類

  • 數組:連續的內存空間保存數據,保存的數據個數在內存分配的時候是確認的;
  • 棧(Stack):先進后出(LIFO)的有序集合;
  • 隊列(Queue):先進先出(FIFO)的有序集合;
  • 堆(Heap)
  • 鏈表:以鍵-值對存儲的數據結構;
  • 字典
  • 散列表:也稱為哈希表,特點是操作很快;

原型和原型鏈

原型

由于JS中只有對象沒有類(ES6之前),因此為了解決數據共享,引入了原型的概念。

原型其實就是一個普通對象,prototype, 也稱為顯式原型,主要作用是為其它對象提供共享屬性。

  • 只有構造函數才有原型;
  • 公有屬性,可操作;
  • 幾乎所有對象在創建的時候都會被賦予一個非空的值作為原型對象的引用;

隱式原型__proto__

  • 只有對象(普通對象、函數)具備;
  • 私有的對象屬性,不可操作;

顯示原型prototype是構造函數才具備的,普通對象要調用構造函數的方法,就只能通過__proto__ 。隱式原型全等于顯示原型,即__proto__ === prototype

常見問題

  • Google中,隱式原型的寫法:[[prototype]]
  • 函數的原型是放在prototype上;
  • 對象、數組的原型是放在__proto__上;
  • Object.getPrototypeof(obj):獲取val的原型對象

constructor、原型對象和函數實例三者間的關系

默認情況下,所有的函數的原型對象都會自動獲得一個名為
constructor的屬性,指向與之關聯的構造函數。

function Person() {}let per = new Person()console.log(Person.prototype === per.__proto__) // true
console.log(Person.prototype.constructor === Person) // true

構造函數的原型和函數實例對象的原型是同一個對象

常見問題

  • constructor用于判斷類型:arr.constructor === Array // true

原型鏈

原型鏈其實就是一條訪問鏈路,通過對象特有的原型構成的一種鏈式結構,用來繼承多個引用類型的屬性和方法。當試圖訪問一個對象的屬性時,就會在原型鏈上進行查找,默認情況下,終點就是最初原型對象的原型:null

字符串。數組、構造函數的原型最終都會指向Object,而Object的原型指向是null

常見問題

__proto__ === prototype 
prototype == {}
{}.__proto__ == Object.prototype[].__proto__ === Array.prototype
{}.__proto__ === [].__proto__.__proto__Person.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

總結

  1. 一切對象都是繼承自ObjectObject對象直接繼承根源對象null
  2. 一切的函數對象,都是繼承自Function對象;
  3. Object對象直接繼承自Function對象;
  4. Function對象的__proto__會指向自己的原型對象,最終還是繼承自Object

繼承

在JS中,所有引用類型都繼承了Object,而繼承也是通過原型鏈實現的,常見繼承方法有8種。

  1. 原型鏈繼承:把子類的原型指向父類構造函數實例來覆蓋子類原型對象。一般用于一個子類繼承的情況,避免屬性篡改影響
function Parent(name) {this.name = namethis.hobby= ['吃飯','睡覺']
}
Parent.prototype.getInfo = _ => {console.log(this.name)console.log(this.hobby)
}
function Child() {}
Child.prototype = new Parent()const children1 = new Child()
children1.name = '222'
children1.hobby.push('唱歌')
children1.getInfo() // 輸出 222 ['吃飯','睡覺', '唱歌']const children2 = new Child()
children2.name = '333'
children2.getInfo() // 輸出 333['吃飯','睡覺', '唱歌']
  1. 構造函數繼承:在子類構造函數中調用父類構造函數,把this指向改變為子類對象(借助call)。

注:父類的引用屬性不會被共享,即構造函數繼承,只能繼承父類的實例屬性和方法,不能繼承原型屬性和方法

function Parent(name) {this.name = name;this.hobby = ['吃飯', '睡覺']
}
Parent.prototype.getName = function() {return this.name
}
function Child(name) {Parent.call(this, name)
}
const children1 = new Child('小明')
children1.hobby.push('唱歌')
console.log(children1.name + ', ' + children1.hobby) // 輸出:小明 ['吃飯','睡覺', '唱歌']const children2 = new Child()
children2.name = '小紅'
console.log(children2.name + ', ' + children2.hobby) // 輸出:小紅 ['吃飯','睡覺']
children2.getName() // 報錯
  1. 組合繼承:將原型鏈繼承和構造函數繼承結合。主要是使用原型鏈實現對原型屬性和方法的繼承,又通過借用構造函數實現對實例屬性的繼承。因此,組合式繼承一般情況下回調用兩次父類的構造函數
function Parent(name) {this.name = name;this.hobby = ['吃飯', '睡覺']
}
Parent.prototype.getInfo = _ =>{console.log(this.name)console.log(this.hobby)
}
function Child(name, age) {Parent.call(this. name) // 第二次調用 this.age = age
}
// 繼承Parent的原型  第一次調用 繼承父類的屬性和方法
Child.prototype = new Parent()
// 修復構造函數的指向
Child.prototype.constructor = Child
// 添加自定義方法
Child.prototype.getAge = _=>{console.log(this.age)
}

注:在上述示例中,Child.prototype = new Parent()(即第一次調用)實際上是不必要的,因為它會導致父類的構造函數被不必要的調用,從而繼承父類實例的所有屬性的方法,數據共享。更推薦的做法是使用Object.create(Parent.prototype)來創建子類的原型對象,即寄生組合式繼承

  1. 原型式繼承
    ES5中新增了Object.create()方法規范了原型式繼承。即實現一個對象的繼承,不比創建構造函數
let Parent = {name: '132',hobby: ['吃飯', '睡覺'],getInfo() {console.log(this.name)console.log(this.hobby)}
}
const children1 = Object.create(Parent)
children1.name = '232'
children1.hobby.push('打游戲')
children1.getInfo() // 232 ['吃飯', '睡覺', '打游戲']const children2 = Object.create(Parent)
children2.getInfo() // 132 ['吃飯', '睡覺', '打游戲']
  1. ES6 extend class 關鍵字繼承
    ES6繼承是一種語法糖,先將父類實例對象的屬性和方法,駕到this上(super使用),然后再用子類的構造函數修改this
class Parent() {constructor(name) {this.name = name}sayHello() {console.log(`hello, ${this.name}`)}
}
class Child extend Parent {constructor(name) {super(name);}
}

作用域和作用域鏈

作用域

作用域:即變量和函數有效的區域集合,變量作用域又稱為上下文。換句話說,就是代碼中國變量和其它資源的可見性

分類

  1. 全局作用域
  2. 局部作用域(函數作用域)
  3. 塊級作用域:ES6中國引入了letconst關鍵字,局部訪問。

作用域鏈

當使用一個變量時,js引擎會在當前作用域下尋找該變量,如果沒有找到,則向上查找,知道找到全局作用域下結束。

執行上下文與執行棧

this對象

this關鍵字是函數運行時自動生成的一個內部對象,只能在函數內部使用,總指向調用它的對象

this在函數執行過程中,一旦確定,就不可再更改。

綁定規則

  1. 默認綁定嚴格模式下,不能講全局對象用于默認綁定,this會綁定到undefined
  2. 隱式綁定this永遠指向最后調用它的對象;
  3. new綁定:通過構建函數new關鍵字生成一個實例對象,此時this指向這個實例對象(new過程中如果返回一個對象,this則指向返回的對象;如果返回一個簡單類型數據時,this依舊指向實例對象);
  4. 顯式修改apply(obj)call(obj)bind(obj)是改變函數調用對象的方法。

綁定規則優先級
new綁定 > 顯式綁定 > 隱式綁定 > 默認綁定

applycallbind的區別

applycallbind的作用都是改變函數執行時的上下文(即this指向)。

const name = 'Li'
const obj = {name: 'Bo',sayHello() {console.log(this.name)}
}
obj.sayHello() // 輸出Bo
setTimeout(obj.sayHello, 0) // 輸出Li

上述示例中,由于使用了setTimeout,在回調中執行obj.sayHello(),因此在指向環境回到主棧執行時,實在全局執行上下文中的環境執行的,此時this指向window,因此輸出 Li,因此,需要調整綁定this指向:

setTimeout(obj.sayHello.call(obj), 0)

三者區別

  • apply(obj, arrayArgs):參數以數組形式傳入,改變this指向后原函數會立即執行,且此方法只是臨時改變一次
  • call(obj, listArgs):參數以列表形式傳入,改變this指向后原函數會立即執行,且此方法只是臨時改變一次
  • bind(obj, listArgs):參數以列表形式傳入(可多次傳入)。改變this指向后不會立即執行,而是返回一個永久改變this指向的函數

三者相同點

  1. 三者第一個參數都是this要指向的對象,如果是nullundefined時,默認指向window

手動實現bind

Function.prototype.myBind = function (context) {// 判斷是否是函數if(typeof this !== "function") {throw new TypeError('error')}// 獲取參數const args = [...arguments].slice(1), fn = this;return function Fn () {// 根據調用方式  傳入不同的綁定值return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments))}
}
箭頭函數

ES6中提供了箭頭函數語法,在書寫時候就能確定this指向。

箭頭函數不能作為構建函數。

閉包

在JS中,閉包是指一個函數能夠訪問并操作它外部的變量。閉包的創建主要是在嵌套函數中發生的。及時外部函數執行完畢并返回之后,內部函數依舊可訪問和修改這些變量,只是因為內部函數保持了對外部作用域的引用。

一般函數的詞法環境在函數返回后就被銷毀。但由于閉包保留了對所在詞法環境的數據引用,因此創建時所在執行上下文被銷毀,但創建所在詞法環境依然存在,延長了變量的生命周期。

示例:

const sumFn = _ => {let count = 0return function () {return count += 1}
}
// 創建兩個獨立的計數器
const sum1 = sumFun()
const sum2 = sumFn()
console.log(sum1()) // 1
console.log(sum1()) // 2
console.log(sum2()) // 1
  1. 閉包的特性

    • 保持變量私有;
    • 模擬素有方法;
    • 實現封裝和抽象,使得代碼模塊化;
    • 實現回調和異步操作;
  2. 閉包的用途

    • 創建私有變量
    • 數據封裝和隱私;
    • 模擬類的私有方法和屬性;
    • 實現函數工程;
  3. 閉包的缺點

    • 內存消耗:由于閉包保持了對外部變量的引用,因此可能會造成內存消耗增加,甚至內存泄漏;
    • 性能考慮:閉包可能會比普通函數的調用要稍慢一些。
  4. 其它
    在創建新的對象或者類時,方法通常應該關聯于對象的原型,而不是定義在對象的構造器中,原因:每個對象的創建,方法都會被重新賦值

function Obj(name, age) {this.name = namethis.age = age
}
Obj.prototype.getName = function() {return this.name
}
Obj.prototype.getAge = function() {return this.age
}

執行上下文

執行上下文就是代碼的執行環境。分為:

  • 全局執行上下文: window
  • 函數執行上下文:只有在函數被調用的時候才會被創建
  • Eval函數執行上下文:Eval函數中的代碼
生命周期

函數執行上下文的生命周期包括3個階段:

  • 創建節點:確定this指向
  • 執行階段:執行變量賦值、代碼執行(找不到值,則分配undefined
  • 回收階段:執行上下文出棧,等待虛擬機回收執行上下文
變量提升

變量提升:由執行上下文和作用域的工作原理決定的。在JS代碼執行前,解析器會首先解析代碼,找出所有的變量聲明(var關鍵字聲明),然后在執行之前,講這些變量提升在其所在作用域的最頂端,這個過程就是變量提升

變量提升的實際原因

  1. 編譯階段與執行階段分離:編譯階段,代碼解析,變量和函數聲明被提升到作用域頂部;執行階段,代碼按照編寫順序執行;
  2. 作用域決定:在JS中,作用域由函數決定;

注意

  1. 只有聲明本身會被提升,賦值或者其它邏輯操作依舊在原處執行;
  2. 使用letconst關鍵字聲明的變量不會被提升(ES6處理);

執行棧(調用棧 - 先進后出結構)

執行棧:用于存儲代碼執行階段創建的所有執行上下文。先進后出,從上到下,創建對應的函數執行上下文冰牙人員棧,執行完成被推出,直至結束。

new操作符具體實現

在JS中,new操作符用于創建一個給定構造函數的實例對象。

流程實現

  • 創建一個新的object
  • 將對象與構造函數通過原型鏈連接起來
  • 將構造函數中的this綁定道新建的對象object
  • 根據構建函數返回類型做判斷:原始值責備忽略,對象則需要正常處理使用。
function Person(name, age) {this.name = namethis.age = age
}
const per = new Person('haa', 33)
// 構造函數沒有return語句  則將新創建的對象返回
console.log(per) //Person {name: 'haa', age: 33}

手寫new操作符

function NNew(Func, ...args) {const obj = {}// 將新對象原型指向構造函數原型對象obj.__proto__ = Func.prototypelet res = Func.apply(obj, args)returm res instanceof Object ? res : obj
}

JS的事件模型

事件:HTML文檔或瀏覽器中發生的一種交互操作;
事件流:父子節點事件綁定,觸發順序

事件流

事件流的三個階段

  1. 事件捕獲階段從上到下依次觸發;
  2. 處于目標階段
  3. 事件冒泡階段:**從下(觸發節點)往上(DOM的最高層父節點)**的傳播方式。

事件模型

分類

  1. 原始事件模型(DOM0級)
    • 綁定速度快
    • 只支持冒泡,不支持捕獲
    • 同一個事件類型只能綁定一次,后綁定會覆蓋之前的;
    • 刪除事件處理,賦值為null即可
  2. 標準事件模型(DOM2級)

標準事件模型中,一次事件共有三個過程:

  • 事件捕獲:從document一直向下傳播到目標元素
  • 事件處理:觸發目標元素的監聽元素
  • 事件冒泡:從目標元素冒泡到document,依次檢查和執行相關的監聽函數

添加事件監聽:addEventListener(evt, handler, userCapture)
移除事件監聽:removeEventListener(evt, handler, userCapture)
useCapture默認為false,表示在冒泡過程中執行,設置為true,表示在捕獲過程中執行

  1. IE事件模型

IE事件模型共有兩個過程:

  1. 事件處理階段:事件到達目標元素,觸發監聽函數;
  2. 事件冒泡階段:冒泡到document,過程中檢查和執行相關監聽函數;

添加事件監聽:attachEvent(evt, handler)
移除事件監聽:detachEvent(evt, handler)

事件代理(事件委托)

事件代理,就是把一個或一組元素的響應事件的函數委托給外層的元素完成,其在冒泡階段完成。

應用場景
通常用于動態綁定,減少重復工作。列表項操作(增、刪、改、查)示例:

const ulDom = document.getElementById('ulDom')
ulDom.onclick = function (evt) {evt = evt || window.eventconst target = evt.target || evt.srcElemnentif(target.nodeName.toLowerCase() === 'li') {console.log(target.innerText)}
}

事件委托的局限性

  • focusbour等沒有冒泡機制的事件,無法使用;
  • mousemovemouseout這些只能不斷通過位置計算定位的,對性能消耗高,不適合事件委托;

事件循環

JS是一門單線程語言,實現單線程非阻塞的方法就是事件循環。主要實現:同步任務進入主線程(主執行棧),異步任務進入任務隊列(先進先出)。主線程內的任務執行完畢為空,則會去任務隊列讀取對應的任務,推入主線程執行,不斷重復。

在JS中,所有的任務都可以分為:

  • 同步任務:一般會直接進入主線程執行;
  • 異步任務:比如ajax請求、定時器函數等。

宏任務與微任務

微任務

微任務:一個需要異步執行的函數,執行時機:主函數執行結束之后、當前宏任務執行結束之前

常見微任務

  • Promise.then()
  • MutationOberserve
  • Proxy
  • nextTick()

宏任務

宏任務的時間粒度比較大,執行事件間隔是不能精確控制的。

常見宏任務

  • script
  • setTimeoutsetInterval
  • UI事件
  • postMessageMessageChanel
  • setImmediate、I/O等

關系圖
在這里插入圖片描述
按照關系圖,執行機制:

  • 執行一個宏任務,如果遇到微任務則先將它推入微任務的事件隊列中;
  • 當前宏任務執行完,查看微任務的事件隊列,將里面的任務依次執行,再繼續執行下一個宏任務。

示例:

console.log(1)setTimeout(_=>{console,log(2)
}, 0)new Promise((res, rej) => {console.log('Promise')resolve()
}).then(_=>{console.log('then')
})
console.log(3)
// 執行后  輸出
1
Promise
3
then
2 // setTimeout術語新的宏任務,所以要等上一個微任務列表執行完后再執行

asyncawait

async:異步的意思,await可以理解為async await,即等待異步方法執行,阻塞后面的代碼。

async

async函數返回一個Promise對象。以下示例,兩種實現是等效的:

function fnc() {return Promise.resolve('123')
}async function() {return '123'
}

await

正常情況下,await后是一個Promise對象,如果不是,則直接返回對應的值。函數運行,遇到await,則會阻塞下面的代碼(加入微任務列表),跳出去執行同步代碼。

async function func() {return await 123
}
func().then(val => console.log(val)) // 123

函數緩存

函數緩存,就是將函數運算過的結果進行緩存。

實現函數緩存主要依靠閉包、科利華、高階函數等。

柯里化:把接受多個參數的函數轉換為接受一個單一參數的函數

const add = (x) => {return function (y) {return x+ y}
}
// 使用
add(2)(3) // 5

JS本地存儲

JS的本地存儲方式主要有:

  1. cookie
  2. sessionStorage
  3. localStorage
  4. indexedDB

Cookie

cookie,類型為小型文本文件,指某些網站為了辨別用戶身份而存儲在用戶本地終端上的數據。是為了解決HTTP無狀態導致的問題。

cookie一般大小不超過4KB,由key-value形式存儲,還包含一些有效期、安全性、適用范圍的可選屬性。

Cookie每次請求都會被發送。修改Cookie,必須保證Domain和Path的值相同。刪除Cookie,一般通過設置一個過期時間,使其從瀏覽器上刪除。

  • Expires:過期時間
  • Domain:主機名
  • Path:要求請求的資源路徑必須帶上這個URL路徑,才可以發送Cookie受不
  • 標記為Secure的cookie只通過HTTPS協議加密的請求發送給服務端。

localStorage

特點

  1. 持久化存儲,除非主動刪除,否則不會過期;
  2. 存儲信息在同一域下是共享的;
  3. 當前頁進行 localStorage的增刪改的時候,本頁不會觸發storage事件,只會在其它頁面觸發;
  4. 大小:5M;
  5. 本質上就是字符串的讀取,內容過多會消耗內存空間,導致頁面卡頓;
  6. 受同源策略的限制

使用

  • localStorage.setItem(key, value)
  • localStorage.getItem(key)
  • localStorage.key(num):獲取第num個鍵名
  • localStorage.removeItem(key)
  • localStorage.clear()

sessionStorage

特點

  1. 會話級別存儲,關閉頁面則自動清除數據;
  2. 存儲信息在同一域下是共享的;
  3. 當前頁進行 sessionStorage的增刪改的時候,本頁不會觸發storage事件,只會在其它頁面觸發;
  4. 大小:5M;
  5. 本質上就是字符串的讀取,內容過多會消耗內存空間,導致頁面卡頓;
  6. 受同源策略的限制

使用

  • sessionStorage.setItem(key, value)
  • sessionStorage.getItem(key)
  • sessionStorage.key(num):獲取第num個鍵名
  • sessionStorage.removeItem(key)
  • sessionStorage.clear()

應用場景

  • 標記用戶與跟蹤用戶行為,使用cookie
  • 適合長期保存在本地的數據(令牌),使用localStorage
  • 敏感賬號一次性登錄,使用sessionStorage
  • 存儲大量數據、在線文檔保存編輯歷史的情況,使用indexDB

大文件的斷點續傳

分片上傳

分片上傳,就是將上傳的文件,按照一定的大小等分割規則,將整個文件分割成多個數據塊(chunk),來進行分片上傳,上傳完成后,再由服務端對所有分片進行匯總整合拼接,生成原始的文件。

斷點續傳

斷點續傳,就是在上傳或下載時,將上傳或下載任務人為的劃分為幾個部分。每一個部分采用一個線程來進行。如果遇到網絡故障,可以從已經完成的部分處開始繼續上傳或下載未完成的部分。節省時間,提高速度。

實現的兩種方式:

  1. 服務端返回,告訴從哪開始;
  2. 瀏覽器端自行處理。

上傳或下載過程中在服務器謝偉臨時文件,處理完成后,再將此文件重命名為正式文件即可。

實現思路
拿到文件,保存文件唯一標識,切割,分段上傳。每次上傳一段,根據唯一標識判斷此次上傳進度,直到全部文件上傳完畢。對于上傳失敗、上傳過程中刷星頁面等情況,處理的一種常見方法是使用瀏覽器的存儲機制(如localStorage、sessionStorage、IndexedDB或Cookies)來保存上傳進度和已上傳的文件塊信息。

function uploadFile(file, chunkSize = 1024 * 1024) {  const totalChunks = Math.ceil(file.size / chunkSize);  for (let index = 0; index < totalChunks; index++) {  const chunk = file.slice(index * chunkSize, (index + 1) * chunkSize);  const formData = new FormData();  formData.append('file', chunk);  formData.append('index', index);  formData.append('totalChunks', totalChunks);  fetch('/upload', {  method: 'POST',  body: formData  })  .then(response => response.json())  .then(data => {  console.log('Chunk uploaded', data);  })  .catch(error => {  console.error('Error uploading chunk:', error);  // 保存已上傳的塊信息  });  }  
}

使用場景

  1. 大文件加速上傳
  2. 流式文件上傳
  3. 網絡環境不太行

使用Web worker處理大文件上傳

  1. 新建 fileUploader.js文件(web worker)
self.onmessage = function(e) {  const { file, chunkSize } = e.data;  const totalChunks = Math.ceil(file.size / chunkSize);  for (let index = 0; index < totalChunks; index++) {  const chunk = file.slice(index * chunkSize, (index + 1) * chunkSize);  const formData = new FormData();  formData.append('file', chunk);  formData.append('index', index);  formData.append('totalChunks', totalChunks);  // 假設你有一個上傳函數  uploadChunk(formData, index).then(() => {  // 通知主線程該塊已上傳  self.postMessage({ index: index, status: 'success' });  }).catch(error => {  // 通知主線程上傳失敗  self.postMessage({ index: index, status: 'error', error: error.message });  });  }  // 假設的上傳函數(需要替換為實際的API調用)  function uploadChunk(formData, index) {  return new Promise((resolve, reject) => {  // 使用fetch或其他HTTP客戶端發送formData  // 這里只是模擬  setTimeout(() => {  if (Math.random() > 0.5) {  resolve();  } else {  reject(new Error('Upload failed for chunk ' + index));  }  }, 1000);  });  }  
};

主文件(index.js)中,創建Worker,并將文件數據和其它必要數據發送給Worker:

if (window.Worker) {  const worker = new Worker('fileUploader.js');  // 假設你有一個文件輸入元素  const fileInput = document.querySelector('input[type="file"]');  fileInput.addEventListener('change', function(e) {  const file = e.target.files[0];  const chunkSize = 1024 * 1024; // 1MB  // 發送文件和塊大小到Worker  worker.postMessage({ file: file, chunkSize: chunkSize });  // 監聽來自Worker的消息  worker.onmessage = function(e) {  console.log('Chunk ' + e.data.index + ' uploaded with status: ' + e.data.status);  if (e.data.status === 'error') {  console.error('Error:', e.data.error);  }  };  // 監聽Worker的錯誤  worker.onerror = function(error) {  console.error('Worker error:', error);  };  });  
} else {  console.log('Your browser doesn\'t support web workers.');  
}

ajax

Ajax:即異步的JS和XML,可以在不重新加載整個網頁的情況下,與服務器交換數據,并且更新部分網頁。

Ajax的原理:通過XMLHttpRequest對象向服務器發送異步請求,從服務器獲取數據,然后用JS來操作DOM而更新頁面。

創建Ajax異步交互需要服務器邏輯進行配合,完成如下步驟:

  1. 創建 Ajax 的核心對象:XMLHttpRequest;
  2. 通過XMLHttpRequest對象的open()方法與服務器建立連接;
  3. 構建請求所需的數據內容,并通過XMLHttpRequest對象的send方法發送給服務器端
  4. 通過XMLHttpRequest對象提供的onreadystatechange事件監聽服務器端的通信狀態(XMLHttpRequest.readyState
  5. 接受并處理服務器向客戶端響應的數據結果
  6. 將處理結果更新到HTML中

封裝:

function ajaxReq (options) {options = options || {}options.type = (options.type || 'GET').toUpperCase()options.dataType = options.dataType || 'json'const datas = options.dataconst xhr = new XMLHttpRequest()if(options.type === 'GET') {xhr.open('GET', `${options.url}?${params}`, true)xhr.send()} else if(options.type === 'POST'){xhr.open('POST', options.url, true)xhr.send(params)}// 監聽服務器的通信狀態xhr.onreadychange = function(e) {if(xhr.readyState === 4) { // 請求完成if(xhr.status >=200 && xhr.status < 300) {options.success && options.success(xhr.responseText, xhr.responseXML)} else {options.fail && options.fail(xhr.status)}}}
}
// 使用
ajaxReq({type: 'get',datas: {id: 1},url:'https:xxx',success: (text, xml) => {console.log(text)},fail: status => {console.log(status)}
})

防抖和節流

防抖節流本質上就是優化高頻率執行代碼的一種手段,減少調用頻率,優化體驗。

  • 防抖(debounce):n秒后再執行該事件,如在n秒內被重復觸發,則重新計時(示例:電梯關門,計時關門)。確保事件處理函數在最后一次事件觸發后的一段時間內才執行,通常用于用戶輸入、驗證碼輸入驗證等場景;
  • 節流(throttle):n秒內只運行一次,如在n秒內重復觸發,只有一次生效(示例:火車發車,要準點)。確保事件處理函數在固定時間間隔內只執行一次,通常應用于滾動、搜索聯想等;

代碼實現

  1. 防抖:n秒后執行
function debounce(func, wait, immediate) {let timeout;return function (...args) {let context = thisif(timeout) clearTimeout(timeout)if(immediate) {let callNow = !timeout // 第一次會立即執行,后續觸發才執行timeout = setTimeout(function() {timeout = null}, wait)if(callNow) {func.apply(context, args)}} else {timeout = setTimeout(function () {func.apply(context, args)}, wait)}}
}
  1. 節流:n秒內僅執行一次
function throttled(fn, delay = 500) {let timer = nullreturn function(...args) {if(!timer) {timer = setTimeout(_ => {fn.apply(this, args)timer = null}, delay)}}
}

單點登錄

單點登錄,是目前比較流行的企業業務整合的解決方案之一。

SSO的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。

SSO一般需要一個獨立的認證中心,子系統的登錄均需要通過認證中心,本身不參與登錄操作。

當一個系統成功登錄以后,認證中心會頒發一個令牌給各個子系統,子系統拿著令牌去獲取各自受保護的資源。為了減少頻繁驗證,一般在授權以后,一定時間內無需再發起認證。

實現方式

  1. 同域名下的單點登錄:使用Cookie,即將Cookiedomain設置為當前域的父域,并且服務的Cookie會被子域共享。path默認為web應用的上下文路徑。
  2. 不同域名下的單點登錄(兩種方法均支持跨域):
    • 方法一:認證中心進行登錄,登錄成功,將token寫入Cookie。應用系統檢查當前請求是否有token,沒有則跳轉登錄,有則將當前token寫入當前應用系統的Cookie,訪問放行。
    • 方法二:將認證中心的token保存在localStorage中,前端每次請求,都主動將localStorage的數據傳給服務端。前端拿到token后,除了寫入自己的localStorage中,還可以通過特殊手段寫入其他域的localStorage中(iframe + postMessage)。

上拉加載、下拉刷新

上拉加載的本質就是頁面觸底時機,自動觸發加載請求。

頁面觸底公式:

const scrollTop = document.documentElement.scrollTop
const scrollHeight = document.body.scrollHeight
const clientHeight = document.documentElement.clientHeight // 瀏覽器高度// 距離底部還有50的時候就可以開始觸發
const isPut = (scrollTop + clientHeight) >= (scrollHeight - 50)

下拉刷新的本質就是頁面本身置于頂部時,用戶下拉觸發操作。

正則表達式

遇到特殊字符,需要使用\轉義哦~

構建正則表達式的方式

  1. 字面量創建,其包含在斜杠之間:/\d+/g
  2. 調用RegExp對象的構造函數:new RegExp("\\d+", g)

匹配規則

  • ^:匹配輸入開始
  • $:匹配輸入結束
  • *:匹配前一個表達式0次或毒刺
  • +:匹配前一個表達式1次或多次,等價于{1,}
  • ?:匹配前一個表達式0次或1次,等價于${0, 1}
  • .:匹配除換行符意外的任何單個字符

標記

  • g:全局搜索
  • i:不區分大小寫搜索
  • m:多行搜索

匹配方法

  • 字符串方法:match()matchAll()search()replace()split()
  • 正則對象方法:test()exec()

常用正則

  • 5-20個字符,以字母開頭,可帶數字,以及_、.的字符串:/^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){4, 19}$/

函數式編程

純函數

純函數就是對給定的輸入返回相同輸出的函數,并要求所有的數據都是不可變的,即:純函數=無狀態+數據不可變

高階函數

高階函數,就是以函數作為輸入或者輸出的函數。

高階函數存在緩存的特性,主要是利用了閉包作用。

const doOnce = fn => {let done = falsereturn function() {if(!done) fn.apply(this. fn)else console.log('已處理')done = true}
}

柯里化

柯里化就是把一個多參數函數轉換為一個嵌套的一元函數的過程(惰性執行)。

web攻擊方式

常見攻擊方式

  1. SQL注入:在表單的輸入框中輸入惡意SQL代碼,并通過提交這些字段來執行惡意的SQL語句,從而影響網站的數據庫安全。

    SQL注入,主要是通過將惡意的Sql查詢或添加語句插入到應用的輸入參數中,再在后臺Sql服務器上解析執行進行的攻擊。

    SQL注入預防:

    • 嚴格檢查輸入變量的類型和格式;
    • 過濾和轉義特殊字符;
    • 對訪問數據庫的Web應用采用防火墻等。
  2. 跨站腳本攻擊(XSS):在Web應用中輸入惡意指令代碼到網頁,使用戶加載并執行攻擊者惡意制造的網頁程序。

    分類

    • 存儲型:惡意數據提交,讀取返回,解析執行
    • 反射型:包含惡意代碼的URL,讀取、拼接返回給HTML,解析執行
    • DOM型:包含惡意代碼的URL,前端讀取打開(前端自身漏洞)

    解決辦法

    • 在使用innerHTMLouterHTMLdocument.write()等小心,不要把不可信的HTML插入到頁面上。如果使用Vue/react等技術棧,應在render階段避免 innerHTML 的xss攻擊隱患;
    • DOM中的內聯事件監聽器,如onclick、onload、onmousemove等,a標簽的href屬性,setTimeout()等,都能把字符串作為代碼運行。如果吧不可信的數據拼接到字符串中傳遞給這些API,很容易產生隱患;
  3. 跨站請求偽造(CSRF):攻擊者優導受害者進入第三方網站,在第三方網站中,向被攻擊網站發起跨站請求。

    跨站請求偽造,可以通過 get 請求,即通過訪問img的頁面后,瀏覽器自動訪問目標地址,發送請求。也可以設置一個自動提交的表單發送POST請求。

    CSRF的特點:

    • 攻擊一般發起在第三方網站;
    • 攻擊利用受害者在被攻擊網站的登錄憑證,冒充受害者提交操作;
    • 跨站請求可以用各種方式:圖片URL、超鏈接、Form表單提交等;部分請求方式可以直接嵌入第三方論壇、文章中,難以追蹤。

    CSRF的預防:

    • 阻止不明外域的訪問;
    • 提交時要求附加本域才能獲取的信息(token等);
  4. 文件上傳漏洞:攻擊者可能會利用文件上傳功能將惡意軟件或腳本上傳到目標服務器上,進而執行惡意操作或獲取敏感信息。

  5. 遠程命令執行漏洞:攻擊者可利用該漏洞在受害機上執行任意命令或程序,進一步控制受害機器。

  6. 目錄遍歷攻擊:攻擊者試圖訪問服務器根目錄之外的目錄,并利用特定的符號(如“…/”)來嘗試訪問受限制的目錄或文件。

  7. 信息泄露攻擊:攻擊者可能試圖通過各種方法獲取或猜測敏感信息,如用戶密碼、密鑰等,以便進一步入侵系統。

  8. 會話劫持:攻擊者利用各種技術手段捕獲會話信息,然后冒充合法用戶進行非法操作。

  9. 零日攻擊:黑客利用尚未被公眾發現的漏洞信息進行攻擊,使得目標系統無法防范。

JS內存泄漏

JS的內存泄漏,是指計算處理中,由于疏忽或者錯誤造成程序未能釋放已經不在使用的內存,從而造成內存的浪費。
對于持續運行的服務進程,必須及時釋放不再用到的內存,否則,內存占用越來越高,影響系統性能,甚至導致程序崩潰。

垃圾回收機制

JS具有自動垃圾回收機制,執行環境會負責管理代碼執行過程中使用的內存。

原理:垃圾收集器會定期找出不再繼續使用的變量,然后釋放內存。
實現方式:

  • 標記清除:變量標記進入和離開執行環境,垃圾回收程序會將離開狀態,且無被引用的變量做清理銷毀‘’
  • 引用計數:如果一個值的引用次數為0,就表示這個值不再用了,可以將此銷毀釋放。

常見內存泄漏情況

  • 意外的全局變量(使用嚴格模式可解決)
  • 定時器
  • 閉包
  • 監聽器:addEventListener

JS數字精度丟失

0.1 + 0.3 === 0.4 // false

存儲二進制小數點的偏移量最大為52位,最多可以表達的位數是2^53=90071992547740992,對應科學計數位數是9.0071992547740992,這也是JS最多能表示的精度。他的長度是16,所以可以使用toPrecision(16)來做運算,超過的精度會自動做湊整處理。

要想解決大數的問題,使用第三方庫:bignumber.js,原理是把所有數字當做字符串,重新實現了計算邏輯,缺點就是性能差。

因此,0.1 + 0.3 === 0.4false,主要是因為計算機存儲雙精度浮點數需要先把十進制數轉換為二進制的科學計數法的形式,然后計算機以自己的規則存儲二進制的科學計數法。
由于存儲時有位數限制(64位),并且某些十進制浮點數在轉換為二進制數時會出現無線循環,造成二進制的舍入操作,當再轉換為十進制,就造成了計算誤差。

解決方案
使用toPrecision()湊整,并parseFloat()轉換為數字后顯示

function strip(num, precision = 12) {return +parseFloat(num.toPrecision(precision))
}

或者直接使用第三方庫:Math.jsBigDecimal.js

尾遞歸

數組求和

const sum = (arr, total) => {if(arr.lengh === 1) return totalreturn sum(arr, total + arr.pop())
}

數組扁平化

const flatArr = (arr=[], result=[]) =>{arr.forEach(val => {if(Arrar.isArray(val)) {result = result.concat(flat(val, []))} else result.push(val)})
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/42636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/42636.shtml
英文地址,請注明出處:http://en.pswp.cn/web/42636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

hnust 1965: 深度優先搜索

hnust 1965: 深度優先搜索 題目描述 輸入一個圖&#xff0c;用鄰接矩陣存儲&#xff08;實際上也可以選擇鄰接表&#xff09;&#xff0c;并實現DFSTraverse操作。 拷貝前面已經實現的代碼&#xff0c;主函數必須如下&#xff0c;完成剩下的部分。 int main() { Graph g; Cre…

RTOS系統 -- 調試大法之FreeRTOS在M4上實現coredump功能

FreeRTOS內核崩潰&#xff08;coredump&#xff09;及異常打印技術 技術背景 在嵌入式系統中&#xff0c;FreeRTOS是一款廣泛使用的實時操作系統。FreeRTOS本身并不包含默認的coredump機制&#xff0c;但我們可以通過自定義異常處理函數來實現異常打印和coredump功能。通過捕…

了解PPO算法(Proximal Policy Optimization)

Proximal Policy Optimization (PPO) 是一種強化學習算法&#xff0c;由 OpenAI 提出&#xff0c;旨在解決傳統策略梯度方法中策略更新過大的問題。PPO 通過引入限制策略更新范圍的機制&#xff0c;在保證收斂性的同時提高了算法的穩定性和效率。 PPO算法原理 PPO 算法的核心…

Oracle數據庫自帶的內置表和視圖、常用內部視圖

文章目錄 一.Oracle數據庫自帶的內置表和視圖1.dba_開頭表2.user_開頭表3.v$開頭表4.all_開頭表5.session_開頭表6.index_開頭表 三.按組分的幾組重要的性能視圖1.System的over view2.某個session的當前情況3.SQL的情況4.Latch/lock/ENQUEUE5.IO方面的 分類類別關系群集、表、視…

【docker 把系統盤空間耗沒了!】windows11 更改 ubuntu 子系統存儲位置

系統&#xff1a;win11 ubuntu 22 子系統&#xff0c;docker 出現問題&#xff1a;系統盤突然沒空間了&#xff0c;一片紅 經過排查&#xff0c;發現 AppData\Local\packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\ 這個文件夾竟然有 90GB 下面提供解決辦法 步…

Spring-AOP(二)

作者&#xff1a;月下山川 公眾號&#xff1a;月下山川 1、什么是AOP AOP&#xff08;Aspect Oriented Programming&#xff09;是一種設計思想&#xff0c;是軟件設計領域中的面向切面編程&#xff0c;它是面向對象編程的一種補充和完善&#xff0c;它以通過預編譯方式和運行期…

【課程總結】Day13(下):人臉識別和MTCNN模型

前言 在上一章課程【課程總結】Day13(上):使用YOLO進行目標檢測,我們了解到目標檢測有兩種策略,一種是以YOLO為代表的策略:特征提取→切片→分類回歸;另外一種是以MTCNN為代表的策略:先圖像切片→特征提取→分類和回歸。因此,本章內容將深入了解MTCNN模型,包括:MTC…

CountDownLatch 是 Java 中的一個同步輔助工具類

下面是一個使用 CountDownLatch 的案例分析&#xff0c;我們將通過一個簡單的示例來展示如何使用 CountDownLatch 來同步多個線程的操作。 ### 場景描述 假設我們有一個任務&#xff0c;需要從多個數據源&#xff08;比如多個數據庫表或文件&#xff09;中讀取數據&#xff0c…

使用jdk11運行javafx程序和jdk11打包jre包含javafx模塊

我們都知道jdk11是移除了javafx的,如果需要使用javafx,需要單獨下載。 這就導致我們使用javafx開發的桌面程序使用jdk11時提示缺少javafx依賴。但這是可以通過下面的方法解決。 一,使用jdk11運行javafx程序 我們可以通過設置vmOptions來使用jdk11運行javafx程序 1,添加j…

【RAG KG】GraphRAG開源:查詢聚焦摘要的圖RAG方法

前言 傳統的 RAG 方法在處理針對整個文本語料庫的全局性問題時存在不足&#xff0c;例如查詢&#xff1a;“數據中的前 5 個主題是什么&#xff1f;” 對于此類問題&#xff0c;是因為這類問題本質上是查詢聚焦的摘要&#xff08;Query-Focused Summarization, QFS&#xff09…

嵌入式單片機,兩者有什么關聯又有什么區別?

在開始前剛好我有一些資料&#xff0c;是我根據網友給的問題精心整理了一份「嵌入式的資料從專業入門到高級教程」&#xff0c; 點個關注在評論區回復“666”之后私信回復“666”&#xff0c;全部無償共享給大家&#xff01;&#xff01;&#xff01;使用單片機是嵌入式系統的…

iOS 國際化語言第一語言不支持時候默認語言強轉英文

對bundle擴展 直接貼代碼 .h文件 // // NSBundleKdLocalBundle.h // QooCam // // Created by bob bob on 2023/9/8.//#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGINinterface NSBundle (KdLocalBundle)end interface KdLocalBundle:NSBundleend interf…

CurrentHashMap巧妙利用位運算獲取數組指定下標元素

先來了解一下數組對象在堆中的存儲形式【數組長度&#xff0c;數組元素類型信息等】 【存放元素對象的空間】 Ma 基礎信息實例數據內存填充Mark Word,ClassPointer,數組長度第一個元素第二個元素固定的填充內容 所以我們想要獲取某個下標的元素首先要獲取這個元素的起始位置…

軟件工程常見知識點

下午收到字節日常實習的面試邀請&#xff0c;希望這次能有一個好的表現。言歸正傳&#xff0c;郵件中提到這些問題&#xff0c;我這邊借了書并查了網上的資料&#xff0c;做一個提前準備。 軟件工程核心概念&#xff1a; 如何從一個需求落實到一個系統設計&#xff1f; 經過我…

c++ primer plus 第15章友,異常和其他:異常,15.3.7 其他異常特性

c primer plus 第15章友&#xff0c;異常和其他&#xff1a;異常,15.3.7 其他異常特性 c primer plus 第15章友&#xff0c;異常和其他&#xff1a;異常,15.3.7 其他異常特性 文章目錄 c primer plus 第15章友&#xff0c;異常和其他&#xff1a;異常,15.3.7 其他異常特性 15.…

Sorted Set 類型命令(命令語法、操作演示、命令返回值、時間復雜度、注意事項)

Sorted Set 類型 文章目錄 Sorted Set 類型zadd 命令zrange 命令zcard 命令zcount 命令zrevrange 命令zrangebyscore 命令zpopmax 命令bzpopmax 命令zpopmin 命令bzpopmin 命令zrank 命令zscore 命令zrem 命令zremrangebyrank 命令zremrangebyscore 命令zincrby 命令zinterstor…

線程池案例

秒殺 需求 10個禮物20個客戶搶隨機10個客戶獲取禮物&#xff0c;另外10無法獲取禮物 任務類 記得給共享資源加鎖 public class MyTask implements Runnable{// 禮物列表private ArrayList<String> gifts ;// 用戶名private String username;public MyTask( String user…

android Dialog全屏沉浸式狀態欄實現

在Android中&#xff0c;創建沉浸式狀態欄通常意味著讓狀態欄背景與應用的主題顏色一致&#xff0c;并且讓對話框在狀態欄下面顯示&#xff0c;而不是浮動。為了實現這一點&#xff0c;你可以使用以下代碼片段&#xff1a; 1、實際效果圖&#xff1a; 2、代碼實現&#xff1a;…

揭秘GPT-4o:未來智能的曙光

引言 近年來&#xff0c;人工智能&#xff08;AI&#xff09;的發展突飛猛進&#xff0c;尤其是自然語言處理&#xff08;NLP&#xff09;領域的進步&#xff0c;更是引人注目。在這一背景下&#xff0c;OpenAI發布的GPT系列模型成為了焦點。本文將詳細探討最新的模型GPT-4o&a…

Unity海面效果——6、反射和高光

Unity引擎制作海面效果 大家好&#xff0c;我是阿趙。 上一篇的結束時&#xff0c;海面效果已經做成這樣了&#xff1a; 這個Shader的復雜程度已經比較高了&#xff1a; 不過還有一些美中不足的地方。 1、 海平面沒有反射到天空球 2、 在近岸邊看得到水底的部分&#xff0c;水…