拼多多
3 選擇題
- opacity|visibity|display區別
在CSS中,opacity: 0
和visibility: hidden
都可以讓元素不可見,但它們的行為不同:
? opacity: 0
(透明度為0)
- 元素仍然占據空間(不移除文檔流)
- 元素可以被交互(例如仍然能點擊,除非配合
pointer-events: none
) - 元素可通過動畫慢慢變回可見
📌 元素是“看不見的”但仍在頁面上
visibility: hidden
? - 元素仍然占據空間
- 元素不可被交互(鼠標點擊不到)
- 通常用于暫時隱藏,但仍保留布局
📌 元素是“不可見的”而且“不可交互的”
哪個“會顯示”?
- 兩個都不會顯示元素(肉眼看不見)
- 但如果你問“哪個會被點擊 / 可以響應事件”,
opacity: 0
的元素仍然可響應點擊事件(默認),而visibility: hidden
的不行。
補充比較:
屬性 | 占位 | 可點擊 | 動畫支持 | 常見用途 |
---|---|---|---|---|
opacity: 0 | ? | ? | ? | 淡入淡出、過渡動畫 |
visibility: hidden | ? | ? | 一般 | 控制可見性但保留布局 |
如果你還想知道和 display: none
的對比,也可以告訴我。
- 瀏覽器渲染流程
是的,從www.sitecom
頁面中通過<link>
標簽加載csdn.css
(假設來自 CSDN 域名,如https://csdn.net/style/csdn.css
),屬于跨域資源加載,是否會觸發限制取決于資源的類型和瀏覽器規則:
? 結論:不會觸發 CORS 跨域限制,但可能被 CSP 限制
加載方式 | 是否跨域 | 是否需要 CORS | 是否容易被攔截 |
---|---|---|---|
<link rel="stylesheet" href="https://csdn.net/style/csdn.css"> | ? 是 | ? 不需要 CORS | ?? 可能被 CSP 或平臺策略限制 |
🔍 詳細解釋:
1. CSS 是“非簡單請求”,但不需要 CORS
- 加載 CSS 文件不會觸發瀏覽器的 CORS 機制(即不會檢查
Access-Control-Allow-Origin
)。 - CSS、圖片、JS 文件通過
<link>
、<img>
、<script>
加載時,瀏覽器默認允許跨域加載資源,只要資源本身能被正常訪問。
🔸 所以 只要 CSDN 那個 CSS 地址是公開的、狀態碼為 200,瀏覽器就會加載它,不會報 CORS 錯誤。
2. 可能會被 CSP(Content Security Policy)限制
如果你在 www.sitecom
設置了 CSP(內容安全策略):
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self'">
那就只能加載本站點的 CSS,CSDN 的外鏈就會被攔截,控制臺會報 CSP 相關的錯誤。
? 若你需要允許 CSDN 資源,需要在 CSP 中加上:
<style-src 'self' https://csdn.net;
3. 瀏覽器和平臺限制(可能導致問題)
- 瀏覽器隱私策略或插件:部分插件或隱私增強瀏覽器可能攔截外部資源。
- CDN 限制:CSDN 的 CDN 可能不允許“站外調用”(Referrer 檢查或 token 限制),返回 403。
- 移動端容器或小程序:某些平臺(如 WebView、快應用、小程序)限制外鏈樣式表加載,尤其是非 https 的。
? 總結
你在 www.sitecom
頁面中加載 https://csdn.net/style/csdn.css
:
- 不會觸發 CORS 錯誤
- 可能被 CSP 策略攔截(如有配置)
- 可能被目標站點拒絕服務(如有防盜鏈)
- 推薦方式:若必須使用,最好將 CSDN 的 CSS 下載后自行托管,避免未來不可控問題。
需要我幫你檢查具體 CSS 鏈接是否可訪問或如何托管在本地嗎?
- 計算機網絡相關內容
使用https
可以增強安全性,但它:
?? 不能完全預防 XSS 和 CSRF 攻擊。
? HTTPS 的作用是:
- 加密傳輸內容(防止被中間人竊聽或篡改)
- 驗證服務端身份(防止中間人偽裝網站)
- 防止會話被劫持(如Cookie被攔截)
? HTTPS 無法直接防御以下攻擊:
1. XSS(跨站腳本攻擊)
-
原理:攻擊者將惡意腳本注入頁面(如用戶輸入沒被過濾)
-
HTTPS 無法阻止惡意腳本執行
-
? 防御方式:
- 輸入/輸出過濾(如 HTML escape)
- 設置 CSP(Content Security Policy)
- 禁用內聯腳本、
eval()
等危險特性
2. CSRF(跨站請求偽造)
-
原理:用戶登錄后被誘導訪問惡意鏈接,對目標站點執行操作(如偽造請求)
-
HTTPS 無法阻止攻擊者誘導用戶發送請求
-
? 防御方式:
- 使用 CSRF Token 驗證請求來源
- 設置
SameSite
Cookie 策略(如SameSite=Lax
或Strict
) - 驗證 Referer / Origin 頭
? HTTPS + XSS/CSRF 防護措施 = 安全基礎
類型 | HTTPS是否有效 | 真正防護方式 |
---|---|---|
XSS | ? 無效 | 輸入/輸出過濾、CSP |
CSRF | ? 無效 | CSRF Token、SameSite Cookie |
中間人 | ? 有效 | HTTPS 加密、防止劫持 |
會話劫持 | ? 有效 | HTTPS + HttpOnly/SameSite Cookie |
🟢 最佳實踐:
- 始終使用 HTTPS(含子域名)
- 開啟
HttpOnly
、Secure
、SameSite
Cookie - 使用 CSP 頭防御 XSS
- 使用 CSRF Token 機制防御偽造請求
- 輸入內容做過濾和轉義
如你需要,我可以幫你配置一套完整的 HTTP 安全響應頭(CSP、HSTS、X-Frame-Options 等)或演示如何防御 XSS/CSRF。要嗎?
3代碼題(問答題)
只能說沒有環境,純純手寫,也不知道寫沒寫對,難道還要HR看?很困惑
1. 實現16進制的數的和(含大數)
function hexAddBig(hex1, hex2) {// 將十六進制字符串加上 '0x' 前綴,轉換為 BigIntconst num1 = BigInt('0x' + hex1);const num2 = BigInt('0x' + hex2);// 相加并轉換為十六進制大寫字符串const result = (num1 + num2).toString(16).toUpperCase();return result;
}// 示例
console.log(hexAddBig("FFFFFFFFFFFFFFFF", "1")); // 輸出:10000000000000000
2. 實現Git類(根據需求寫一個類)
明白了,你希望 `log()` 只顯示已經 `push()` 過的提交記錄。我幫你改下代碼,`push()` 會把當前所有提交標記為已推送,`log()` 只打印這些已推送的提交。---```javascript
class Git {constructor(repoName) {this.repo = repoName || "unnamed-repo";this._commits = [];this._pushedCommits = [];}commit(message, time) {if (!message) {console.warn(`[${this.repo}] Commit message is required.`);return this;}this._commits.push({message,timestamp: time || new Date().toISOString(),});return this;}push() {if (this._commits.length === 0) {console.log(`[${this.repo}] Nothing to push.`);} else {// 把當前未推送的提交都移到 _pushedCommitsthis._pushedCommits.push(...this._commits);this._commits = [];console.log(`[${this.repo}] Pushed ${this._pushedCommits.length} commit(s).`);}return this;}log() {if (this._pushedCommits.length === 0) {console.log(`[${this.repo}] No pushed commits yet.`);} else {this._pushedCommits.forEach(({ message, timestamp }) => {console.log(`${this.repo} ${timestamp} ${message}`);});}return this;}static init(repoName) {return new Git(repoName);}
}
使用示例:
const git = Git.init("my-repo");git.commit("init commit").commit("fix bug").log(); // 這里不會打印任何內容,因為還沒 pushgit.push().log(); // 這里會打印之前 push 的提交git.commit("new feature").log(); // 還是只打印 push 過的提交,不包含新提交git.push().log(); // 打印所有 push 過的提交,包括新 feature
這樣邏輯是:
commit()
新提交存儲在_commits
push()
把_commits
全部移動到_pushedCommits
,代表已經推送過log()
只打印_pushedCommits
3. 基于Promise實現應用場景(接力棒賽跑)
目標回顧
你有一個 teamsConfig
,結構是:
{team1: [runnerFn1, runnerFn2, ...],team2: [runnerFn1, runnerFn2, ...],...
}
每個 runnerFn
是一個返回 Promise 的函數,表示該運動員跑完后異步完成。
你希望:
- 所有隊伍的第一個運動員同時開始跑
- 同一隊伍的運動員必須一個接一個跑完,串行執行
- 所有隊伍的最后一個運動員跑完后,根據耗時排序,輸出排名
代碼
function relayRace(teamsConfig) {var results = []; // 用來保存每個隊伍的完成時間和隊名var finishedCount = 0; // 記錄已經完成的隊伍數var teamNames = Object.keys(teamsConfig); // ['team1', 'team2', ...]return new Promise(function (resolve) {// 遍歷所有隊伍teamNames.forEach(function (teamName) {var team = teamsConfig[teamName]; // 該隊伍運動員數組var startTime = Date.now(); // 記錄隊伍起跑時間// 定義一個遞歸函數,用于串行執行隊伍中的運動員function runRunner(i) {if (i >= team.length) {// 運動員全部跑完,計算耗時var duration = Date.now() - startTime;// 保存結果,包含隊伍名和耗時results.push({ teamName: teamName, duration: duration });finishedCount++; // 完成隊伍數量加1// 如果所有隊伍都跑完了,排序并返回結果if (finishedCount === teamNames.length) {// 按耗時升序排序,耗時少的排在前面results.sort(function (a, b) {return a.duration - b.duration;});// 結束 Promise,返回排序后的結果resolve(results);}return; // 結束該隊伍遞歸}// 當前運動員跑步,返回 Promisevar runner = team[i];runner().then(function () {// 當前運動員跑完后,遞歸調用執行下一個運動員runRunner(i + 1);});}// 開始執行第一個運動員,所有隊伍第一個運動員會同時觸發runRunner(0);});});
}
詳細解釋
1. 為什么用 Object.keys
?
teamsConfig
是對象,不是數組。我們要拿到隊伍名稱列表:
var teamNames = Object.keys(teamsConfig);
這樣我們能遍歷每個隊伍,知道“哪個隊伍”對應哪個運動員數組。
2. 為什么用 Promise
?
整個比賽是異步的,要等所有隊伍跑完才能得到最終排名,所以用 Promise
來包裝,方便外面用 .then()
獲取結果。
3. 為什么遞歸 runRunner(i)
?
同一隊伍運動員必須一個接一個跑,不能同時跑。調用第 i 個運動員函數得到一個 Promise,跑完后才跑 i+1。
遞歸寫法:
- 如果
i >= team.length
,說明跑完了 - 否則
runner[i]()
返回 Promise,跑完后遞歸跑下一個
這樣保證串行執行。
4. 為什么 finishedCount
?
我們有多個隊伍同時比賽,forEach
啟動所有隊伍跑步。
需要知道什么時候所有隊伍都完成,才能排序輸出結果。
finishedCount
就是計數器,每當一個隊伍跑完(遞歸結束時)就加一。
當等于隊伍總數時,就調用 resolve
,通知外面跑完了。
5. 為什么每個隊伍用自己的 startTime
?
題目要求:
- 所有隊伍的第一個運動員同時開始跑
- 記錄的是每個隊伍自己的耗時
我們在 forEach
循環里對每個隊伍分別調用 Date.now()
,開始時間就是該隊伍第一個運動員實際開始的時刻。
因為是并行啟動的,這樣自然符合“同時開始”的要求。
6. 排序和輸出
比賽結束后,把 results
按耗時排序:
results.sort(function (a, b) {return a.duration - b.duration;
});
耗時越少排名越靠前。
7. 如何使用?
你需要傳入格式是:
{team1: [runnerFn, runnerFn, ...],team2: [runnerFn, runnerFn, ...],...
}
每個 runnerFn
是一個返回 Promise 的函數,表示運動員跑步異步完成。
示例代碼
function createRunner(name, time) {return function () {return new Promise(function (resolve) {console.log(name + " start running");setTimeout(function () {console.log(name + " finished running");resolve();}, time);});};
}var teamsConfig = {team1: [createRunner("Team1-Runner1", 1000), createRunner("Team1-Runner2", 800)],team2: [createRunner("Team2-Runner1", 900), createRunner("Team2-Runner2", 1200)],team3: [createRunner("Team3-Runner1", 1100), createRunner("Team3-Runner2", 700)],
};relayRace(teamsConfig).then(function (rankings) {console.log("Race finished! Rankings:");rankings.forEach(function (res, i) {console.log("#" + (i + 1) + " " + res.teamName + " - Time: " + res.duration + "ms");});
});
總結
- 使用
Object.keys
遍歷隊伍名,遍歷對象 - 用遞歸函數串行執行隊伍中運動員
- 用計數器判斷所有隊伍完成
- 結果排序輸出,給出排名
這樣實現沒有用 async/await
和 generator,邏輯清晰,符合題目要求。
如果你需要我幫你寫注釋版代碼,或者進一步改進(比如加超時檢測、錯誤處理),告訴我!
4399
10選擇題目
3個簡答題目
1.XSS攻擊
0.1+0.2等于0.3嗎
3個編程題目
1. HTML/CSS基礎題目,基于Flex布局實現一個卡片布局
你發的內容是一個數字串:0272700
。請問你想了解或操作它的什么方面?以下是幾種可能的解釋方向,你可以確認一下你的意圖:
🧾 可能的理解方式:
類型 | 解釋 |
---|---|
📦 條形碼或產品編號 | 可能是商品、倉庫或物流中的 SKU 編號(可能需要查數據庫或系統) |
🏢 公司代碼(如韓國) | 可能是股票代碼,比如韓國證券交易所(KRX)中 三星電機 的股票代碼是 027270 ,你寫的是 0272700 ,可能多了一個 0 。需確認。 |
🔢 數值或編碼含義 | 可能是你在做編程、算法題中用到的值,或者某種內部 ID(比如用戶編號) |
🧪 作為輸入測試數據 | 也可能是你用于測試輸入框、校驗、正則等功能的字符串 |
? 手滑或誤輸入 | 有時是誤發的內容,你可直接說明你想做什么 |
🧭 如果你能補充:
- 你是在哪看到的這個數字?
- 想查什么?(比如含義、歸屬、正則、格式處理?)
- 是用于前端輸入、JS 腳本、接口測試等嗎?
我就能更準確地幫你分析或寫代碼。歡迎補充上下文。
2. 實現一個類
下面是一個基于 ES6+ 實現的 AsyncBatcher
類,支持以下功能:
? 功能要求實現
- 構造函數支持
batchSize
和delay
(單位 ms) addTask(task)
方法:添加異步任務(是個函數,返回 Promise)- 任務將在達到批大小或延遲后批量執行
flush()
方法:立即執行當前批次所有等待任務- 每個
addTask
返回的 Promise 可拿到對應任務的結果
? 實現代碼
class AsyncBatcher {constructor(batchSize, delay) {this.batchSize = batchSize;this.delay = delay;this.queue = [];this.timer = null;}addTask(taskFn) {return new Promise((resolve, reject) => {this.queue.push({ taskFn, resolve, reject });if (this.queue.length === 1) {// 啟動延遲定時器this.timer = setTimeout(() => this._runBatch(), this.delay);}if (this.queue.length >= this.batchSize) {this._runBatch();}});}flush() {if (this.queue.length > 0) {this._runBatch();}}async _runBatch() {clearTimeout(this.timer);this.timer = null;const currentBatch = this.queue.splice(0, this.batchSize);await Promise.all(currentBatch.map(async ({ taskFn, resolve, reject }) => {try {const result = await taskFn();resolve(result);} catch (err) {reject(err);}}));}
}
? 使用示例
const batcher = new AsyncBatcher(3, 2000);// 模擬異步任務
const createTask = (id) => () =>new Promise((res) => {setTimeout(() => res(`任務 ${id} 完成`), 500);});batcher.addTask(createTask(1)).then(console.log);
batcher.addTask(createTask(2)).then(console.log);
batcher.addTask(createTask(3)).then(console.log); // 達到 batchSize,會立即執行
batcher.addTask(createTask(4)).then(console.log); // 新一批等待 delay 或手動 flushsetTimeout(() => {batcher.flush(); // 手動觸發剩余任務
}, 1000);
🧠 注意點
- 所有任務執行都是并發的(
Promise.all
) - 如果任務數沒滿,但超時到了,也會觸發
flush()
可隨時讓隊列中的任務立即執行- 若需要支持自定義批處理邏輯(例如任務之間組合處理),可額外傳入一個批處理器函數
如果你需要:
- 限制并發數(如批任務內部并發上限)
- 允許添加回調(batch 完成后通知)
- 支持取消 pending 的任務
我也可以幫你擴展這一類。是否需要增強版?
3. 回溯遍歷一個vDOM,獲得所有路徑
你問得很對!**使用回溯(Backtracking)**思路確實更清晰、更符合“路徑查找”的語義,尤其在以下情況:
- 你想找出所有從根到目標節點的路徑
- 你想在遍歷中動態構建路徑、嘗試、撤銷
- 你希望未來支持提前剪枝、多個目標匹配、路徑過濾等功能
? 回溯解法本質上就是 DFS + 路徑數組動態維護:
相比 DFS 用 newPath = [...path, name]
,回溯更高效,因為它在一個數組上操作,并在每次遞歸后“回退”一步,避免復制數組。
? 回溯實現版本(更接近真實算法語義)
function findComponentPaths(vNode, targetComponent) {const results = [];const path = [];function backtrack(node) {if (!node || typeof node !== 'object') return;const name =typeof node.type === 'string'? node.type: node.type?.name || 'Anonymous';path.push(name);const isMatch =typeof targetComponent === 'string'? name === targetComponent: node.type === targetComponent;if (isMatch) {results.push([...path]); // 必須復制,否則后面修改 path 會影響已存路徑}const children = node.children || [];const list = Array.isArray(children) ? children : [children];for (const child of list) {backtrack(child);}path.pop(); // 回退一步}backtrack(vNode);return results;
}
? 回溯相比 DFS + 拷貝的優勢
比較點 | DFS 復制路徑 | 回溯方式(path 共享 + pop) |
---|---|---|
內存消耗 | 每步都復制 path | path 一條,只在 push/pop |
表達意圖 | 邏輯能通但略啰嗦 | 更貼近“探索 -> 撤銷”的語義 |
擴展剪枝能力 | 略繁瑣 | 可隨時加 if 控制遞歸/剪枝 |
性能 | 多次 path.slice() 復制 | 低復制開銷,更適合大樹 |
? 示例輸入再次驗證
const tree = {type: 'App',children: [{type: 'Layout',children: [{ type: 'Sidebar' },{type: 'Content',children: [{ type: 'Sidebar' }]}]}]
};console.log(findComponentPaths(tree, 'Sidebar'));
// 輸出:
// [
// ['App', 'Layout', 'Sidebar'],
// ['App', 'Layout', 'Content', 'Sidebar']
// ]
🧠 總結
-
? 回溯方式更適合路徑類搜索問題
-
? 代碼更高效、優雅、語義化
-
如果你想未來加入:
- 匹配多個目標組件
- 限制最大路徑長度
- 路徑過濾條件
- 找到一個路徑就返回(如 DFS 找最近)
那回溯結構更便于擴展。
需要我進一步加上“匹配多個組件”、“路徑包含某關鍵字的路徑”、“目標只找第一個”等擴展功能嗎?