Javascript/ES6+/Typescript重點內容篇——手撕(待總結)

在這里插入圖片描述

前端核心知識點梳理與面試題詳解

1. Promise

核心知識點

  • Promise 是異步編程的解決方案,用于處理異步操作
  • 三種狀態:pending(進行中)、fulfilled(已成功)、rejected(已失敗)
  • 狀態一旦改變就不會再變,從 pending 到 fulfilled 或 rejected
  • 常用方法:then()、catch()、finally()、all()、race()、allSettled() 等

面試題:實現 Promise.all

Promise.myAll = function(promises) {return new Promise((resolve, reject) => {// 判斷傳入的是否是可迭代對象if (!Array.isArray(promises)) {return reject(new TypeError('The input must be an array'));}const results = [];let completedCount = 0;const total = promises.length;if (total === 0) {return resolve(results);}promises.forEach((promise, index) => {// 確保每個元素都是 Promise 對象Promise.resolve(promise).then((value) => {results[index] = value;completedCount++;// 所有 Promise 都成功時才 resolveif (completedCount === total) {resolve(results);}},(reason) => {// 有一個 Promise 失敗就立即 rejectreject(reason);});});});
};

面試題:實現 Promise 串行執行

// 實現一個函數,讓多個Promise按順序執行
function promiseSerial(tasks) {// 初始返回一個已 resolved 的 Promisereturn tasks.reduce((prev, current) => {return prev.then((results) => {// 等待當前任務執行完成return current().then((result) => {// 將結果添加到數組中return [...results, result];});});}, Promise.resolve([]));
}// 使用示例
const createTask = (time, value) => {return () => new Promise(resolve => {setTimeout(() => {console.log(value);resolve(value);}, time);});
};const tasks = [createTask(1000, '任務1'),createTask(500, '任務2'),createTask(800, '任務3')
];promiseSerial(tasks).then(results => {console.log('所有任務完成:', results);
});

2. 原型鏈

核心知識點

  • 每個對象都有 __proto__ 屬性,指向其構造函數的 prototype
  • 構造函數有 prototype 屬性,是其實例的原型
  • 原型鏈是由 __proto__ 連接而成的鏈式結構
  • 當訪問對象的屬性或方法時,會先在自身查找,找不到則沿原型鏈向上查找
  • Object.prototype 是原型鏈的終點,其 __proto__ 為 null

面試題:原型鏈相關輸出題

function Foo() {getName = function() { console.log(1); };return this;
}Foo.getName = function() { console.log(2); };Foo.prototype.getName = function() { console.log(3); };var getName = function() { console.log(4); };function getName() { console.log(5); }// 以下輸出結果是什么?
Foo.getName();          // 2 - 訪問Foo的靜態方法
getName();              // 4 - 函數聲明提升后被變量聲明覆蓋
Foo().getName();        // 1 - Foo()執行后修改了全局getName
getName();              // 1 - 已經被Foo()修改
new Foo.getName();      // 2 - 優先級問題,相當于new (Foo.getName)()
new Foo().getName();    // 3 - 實例化后訪問原型上的方法
new new Foo().getName();// 3 - 先實例化Foo,再調用其原型上的getName并實例化

面試題:實現 instanceof

function myInstanceof(left, right) {// 基本類型直接返回falseif (typeof left !== 'object' || left === null) return false;// 獲取對象的原型let proto = Object.getPrototypeOf(left);// 遍歷原型鏈while (true) {// 找到盡頭仍未匹配,返回falseif (proto === null) return false;// 找到匹配的原型,返回trueif (proto === right.prototype) return true;// 繼續向上查找proto = Object.getPrototypeOf(proto);}
}// 測試
function Person() {}
const p = new Person();
console.log(myInstanceof(p, Person)); // true
console.log(myInstanceof(p, Object)); // true
console.log(myInstanceof(p, Array));  // false
console.log(myInstanceof([], Array)); // true

3. 生成器(Generator)

核心知識點

  • 生成器函數使用 function* 聲明,內部使用 yield 關鍵字
  • 調用生成器函數返回迭代器對象,而非直接執行函數體
  • 通過迭代器的 next() 方法控制函數執行,每次遇到 yield 暫停
  • next() 方法返回包含 valuedone 屬性的對象
  • 可用于實現異步操作的同步化表達、自定義迭代器等

面試題:使用 Generator 實現異步操作

// 模擬異步操作
function fetchData(url) {return new Promise((resolve) => {setTimeout(() => {resolve(`數據: ${url}`);}, 1000);});
}// 使用Generator處理異步
function* getData() {console.log('開始請求數據1');const data1 = yield fetchData('url1');console.log('獲取到數據1:', data1);console.log('開始請求數據2');const data2 = yield fetchData('url2');console.log('獲取到數據2:', data2);return '所有數據獲取完成';
}// 執行生成器
function runGenerator(gen) {const iterator = gen();function handleResult(result) {if (result.done) {console.log('最終結果:', result.value);return;}// 處理Promiseresult.value.then(data => {handleResult(iterator.next(data));});}handleResult(iterator.next());
}// 運行
runGenerator(getData);

4. 閉包

核心知識點

  • 閉包是函數及其捆綁的周邊環境狀態的引用的組合
  • 形成條件:函數嵌套、內部函數引用外部函數的變量、內部函數被外部引用
  • 作用:實現私有變量、模塊化、柯里化、保存狀態等
  • 注意:不當使用可能導致內存泄漏

面試題:實現防抖函數

function debounce(fn, delay, immediate = false) {let timer = null;let isInvoked = false;// 返回閉包函數return function(...args) {const context = this;// 如果存在定時器,清除它if (timer) {clearTimeout(timer);}// 立即執行的情況if (immediate && !isInvoked) {fn.apply(context, args);isInvoked = true;} else {// 延遲執行timer = setTimeout(() => {fn.apply(context, args);isInvoked = false;timer = null;}, delay);}};
}// 使用示例
const handleSearch = (keyword) => {console.log('搜索:', keyword);
};// 防抖處理,延遲500ms執行,不立即執行
const debouncedSearch = debounce(handleSearch, 500);// 模擬頻繁調用
debouncedSearch('a');
debouncedSearch('ab');
debouncedSearch('abc'); // 只有最后一次會在500ms后執行

面試題:實現節流函數

function throttle(fn, interval) {let lastTime = 0;let timer = null;return function(...args) {const context = this;const now = Date.now();const remainingTime = interval - (now - lastTime);// 如果時間間隔已到,直接執行if (remainingTime <= 0) {if (timer) {clearTimeout(timer);timer = null;}fn.apply(context, args);lastTime = now;} else if (!timer) {// 否則設置定時器,確保最后一次一定會執行timer = setTimeout(() => {fn.apply(context, args);lastTime = Date.now();timer = null;}, remainingTime);}};
}// 使用示例
const handleScroll = () => {console.log('滾動事件觸發');
};// 節流處理,每100ms最多執行一次
const throttledScroll = throttle(handleScroll, 100);// 監聽滾動事件
window.addEventListener('scroll', throttledScroll);

5. 異步與事件循環

核心知識點

  • JavaScript 是單線程語言,通過事件循環實現異步
  • 事件循環:調用棧 -> 微任務隊列 -> 宏任務隊列 -> UI渲染
  • 微任務優先級高于宏任務,常見微任務:Promise.then/catch/finally、process.nextTick、MutationObserver
  • 常見宏任務:setTimeout、setInterval、DOM事件、I/O操作、setImmediate

面試題:事件循環輸出題

console.log('1');setTimeout(function() {console.log('2');new Promise(function(resolve) {console.log('3');resolve();}).then(function() {console.log('4');});
}, 0);new Promise(function(resolve) {console.log('5');resolve();
}).then(function() {console.log('6');
});setTimeout(function() {console.log('7');new Promise(function(resolve) {console.log('8');resolve();}).then(function() {console.log('9');});
}, 0);console.log('10');// 輸出順序:1 5 10 6 2 3 4 7 8 9

6. Map

核心知識點

  • Map 是 ES6 新增的鍵值對集合
  • 與對象相比,Map 的鍵可以是任意類型,而對象的鍵只能是字符串或 Symbol
  • 常用方法:set()、get()、has()、delete()、clear()、size 屬性
  • 可以通過 for…of 直接迭代,迭代順序是插入順序
  • 適合用于頻繁添加/刪除鍵值對的場景

面試題:實現 Map 的基本功能

class MyMap {constructor() {// 存儲鍵值對的數組this.items = [];}// 向Map中添加元素set(key, value) {// 檢查鍵是否已存在const index = this.items.findIndex(item => this.isEqual(item.key, key));if (index !== -1) {// 鍵存在則更新值this.items[index].value = value;} else {// 鍵不存在則添加新鍵值對this.items.push({ key, value });}return this;}// 獲取指定鍵的值get(key) {const item = this.items.find(item => this.isEqual(item.key, key));return item ? item.value : undefined;}// 檢查是否包含指定鍵has(key) {return this.items.some(item => this.isEqual(item.key, key));}// 刪除指定鍵delete(key) {const index = this.items.findIndex(item => this.isEqual(item.key, key));if (index !== -1) {this.items.splice(index, 1);return true;}return false;}// 清空Mapclear() {this.items = [];}// 獲取Map的大小get size() {return this.items.length;}// 判斷兩個鍵是否相等isEqual(a, b) {// 處理NaN的情況,NaN !== NaN但在Map中視為相等if (a !== a && b !== b) return true;// 處理+0和-0的情況,在Map中視為相等if (a === 0 && b === 0) return 1 / a === 1 / b;return a === b;}// 迭代器,用于for...of循環*[Symbol.iterator]() {for (const item of this.items) {yield [item.key, item.value];}}
}// 使用示例
const map = new MyMap();
map.set('name', '張三');
map.set(1, '數字1');
map.set(NaN, 'NaN值');
console.log(map.get('name')); // 張三
console.log(map.size); // 3
console.log(map.has(NaN)); // truefor (const [key, value] of map) {console.log(key, value);
}

7. 數組

核心知識點

  • 數組是有序的元素集合,具有動態長度
  • 常用方法:push()、pop()、shift()、unshift()、slice()、splice() 等
  • 高階函數:map()、filter()、reduce()、forEach()、find()、some()、every() 等
  • 數組去重、扁平化、排序是常見操作

面試題:數組扁平化

// 方法1:使用遞歸
function flatten(arr, depth = Infinity) {if (depth <= 0) return arr.slice();return arr.reduce((acc, item) => {if (Array.isArray(item)) {// 遞歸扁平化,并減少深度return acc.concat(flatten(item, depth - 1));} else {return acc.concat(item);}}, []);
}// 方法2:使用ES6的flat方法
// const flattened = arr.flat(depth);// 測試
const nestedArray = [1, [2, [3, [4]], 5]];
console.log(flatten(nestedArray)); // [1, 2, 3, 4, 5]
console.log(flatten(nestedArray, 1)); // [1, 2, [3, [4]], 5]

面試題:數組去重

// 方法1:使用Set
function unique1(arr) {return [...new Set(arr)];
}// 方法2:使用filter和indexOf
function unique2(arr) {return arr.filter((item, index) => {return arr.indexOf(item) === index;});
}// 方法3:使用對象鍵值對
function unique3(arr) {const obj = {};return arr.filter(item => {// 處理不同類型但值相同的情況,如1和'1'const key = typeof item + item;if (!obj[key]) {obj[key] = true;return true;}return false;});
}// 方法4:使用Map
function unique4(arr) {const map = new Map();return arr.filter(item => {if (!map.has(item)) {map.set(item, true);return true;}return false;});
}// 測試
const testArray = [1, 2, 2, '3', '3', true, true, false, false, null, null, undefined, undefined, NaN, NaN];
console.log(unique1(testArray)); // Set方法能正確去重NaN
console.log(unique2(testArray)); // indexOf無法識別NaN,會保留重復的NaN
console.log(unique3(testArray)); // 能區分不同類型的值
console.log(unique4(testArray)); // Map方法也能正確去重NaN

以上梳理了前端核心知識點及常見面試題,涵蓋了Promise、原型鏈、生成器、閉包、異步、事件循環、Map和數組等內容。這些知識點不僅是面試高頻考點,也是日常開發中經常用到的核心概念,掌握這些內容對于前端工程師至關重要。

前端對象與字符串知識點梳理及面試題詳解

一、對象(Object)

核心知識點

  • 對象是鍵值對的集合,鍵可以是字符串或Symbol,值可以是任意類型
  • 對象的創建方式:對象字面量{}new Object()、構造函數、Object.create()
  • 對象屬性的訪問方式:點語法(obj.key)和方括號語法(obj['key']
  • 可枚舉屬性與不可枚舉屬性:可枚舉屬性會被for...in遍歷到
  • 對象的特性:writable(可寫)、enumerable(可枚舉)、configurable(可配置)
  • 常見方法:Object.keys()Object.values()Object.entries()Object.assign()

面試題:實現對象的深拷貝

function deepClone(obj, hash = new WeakMap()) {// 處理null和基本類型if (obj === null || typeof obj !== 'object') {return obj;}// 處理日期if (obj instanceof Date) {return new Date(obj);}// 處理正則if (obj instanceof RegExp) {return new RegExp(obj.source, obj.flags);}// 處理循環引用if (hash.has(obj)) {return hash.get(obj);}// 區分數組和對象const cloneObj = Array.isArray(obj) ? [] : {};hash.set(obj, cloneObj);// 遍歷屬性(包括Symbol鍵)Reflect.ownKeys(obj).forEach(key => {cloneObj[key] = deepClone(obj[key], hash);});return cloneObj;
}// 測試
const obj = {a: 1,b: { c: 2 },d: [3, 4],e: new Date(),f: /test/,[Symbol('g')]: 5
};
obj.self = obj; // 循環引用const cloned = deepClone(obj);
console.log(cloned);

面試題:實現對象的扁平化解構

function flattenObject(obj, prefix = '', result = {}) {for (const key in obj) {if (obj.hasOwnProperty(key)) {const value = obj[key];const newKey = prefix ? `${prefix}.${key}` : key;if (typeof value === 'object' && value !== null && !Array.isArray(value)) {// 遞歸處理嵌套對象flattenObject(value, newKey, result);} else {result[newKey] = value;}}}return result;
}// 測試
const nestedObj = {a: 1,b: {c: 2,d: {e: 3,f: 4}},g: 5
};console.log(flattenObject(nestedObj));
// 輸出: { a: 1, 'b.c': 2, 'b.d.e': 3, 'b.d.f': 4, g: 5 }

面試題:實現對象的屬性攔截(類似Vue2的響應式原理)

function observe(obj) {if (typeof obj !== 'object' || obj === null) {return;}// 遍歷對象屬性Object.keys(obj).forEach(key => {let value = obj[key];// 遞歸處理嵌套對象observe(value);// 重定義屬性Object.defineProperty(obj, key, {get() {console.log(`獲取屬性${key}的值: ${value}`);return value;},set(newValue) {console.log(`設置屬性${key}的值: ${newValue}`);// 處理新值為對象的情況observe(newValue);value = newValue;}});});
}// 測試
const data = {name: '張三',age: 20,address: {city: '北京'}
};observe(data);
data.name; // 觸發get
data.age = 21; // 觸發set
data.address.city = '上海'; // 觸發get和set

二、字符串(String)

核心知識點

  • 字符串是字符的有序序列,在JavaScript中是不可變的
  • 常見創建方式:字符串字面量(''"")、模板字符串(` `)、new String()
  • 常用屬性:length(長度)
  • 常用方法:
    • 查找:indexOf()lastIndexOf()includes()startsWith()endsWith()
    • 截取:slice()substring()substr()
    • 轉換:toUpperCase()toLowerCase()trim()
    • 其他:split()replace()charAt()concat()
  • 模板字符串支持多行文本和變量插值(${}

面試題:實現字符串反轉

// 方法1:使用數組方法
function reverseString1(str) {return str.split('').reverse().join('');
}// 方法2:使用for循環
function reverseString2(str) {let result = '';for (let i = str.length - 1; i >= 0; i--) {result += str[i];}return result;
}// 方法3:使用遞歸
function reverseString3(str) {if (str === '') {return '';} else {return reverseString3(str.substr(1)) + str.charAt(0);}
}// 測試
console.log(reverseString1('hello')); // 'olleh'
console.log(reverseString2('world')); // 'dlrow'
console.log(reverseString3('test'));  // 'tset'

面試題:實現字符串中的單詞反轉(不改變單詞順序)

function reverseWords(str) {// 分割單詞(處理多個空格的情況)const words = str.split(/\s+/);// 反轉每個單詞const reversedWords = words.map(word => {return word.split('').reverse().join('');});// 拼接回字符串return reversedWords.join(' ');
}// 測試
console.log(reverseWords('Hello World')); // 'olleH dlroW'
console.log(reverseWords('I love JavaScript')); // 'I evol tpircSavaJ'
console.log(reverseWords('   Hello   there   ')); // '   olleH   ereht   '

面試題:實現千位分隔符格式化數字

function formatNumber(num) {// 處理非數字情況if (typeof num !== 'number' || isNaN(num)) {return '0';}// 轉換為字符串并分割整數和小數部分const parts = num.toString().split('.');let integerPart = parts[0];const decimalPart = parts[1] || '';// 處理負數let sign = '';if (integerPart[0] === '-') {sign = '-';integerPart = integerPart.slice(1);}// 反轉字符串便于處理let reversed = integerPart.split('').reverse().join('');let formatted = '';// 每三位添加一個逗號for (let i = 0; i < reversed.length; i++) {if (i !== 0 && i % 3 === 0) {formatted += ',';}formatted += reversed[i];}// 反轉回來并拼接符號和小數部分formatted = sign + formatted.split('').reverse().join('');return decimalPart ? `${formatted}.${decimalPart}` : formatted;
}// 測試
console.log(formatNumber(1234567)); // '1,234,567'
console.log(formatNumber(1234567.89)); // '1,234,567.89'
console.log(formatNumber(-123456)); // '-123,456'
console.log(formatNumber(0)); // '0'

面試題:實現字符串匹配算法(KMP算法)

// 構建部分匹配表
function buildLPS(pattern) {const lps = new Array(pattern.length).fill(0);let len = 0; // 最長前綴后綴的長度for (let i = 1; i < pattern.length; ) {if (pattern[i] === pattern[len]) {len++;lps[i] = len;i++;} else {if (len !== 0) {len = lps[len - 1];} else {lps[i] = 0;i++;}}}return lps;
}// KMP匹配算法
function kmpSearch(text, pattern) {const n = text.length;const m = pattern.length;if (m === 0) return 0; // 空模式匹配開頭const lps = buildLPS(pattern);let i = 0; // text的索引let j = 0; // pattern的索引while (i < n) {if (pattern[j] === text[i]) {i++;j++;if (j === m) {return i - j; // 找到匹配,返回起始索引}} else {if (j !== 0) {j = lps[j - 1];} else {i++;}}}return -1; // 未找到匹配
}// 測試
console.log(kmpSearch('ABC ABCDAB ABCDABCDABDE', 'ABCDABD')); // 15
console.log(kmpSearch('hello world', 'world')); // 6
console.log(kmpSearch('test string', 'xyz')); // -1

對象和字符串是JavaScript中最基礎也是最常用的數據類型,掌握它們的特性和操作方法對于前端開發至關重要。上述知識點和面試題覆蓋了常見的考點和實際開發需求,深入理解這些內容有助于提升代碼質量和解決問題的能力。


以下是對前端核心知識點(Promise、原型鏈、閉包、異步、事件循環、Map、數組)的系統梳理及高頻手寫題目詳解,結合面試考點和實現細節,助你高效備戰。


一、核心知識梳理與面試詳解

? 1. Promise與異步編程
  • 核心機制
    • 三種狀態Pending(等待)、Fulfilled(成功)、Rejected(失敗),狀態不可逆。
    • 鏈式調用.then()處理成功狀態,.catch()捕獲失敗,.finally()無論成敗均執行。
  • 手寫Promise.all
    function myPromiseAll(promises) {return new Promise((resolve, reject) => {// 校驗輸入是否為可迭代對象if (typeof promises?.[Symbol.iterator] !== 'function') {reject(new TypeError('Argument is not iterable'));return;}const promiseArray = Array.from(promises);const results = new Array(promiseArray.length);let completedCount = 0;// 空數組直接返回if (promiseArray.length === 0) resolve(results);promiseArray.forEach((p, i) => {Promise.resolve(p)  // 包裝非Promise值.then(res => {results[i] = res;  // 按索引存儲結果if (++completedCount === promiseArray.length) resolve(results);}).catch(err => reject(err));  // 任一失敗立即終止});});
    }
    
    得分點
    • 校驗可迭代對象、處理空數組、非Promise包裝。
    • 結果順序與輸入一致(避免push導致錯亂)。

🔗 2. 原型鏈與繼承
  • 核心概念
    • 原型(Prototype):構造函數(如Array)的prototype屬性,存放共享方法(如Array.prototype.map)。
    • 原型鏈:對象通過__proto__向上查找屬性,終點為Object.prototype.__proto__ === null
  • 繼承實現
    // 寄生組合繼承(最優解)
    function Parent(name) { this.name = name; }
    Parent.prototype.say = function() { console.log(this.name); };
    function Child(name, age) {Parent.call(this, name);  // 繼承實例屬性this.age = age;
    }
    Child.prototype = Object.create(Parent.prototype); // 繼承方法
    Child.prototype.constructor = Child;  // 修復構造函數指向
    
    面試重點
    • 避免組合繼承的兩次調用父類構造函數問題。
    • ES6的class本質是語法糖(如super()調用父類構造)。

🔒 3. 閉包與生成器
  • 閉包(Closure)
    • 原理:函數嵌套,內層函數訪問外層作用域的變量(即使外層已銷毀)。
    • 應用
      // 自增ID生成器
      function createIdGenerator(init = 0) {let id = init;return () => ++id;  // 閉包保存id狀態
      }
      const gen = createIdGenerator();
      gen(); // 1, gen(); // 2
      
      優勢:狀態隔離(多個生成器互不干擾)。
  • 生成器(Generator)
    • 特性function*定義,yield暫停執行,next()恢復執行。
    • 異步應用:配合co庫實現類似async/await的異步控制(已逐漸被替代)。

🔁 4. 事件循環(Event Loop)
  • 執行順序
    1. 同步代碼 → 微任務(Promise.thenMutationObserver) → 宏任務(setTimeoutDOM事件)。
  • 經典面試題
    console.log('1');
    setTimeout(() => console.log('2'), 0);
    Promise.resolve().then(() => console.log('3'));
    console.log('4');
    // 輸出:1 → 4 → 3 → 2
    
    原理:微任務優先級高于宏任務,同步代碼執行完畢后清空微任務隊列。

🗺? 5. Map與數組高頻操作
  • Map vs. Object

    • Map優勢:鍵可為任意類型(對象、函數)、有序迭代、性能更優(頻繁增刪場景)。
    • APIset(), get(), has(), delete()
  • 數組重點方法

    方法用途是否改變原數組
    map()映射新數組?
    filter()過濾滿足條件的元素?
    reduce()累計計算(如求和、扁平化)?
    splice()刪除/插入元素?
    slice()截取子數組?

    手寫數組扁平化

    function flatten(arr) {return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []);
    }
    flatten([1, [2, [3]]]); // [1, 2, 3]
    

二、高頻手撕題目詳解

1. 防抖(Debounce)與節流(Throttle)
  • 防抖:連續觸發時只執行最后一次:
    function debounce(fn, delay) {let timer;return (...args) => {clearTimeout(timer);timer = setTimeout(() => fn.apply(this, args), delay);};
    }
    // 場景:搜索框輸入聯想(停止輸入300ms后請求)
    
  • 節流:固定間隔執行一次:
    function throttle(fn, interval) {let lastTime = 0;return (...args) => {const now = Date.now();if (now - lastTime >= interval) {fn.apply(this, args);lastTime = now;}};
    }
    // 場景:滾動事件(每200ms計算位置)
    
2. 手寫深拷貝
function deepClone(obj, map = new Map()) {if (typeof obj !== 'object' || obj === null) return obj;if (map.has(obj)) return map.get(obj); // 解決循環引用const clone = Array.isArray(obj) ? [] : {};map.set(obj, clone);for (const key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key], map);}}return clone;
}

邊界處理:循環引用(Map記錄已拷貝對象)、函數和正則等特殊類型。


三、面試策略與復習建議

  1. 手寫題要點
    • 先口述思路(如防抖的“清除定時器+延遲執行”),再寫代碼。
    • 處理邊界:空輸入、非預期類型(如Promise.all校驗可迭代對象)。
  2. 框架結合
    • React:閉包在useCallback中的應用(依賴項變化才更新函數)。
    • Vue:響應式原理依賴Object.definePropertygetter/setter(類閉包狀態保存)。
  3. 性能優化
    • 減少閉包濫用(避免內存泄漏),如事件監聽后及時移除。
    • 數組操作優先map/filter(返回新數組,避免forEach副作用)。

本文完整題目及擴展解析參考:https://blog.csdn.net/weixin_48377020/article/details/132241816 | https://blog.csdn.net/T_iAn_T/article/details/145597660。


在 JavaScript 中,對象(引用類型)和字符串(基本類型)的拷貝機制存在本質差異,這直接影響開發中的數據處理邏輯。以下是關鍵知識點梳理和對比:


📦 一、對象的拷貝

1. 淺拷貝(Shallow Copy)
  • 特點:僅復制對象的第一層屬性。嵌套對象仍共享內存地址。
  • 實現方式
    • Object.assign({}, obj)
    • 展開運算符 { ...obj }
    • 數組方法如 slice()concat()
  • 示例
    const obj = { a: 1, b: { c: 2 } };
    const shallowCopy = { ...obj };
    shallowCopy.b.c = 3; // 修改嵌套屬性
    console.log(obj.b.c); // 3(原對象被影響)
    
2. 深拷貝(Deep Copy)
  • 特點:遞歸復制所有層級,新舊對象完全獨立。
  • 實現方式
    • JSON.parse(JSON.stringify(obj))
      • ? 簡單快捷
      • ? 無法處理函數、undefinedSymbolDate(轉為字符串)、循環引用
    • 遞歸實現
      function deepClone(obj, hash = new WeakMap()) {if (typeof obj !== 'object' || obj === null) return obj;if (hash.has(obj)) return hash.get(obj); // 解決循環引用const clone = Array.isArray(obj) ? [] : {};hash.set(obj, clone);for (const key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key], hash);}}return clone;
      }
      
    • 特殊類型處理
      • Datenew Date(obj.getTime())
      • RegExpnew RegExp(obj)
      • Map/Set → 遞歸復制元素
    • 現代 APIstructuredClone()
      • ? 支持循環引用、DateMapSet
      • ? 不支持函數、DOM 節點
    • 第三方庫_.cloneDeep(obj)(Lodash)

🔤 二、字符串的拷貝

  • 字符串是基本類型(Primitive Type),賦值時直接復制值,而非引用地址。
  • 示例
    const str1 = "hello";
    const str2 = str1; // 復制值
    str2 = "world";    // 修改不影響 str1
    console.log(str1); // "hello"
    
  • 特點
    • 無需深拷貝/淺拷貝概念,每次賦值均創建獨立副本。
    • 操作(如拼接、切片)均返回新字符串,原字符串不變。

?? 三、關鍵差異總結

特性對象(引用類型)字符串(基本類型)
拷貝機制賦值傳遞引用地址賦值直接復制值
修改影響淺拷貝時嵌套屬性互相影響修改后原數據不變
深拷貝需求必需(需遞歸處理嵌套引用)無需
內存占用淺拷貝節省內存,深拷貝開銷大每次修改均創建新副本

💡 四、實際應用建議

  1. 對象拷貝場景
    • 簡單數據且無特殊類型 → JSON.parse(JSON.stringify())
    • 復雜對象(含循環引用、Map等)→ structuredClone() 或 Lodash 的 _.cloneDeep()
  2. 字符串操作
    • 直接賦值或使用 slice()substring() 等返回新字符串的方法。
  3. 性能優化
    • 避免對大對象頻繁深拷貝,改用 不可變數據(Immutable.js)按需拷貝

完整實現代碼及邊界案例可參考:https://blog.csdn.net/qq_53353440/article/details/148548048、https://developer.mozilla.org/en-US/docs/Web/API/structuredClone。

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

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

相關文章

[自動化Adapt] 父子事件| 冗余過濾 | SQLite | SQLAlchemy | 會話工廠 | Alembic

第五章&#xff1a;事件處理與融合 歡迎回到OpenAdapt探索之旅~ 在第四章&#xff1a;系統配置中&#xff0c;我們掌握了如何定制化系統參數。更早的第一章&#xff1a;錄制引擎則展示了系統如何捕獲海量原始操作數據。 假設我們需要訓練機器人輸入"hello"一詞。原…

組合期權:跨式策略

文章目錄0.簡介1.買入跨式組合&#xff08;Long Straddle&#xff09;1.1 適用場景?1.2 合約選擇1.3 損益分析1.4 案例示范2.賣出跨式組合&#xff08;Short Straddle&#xff09;2.1 適用場景?2.2 合約選擇2.3 損益分析2.4 案例示范3.小結參考文獻0.簡介 跨式策略是一種交易…

Vue計算屬性詳解2

可寫計算屬性 計算屬性默認是只讀的,但在特殊場景下,我們可以創建"可寫"的計算屬性,通過同時提供getter和setter實現: <script setup>import { ref, computed } from vueconst firstName = ref(John)const lastName = ref(Doe)const fullName = computed(…

UniStorm 5.3.0 + Unity2022 + URP配置說明

一、前言 以前我用的是UniStorm3.0&#xff0c;主要用在內置管線里面&#xff0c;最近想在URP管線里面使用UniStorm天氣系統&#xff0c;于是弄了UniStorm5.3.0的包&#xff0c;在Unity2022.3的URP模式下配置&#xff0c;直接導入package&#xff0c;兩次宣告失敗。最后看了官方…

力扣經典算法篇-44-組合總和(回溯問題)

1、題干 給你一個無重復元素的整數數組candidates和一個目標整數 target &#xff0c;找出 candidates 中可以使數字和為目標數 target 的 所有 不同組合 &#xff0c;并以列表形式返回。你可以按 任意順序 返回這些組合。 candidates 中的 同一個 數字可以 無限制重復被選取 。…

矩陣與高斯消元:數學算法在計算機領域的應用

一、概述和基本概念 矩陣&#xff0c;類似于在 C 中我們看到的二維數組。它有兩個維度&#xff0c;行和列。下面是一個典型的矩陣&#xff1a; M[12342345445610111213] M \begin{bmatrix} 1 & 2 & 3 & 4 \\ 2 & 3 & 4 & 5 \\ 4 & 4 & 5 &…

【補題】CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes!) D. K-good

題意&#xff1a;給一個n&#xff0c;如果能被k個數整除&#xff0c;要求這k個數%k后不相同&#xff0c;問如果可以&#xff0c;任意k是多少&#xff0c;如果不可以輸出-1 思路&#xff1a; D. K-good_牛客博客 從來沒見過&#xff0c;太詭異了&#xff0c;做題做少了 1.…

LLM推理框架的“權力的游戲”:vLLM之后的群雄逐鹿

既然我們已經深入探討了本地與云端的兩大代表Ollama和vLLM&#xff0c;是時候將視野拓寬&#xff0c;檢視一下在高性能推理這片“高手如云”的競技場中&#xff0c;還有哪些重量級的玩家。vLLM的出現點燃了戰火&#xff0c;但遠非終點。 歡迎來到LLM推理框架的“后vLLM時代”—…

TDengine IDMP 背后的技術三問:目錄、標準與情景

過去十年&#xff0c;#工業 和#物聯網 場景經歷了快速的#數字化 建設&#xff1a;傳感器接入、系統聯網、數據上云……數據平臺已能輕松承載每秒千萬級別的寫入&#xff0c;每天幾 TB 的存儲量。但今天再回頭看&#xff0c;這些看似“完成”的系統&#xff0c;實際上只解決了一…

MyBatis基礎操作完整指南

文章目錄MyBatis簡介環境搭建Maven依賴數據庫表結構核心配置MyBatis配置文件數據庫配置文件實體類基礎CRUD操作Mapper接口Mapper XML映射文件工具類測試類動態SQL常用標簽高級特性一對一關聯映射一對多關聯映射分頁查詢使用注解方式MyBatis簡介 MyBatis是Apache的一個開源項目…

go與grpc

目錄下載與安裝遇到的問題cmd中protoc找不到命令cmd中--go_out: protoc-gen-go: Plugin failed with status code 1.下載與安裝 下載protoc&#xff1a; https://github.com/protocolbuffers/protobuf/releases 點擊下載相應電腦版本即可&#xff0c;我是windows系統下載了pro…

2025年AI面試重構招聘新生態

當企業面臨業務擴張與人才競爭的雙重壓力&#xff0c;傳統招聘模式已難以滿足高效、精準、公平的人才篩選需求。尤其在校招季、藍領用工潮等關鍵節點&#xff0c;面試官超負荷運轉、跨地域協調困難、評估標準模糊等問題頻發。AI技術的深度介入正推動招聘行業從“經驗驅動”向“…

Rust進階-part5-trait

Rust進階[part5]_trait trait概述 在 Rust 中,trait 是一種定義共享行為的方式。它類似于其他語言中的接口,允許我們定義一組方法簽名,然后讓不同的類型去實現這些方法。通過 trait,我們可以實現多態性,即不同類型可以以統一的方式處理。 普通實現 使用 trait 關鍵字來…

【人工智能-18】機器學習:決策樹、隨機森林

上一期【人工智能-17】機器學習&#xff1a;KNN算法、模型選擇和調優、樸素貝葉斯分類 文章目錄一、決策樹1.使用理由2.技術二、隨機森林1.使用理由2.原理核心&#xff1a;Bagging 隨機特征子集3.優點和缺點一、決策樹 決策樹是一種監督學習算法&#xff0c;主要用于分類&…

RFID高頻讀寫器在工業生產線的使用優勢

在工業4.0浪潮下&#xff0c;智能制造對生產效率與精準度的要求日益提升。RFID技術憑借其獨特的技術優勢&#xff0c;成為工業場景中實現數據實時采集與流程優化的關鍵工具。本文主要從RFID高頻讀寫器出發&#xff0c;系統解析其在工業生產線中的使用優勢。RFID高頻讀寫器一、技…

大模型學習筆記

prompt 提示詞的構成&#xff1a; 指示&#xff1a;描述讓它做什么上下文&#xff1a;給出與任務相關的背景信息輸入&#xff1a; 任務的輸入信息輸出&#xff1a;輸出的格式 生成與檢索 生成&#xff1a; 優點&#xff1a;內容的多樣性、創造性缺點&#xff1a;存在不可控制 檢…

龍虎榜——20250806

上證指數繼續收陽線&#xff0c;創新高的概率較大&#xff0c;個股上漲多于下跌&#xff0c;但板塊輪動較明顯&#xff0c;高位板塊注意風險。深證指數較昨天放量收陽線&#xff0c;站上5日和10日均線繼續上線&#xff0c;大科技方向資金關注更多。2025年8月6日龍虎榜行業方向分…

數據可視化發展歷程

數據可視化是數據描述的圖形表示&#xff0c;是當今數據分析發展最快速、最引人注目的領域之一。借助于可視化工具的發展&#xff0c;或樸實&#xff0c;或優雅&#xff0c;或絢爛的可視化作品給我們講述著各種數據故事。在這個領域中&#xff0c;科學、技術和藝術完美地結合在…

深入理解C++中的stack、queue和priority_queue

目錄 前言 1. stack&#xff08;棧&#xff09; 1.1 基本概念 1.2 常用接口 1.3 應用示例&#xff1a;最小棧 1.4 模擬實現 2. queue&#xff08;隊列&#xff09; 2.1 基本概念 2.2 常用接口 2.3 模擬實現 3. priority_queue&#xff08;優先隊列&#xff09; 3.1…

C++ 操作 Redis 客戶端

引言 前面幾篇文章都在介紹 Redis 原生命令行客戶端&#xff0c;在實際應用開發中&#xff0c;開發人員更希望使用針對特定編程語言的專用客戶端&#xff0c;通過編程的方式操作 Redis 數據庫。因此&#xff0c;Redis 支持多種編程語言。本文將介紹 如何使用 C 語言來操作 Red…