
文章目錄
- 1. 變數宣告與型別
- 1.1 var let const 區別
- 1.2 資料型別
- 1.3 值型別與參考型別的區別
- 1.4 typeof 能判斷哪些型別
- 1.5 判斷資料型別的方式
- 1.6 `===` 與 `==`
- 1.7 truly變數與falsely變數
- 1.8 強制型別轉換和隱式型別轉換
- 1.9 陳述句與運算式
- 2. 陣列字串相關
- 2.1 手寫深拷貝
- 2.2 手寫深度比較
- 2.3 陣列的API有哪些是純函式
- 2.4 `split()`和`join()`的區別
- 2.5 陣列`slice`與`splice`區別
- 2.6 手寫字串 trim
- 3. 函式相關
- 3.1 函式宣告與函式運算式
- 3.2 什么是JSON
- 3.3 將URL引數決議成JS物件
- 4. 原型與原型鏈
- 4.1 解釋一下原型與原型鏈
- 4.2 class的原型本質
- 4.3 new Object() 與 Object.create()的區別
- 4.4 用class語法寫一個簡單的jQuery
- 5. 作用域與閉包
- 5.1 作用域
- 5.2 this不同場景下如何取值
- 5.3 手寫bind
- 5.4 閉包
- 5.5 閉包的應用場景
- 6. ES6新特性
- 7. 異步相關
- 7.1 同步與異步的區別
- 7.2 前端使用異步的場景
- 7.3 Promise的三種狀態
- 7.4 promise的then和catch
- 7.5 手寫promise加載圖片
- 7.6 async/await與Promise
- 7.7 位元組面試看代碼題
- 7.8 for-of 的應用場景 【異步】
- 8. Event Loop
- 8.1 宏任務macroTask與微任務microTask
- 8.2 Event Loop機制
- 8.3 event loop練習
- 9. DOM
- 9.1 獲取節點操作
- 9.2 標簽屬性 attribute
- 9.3 物件屬性 property
- 9.4 DOM結構操作
- 9.5 優化DOM性能
- 9.5.1 對DOM查詢進行快取
- 9.5.2 將頻繁操作改為一次性操作
- 10. BOM
- 10.1 檢查瀏覽器型別
- 10.2 拆解URL各個部分
- 11. 事件
- 11.1 事件系結、冒泡、代理
- 11.2 寫一個通用的事件系結函式
- 11.3 描述事件冒泡程序
- 11.4 無限下拉的圖片串列,如何監聽每個圖片的點擊
- 12. AJAX
- 12.1 手寫一個簡單的ajax
- 12.2 跨域解決方案
- 13. 瀏覽器存盤
- 14. 頁面加載
- 14.1 資源的形式
- 14.2 從輸入url到渲染出頁面的整個程序
- 14.3 window.onload與DOMContentLoaded的區別
- 15. 性能優化
- 15.1 前端常見性能優化方案
- 15.2 快取
- 15.3 SSR服務渲染
- 15.4 圖片懶加載
- 15.5 節流與防抖
- 16. 前端安全
- 16.1 XSS 跨站請求攻擊
- 16.2 XSRF 跨站請求偽造
- 17. 演算法刷題
之前也總結過一些前端面試題,推薦可以一起看看
【網路】計算機網路常見面試題 - 前端面試必備 - 吐血整理
【CSS】面試題總結 - 基礎知識總結 - 復習專用 - 前端面試必備 - 吐血整理
【Vue】面試題總結 - 基礎知識總結 - 復習專用 - 組件相關 - Vue家族 - 原始碼相關
這次是關于JavaScript的面試題有題目也有答案,想深入了解,可以看我之前的一些筆記博文(狠詳細),當然,每題有相關博文的我都會放鏈接~
持續更新中…
1. 變數宣告與型別
1.1 var let const 區別
- var是ES5語法,let、const是ES6的語法
- var有變數提升
- var、let是變數,可修改;const是常量,不可修改
- let、const 塊級作用域;var函式作用域

【ES6】變數宣告-var-let-const-區別與聯系-總結
1.2 資料型別
值型別(7個):Undefined、Null、Number、String、Boolean、Symbol(ES6)、BigInt(ES10)
參考型別:Object:Array、Function
【JS】JavaScript-ES5資料型別-基本資料型別-參考資料型別-型別之間的轉換-資料型別的判斷
1.3 值型別與參考型別的區別
值型別 存在堆疊記憶體中,變數拿到的就是它的值
參考型別 存在堆記憶體中,變數拿到的只是它的一個參考,是它的地址
【JS】JavaScript-物件-Object-內建物件-宿主物件-自定義物件-操作物件-基本資料型別與參考資料型別區別
1.4 typeof 能判斷哪些型別
- undefined、string、number、boolean、symbol、bigint【除了null的基本型別】
- function 【函式】
- object (typeof null === ‘object’) 【所有參考型別只能到object這里】
1.5 判斷資料型別的方式
- typeof 【除了null的基本型別 + function】
- instanceof 【參考型別】【從子類到父類直到object】【順著原型鏈】
- toString() 【任意型別】
- Array.isArray() 【陣列】
1.6 === 與 ==
=== 嚴格的比較是否相等
== 會進行型別轉換,再進行比較
以下都是成立的
100 == '100'
0 == ''
0 == false
fase == ''
null == undefined
有一個情況可以用下 ==
if(a == null) {}
// 等價于
if(a === null || a === undefined)()
1.7 truly變數與falsely變數
truly變數:!!a === true 的變數
falsely變數:!!b === false 的變數
以下是falsey變數,除了這六種情況,其余都是truely變數
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
1.8 強制型別轉換和隱式型別轉換
強制:parseInt、parseFloat、toString
隱式:if、邏輯運算、==、+拼接字串
一定要看這個,狠詳細
【JS】JavaScript-ES5資料型別-基本資料型別-參考資料型別-型別之間的轉換-資料型別的判斷
1.9 陳述句與運算式
運算式:一個運算式會產生一個值,可以放在任何一個需要值的地方
a
a+b
demo(1)
x===y? 'a': 'b'
陳述句
if(){}
for(){}
2. 陣列字串相關
2.1 手寫深拷貝
function deepClone(obj){
if (typeof obj !== 'object' || obj === null){
return obj
}
let result = Array.isArray(obj) ? []: {}
for (let key in obj) {
if(obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
【JS】自定義JS工具函式庫-自定義物件方法-new-instanceof-mergeObject-實作陣列與物件的深拷貝與淺拷貝-封裝字串相關函式
2.2 手寫深度比較
// 判斷是否是物件或陣列
function isObject(obj) {
return typeof obj === object && obj !== null;
}
// 深度比較
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值型別,直接判斷【一般不會傳函式,不考慮函式】
return obj1 === obj2;
}
if (obj1 === obj2) {
return true;
}
// 兩個都是物件或陣列,而且不相等
// 1. 先判斷鍵的個數是否相等,不相等一定回傳false
const obj1Keys = Object.keys(obj1);
const obj2Keys = Objext.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
// 2. 以obj1為基準,和obj2依次遞回比較
for (let key in obj1) {
// 遞回比較
const res = isEqual(obj1[key], obj2[key]);
if (!res) {
return false;
}
}
// 3. 全相等
return true;
}
2.3 陣列的API有哪些是純函式
純函式:①不改變原陣列(沒有副作用) ②回傳一個新陣列
concat、map、filter、slice
非純函式:push、pop、shift、unshift、forEach、some、every、reduce
【JS】你不得不知道的JavaScript陣列相關知識【全面總結】復習專用
2.4 split()和join()的區別
split() 是字串的方法
join() 是陣列的方法
'1-2-3'.split('-') // [1,2,3]
[1,2,3].join('-') // 1-2-3
2.5 陣列slice與splice區別
slice 切片
splice 剪接
【JS】JavaScript陣列-操作方法-concat-陣列強制打平-slice-splice方法使用
2.6 手寫字串 trim
String.prototype.trim = function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
3. 函式相關
3.1 函式宣告與函式運算式
函式宣告式
function fn(a, b) {
return a + b;
}
函式運算式
let fun = function(a, b){
return a + b;
}
3.2 什么是JSON
- JSON是一種資料格式,本質是一段字串
- JSON格式與JS物件結構一致,對JS語言更友好
window.JSON是一個全域物件,常用的兩個方法JSON.stringify和JSON.parse
3.3 將URL引數決議成JS物件
傳統方法,分析search
function queryToObj() {
const res = {}
const search = location.search.substr(1)
search.split('&').forEach(paramStr => {
const arr = paramStr.split('=')
const key = arr[0]
const val = arr[1]
res[key] = val
})
return res
}
使用URLSearchParams
function queryToObj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
4. 原型與原型鏈
4.1 解釋一下原型與原型鏈
每個函式物件都有顯式原型 prototype
每個實體物件都有隱式原型 __proto__
實體物件的__proto__指向函式物件的prototype

(之前博文中的圖)
原型鏈:實體物件在獲取物件上的屬性和方法時,先在自身找,找不到就去隱式原型上面找

(之前博文中的圖)
4.2 class的原型本質
class是ES6語法規范,由ECMA委員會發布【建構式、繼承】
ECMA只規定語法規則,不規定如何實作
下面博文具體介紹了class語法,以及具體的原生實作【建構式、繼承】
【ES6】JavaScript面向物件-面向物件與面向程序的對比-類class-繼承extends-建構式-super
【JS】JavaScript創建物件 - 工廠模式 - 建構式模式 - 原型模式 - 原型鏈 - 組合模式
【JS】JavaScript繼承 - 原型鏈 - 盜用建構式 - 組合繼承 -原型式繼承 - 寄生式繼承 - 寄生式組合繼承
4.3 new Object() 與 Object.create()的區別
{}等同于new Object(),原型為Object.prototypeObject.create(null)沒有原型Object.create({...})可以指定原型
4.4 用class語法寫一個簡單的jQuery
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector);
const length = result.length;
for (let i = 0; i < length; i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i];
fn(elem);
}
}
on(type, fn) {
return this.each((elem) => {
elem.addEventListener(type, fn, false);
});
}
}
// 插件
jQuery.prototype.dialog = function(info){
console.log(info);
}
// 拓展性
class myjQuery extends jQuery{
constructor(selector){
super(selector)
}
// 擴展自己的方法
addClass(className){}
addStyle(data){}
}
5. 作用域與閉包
這篇博文寫的很詳細,推薦閱讀
【JS】你不知道的JavaScript 筆記(一)—— 作用域與閉包 - 編譯原理 - LHS - RHS - 回圈與閉包 - 模塊 - 詞法作用域 - 動態作用域
【JS】函式定義與呼叫方式-函式this指向問題-call-apply-bind方法使用與自定義
【JS】你不知道的JavaScript筆記(二)- this - 四種系結規則 - 系結優先級 - 系結例外 - 箭頭函式
5.1 作用域
一個變數合法的使用范圍,JS中采用的是詞法作用域(靜態作用域)
【變數的查找,取決于在哪里定義,而不是在哪里執行】
全域作用域 - 函式作用域 - 塊級作用域
自由變數:一個變數在當前作用域沒有定義,但是被使用了,這時就向上級作用域一層一層依次查找,直到找到為止,最后在全域作用域都沒找到就報錯 ReferenceError:xxx is not defiend
5.2 this不同場景下如何取值
this查找采用的是動態作用域
【this的指向,取決于在哪里執行,而不是在哪里定義】
例題
const User = {
count: 1,
getCount: function() {
return this.count
}
}
console.log(User.getCount()) // 1
const func = User.getCount
console.log( func() ) // undefined
【JS】你不知道的JavaScript筆記(二)- this - 四種系結規則 - 系結優先級 - 系結例外 - 箭頭函式
5.3 手寫bind
【JS】函式定義與呼叫方式-函式this指向問題-call-apply-bind方法使用與自定義
Function.prototype.myBind = function() {
// 將引數拆解為陣列
const args = Array.prototype.slice.call(arguments)
// 獲取this
const t = args.shift()
// fn1.bind(...)中的 fn1
const self = this
return function() {
return self.apply(t, args)
}
}
使用
function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'this is fn1'
}
const fn2 = fn1.myBind({x: 100}, 10, 20, 30)
const result = fn2()
console.log(result)

5.4 閉包
當函式可以記住并訪問所在的詞法作用域,即使函式是在當前詞法作用域之外執行,這時就產生了閉包
① 函式作為引數被傳遞 ② 函式作為回傳值被回傳
【JS】你不知道的JavaScript 筆記(一)—— 作用域與閉包 - 編譯原理 - LHS - RHS - 回圈與閉包 - 模塊 - 詞法作用域 - 動態作用域
5.5 閉包的應用場景
隱藏資料,只提供API
function createCache() {
const data = {} // 閉包中的資料被隱藏,不被外界訪問
return {
set: function(key, value) {
data[key] = value
},
get: function(key){
return data[key]
}
}
}
const cache = createCache()
c.set('a', 100)
console.log(c.get('a'))
6. ES6新特性
【ES6】變數宣告-var-let-const-區別與聯系-總結
【ES6】Symbol基本使用及常用內置符號
【ES6】JavaScript函式-箭頭函式-this指向-簡寫
【ES6】JavaScript-變數的解構賦值-陣列解構-物件解構-物件的屬性-物件的方法
【ES6】JavaScript物件-增強的物件語法-屬性值簡寫-可計算屬性-簡寫方法名
【ES6】JavaScript函式-引數的默認值-與解構賦值的結合使用-對arguments的影響-默認引數作用域與暫時性死區
【ES6】JavaScript陣列-陣列的創建-建構式-字面量-Array.from()-Array.of()靜態方法
【ES6】JavaScript陣列-迭代器方法-keys()-values()-entries()-迭代方法-every()-some()-filter()-map()-forEach()
【ES6】JavaScript面向物件-面向物件與面向程序的對比-類class-繼承extends-建構式-super
【ES6】迭代器與生成器
【Promise】入門-同步回呼-異步回呼-JS中的例外error處理-Promis的理解和使用-基本使用-鏈式呼叫-七個關鍵問題
【ES8】異步代碼終極解決方案 async 和 await
【ES6模塊化】import - export - 按需引入 - 專案中使用babel - ES6模塊化引入npm包
7. 異步相關
因為單執行緒,所以異步【同步會阻塞代碼執行】
JS單執行緒, 和DOM渲染共用一個執行緒【因為JS可以修改DOM結構】
瀏覽器和node.js已經支持JavaScript啟動行程,如 Web Worker
7.1 同步與異步的區別
【JavaScript】同步與異步-異步與并行-異步運行機制-為什么要異步編程-異步與回呼-回呼地獄-JavaScript中的異步操作
7.2 前端使用異步的場景
網路請求,如ajax
定時任務
7.3 Promise的三種狀態
- pending 等待中 不會觸發
then和catch - resolved 成功了 會觸發后續的
then回呼函式 - rejected 失敗了 會觸發后續的
catch回呼函式
then正常回傳 resolved,里面有報錯則回傳 rejected
catch正常回傳 resolved,里面有報錯則回傳 rejected
【Promise】入門-同步回呼-異步回呼-JS中的例外error處理-Promis的理解和使用-基本使用-鏈式呼叫-七個關鍵問題
7.4 promise的then和catch
Promise.resolve().then(()=>{
console.log(1) // 執行
}).catch(()=>{
console.log(2) // 不執行
}).then(()=>{
console.log(3) // 執行
})
Promise.resolve().then(()=>{
console.log(1) // 執行
throw new Error('err1')
}).catch(()=>{
console.log(2) // 執行
}).then(()=>{
console.log(3) // 執行
})
Promise.resolve().then(()=>{
console.log(1) // 執行
throw new Error('err1')
}).catch(()=>{
console.log(2) // 執行
}).catch(()=>{
console.log(3) // 不執行
})
7.5 手寫promise加載圖片
function loadImg(src){
return new Promise((resolve, reject)=>{
const img = document.createElement('img')
img.onload = () =>{
resolve(img)
}
img.onerror = () => {
reject(new Error(`圖片加載失敗 ${src}`))
}
img.src = src
})
}
// 使用
const url = ''
loadImg(url).then(img => {
console.log(img.width)
return img
}).then(img => {
console.log(img.height)
}.catch(err => console.error(err))
// 使用加載多張圖片
url1 = ''
url2 = ''
loadImg(url1).then(img1 => {
console.log(img1.width)
return img1
}).then(img1 => {
console.log(img1.height)
return loadImg(url2)
}).then(img2 => {
console.log(img2.width)
return img2
}).then(img2 => {
console.log(img2.height)
}).catch(err => console.error(err))
7.6 async/await與Promise
- 執行
async函式,回傳的是Promise物件 await相當于Promise的thentry/catch可以捕獲例外,代替了Promise的catch
【ES8】異步代碼終極解決方案 async 和 await
7.7 位元組面試看代碼題
async function async1() { // 函式定義
console.log("async1 start"); // 2
await async2(); // 函式執行
// await后面的內容,可以看作是回呼里的內容,即異步執行
console.log("async1 end"); // 6
}
async function async2() { // 函式定義
console.log("async2"); // 3
}
console.log("script start"); // 1
async1(); // 函式執行
new Promise((resolve)=>{
console.log('promise1'); // 4
resolve();
}).then(()=>{
console.log('promise2'); // 7
})
console.log("script end"); // 5 同步代碼執行完畢
7.8 for-of 的應用場景 【異步】
for、forEach、for-in 是常規的【同步】遍歷
for-of常用于【異步】的遍歷
function muti(num) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * num);
}, 1000);
});
}
const nums = [1, 2, 3];
nums.forEach(async (i) => {
const res = await muti(i);
console.log(res); // 會同時列印出三個結果
});
(async function () {
for (let i of nums) {
const res = await muti(i);
console.log(res); // 會每隔一秒列印出一個結果
}
})();
8. Event Loop
8.1 宏任務macroTask與微任務microTask
- 宏任務:setTimeout、setInterval、Ajax、DOM事件【W3C規范】
- 微任務:Promise、async/await【ES規范】
微任務執行時機比宏任務早
- 微任務在DOM渲染前觸發 【Promise】
- 宏任務在DOM渲染后觸發 【setTimeout】
8.2 Event Loop機制
- 回呼堆疊 Call Stack
- 事件回圈 event loop
- 回呼佇列 Callback Queue
- 微任務佇列 micro task queue

- 同步代碼,一行一行放在回呼堆疊中執行,執行完了就出堆疊
- 遇到異步,記錄下來,等待時機;時機到了,就移動到回呼佇列中
- 當回呼堆疊為空,(微任務[微任務佇列])【嘗試DOM渲染】 事件回圈開始作業:輪詢查找(宏任務)回呼佇列,有則移動到回呼堆疊中執行
- 繼續輪詢loop
【同步任務——微任務——DOM渲染——宏任務】——同步任務——微任務——DOM渲染——宏任務…
實際上真正的 event loop 是這樣的
- 一開始整個腳本作為一個宏任務執行
- 執行程序中同步代碼直接執行,宏任務進入宏任務佇列,微任務進入微任務佇列
- 當前宏任務執行完出隊,檢查微任務串列,有則依次執行,直到全部執行完
- 執行瀏覽器UI執行緒的渲染作業
- 檢查是否有Web Worker任務,有則執行
- 執行完本輪的宏任務,回到2,依此回圈,直到宏任務和微任務佇列都為空
所以程序是
【宏任務(代碼整體)——同步任務——微任務——DOM渲染】——宏任務——同步任務——微任務——DOM渲染…
8.3 event loop練習
console.log('1'); // ①同步任務
setTimeout(function() { // ① 宏任務
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() { // ① 微任務
console.log('6');
})
new Promise(function(resolve) {
console.log('7'); // ① 同步任務
resolve();
}).then(function() { // ① 微任務
console.log('8')
})
setTimeout(function() { // ① 宏任務
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
console.log('13');
process.nextTick(function() {
console.log('14'); // 微任務 process.nextTick 比 promse.then優先級要高
})
})
1 7 6 8 2 4 3 5 9 11 13 10 14 12
9. DOM
9.1 獲取節點操作
document.getElementById('yk') // 元素
document.getElementsByTagName('div') // 集合
document.getElementsByClassName('container') // 集合
document.querySelectorAll('p') // 集合
9.2 標簽屬性 attribute
修改的是標簽屬性【行內樣式】
修改html屬性,會改變html結構
const pList = document.querySelectorAll('p')
const p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name','ykjun')
p.getAttribute('style')
p.setAttribute('style', 'font-size: 10px')
9.3 物件屬性 property
用JS的屬性操作DOM元素
修改物件屬性,不會 體現到html結構中
const pList = document.querySelectorAll('p')
const p = pList[0]
console.log(p.style.width) // 獲取樣式
p.style.width = '100px' // 修改樣式
console.log(p.className) // 獲取class
p.className = 'p1' // 修改class
console.log(p.nodeName) // 獲取nodeName節點名稱
console.log(p.nodeType) // 獲取nodeType節點型別
9.4 DOM結構操作
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建節點
const newP = document.createElement('p')
newP.innerHTML = 'this is new p'
// 插入節點
div1.appendChild(newP)
// 移動節點
const p1 = document.getElementsByTagName('p')[0]
div2.appendChild(p1)
// 獲取父元素
console.log(p1.parentNode)
// 獲取子元素串列
const div1ChildNodes = div1.childNodes
console.log('div1ChildNodes', div1ChildNodes)
const div1ChildNodesP = Array.from(div1ChildNodes).filter(child => {
if(child.nodeType === 1) {
return true;
}
return false;
}
console.log('div1ChildNodesP', div1ChildNodesP)
// 洗掉節點
div1.removeChild(div1ChildNodesP[0])
9.5 優化DOM性能
9.5.1 對DOM查詢進行快取
不使用快取DOM查詢結果
for(let i = 0; i < document.getELementsByTagName('p').length; i++){
// 每次回圈都會計算length,頻繁進行DOM查詢
}
快取DOM查詢結果
const pList = document.getELementsByTagName('p')
const length = pList.length
for(let i = 0; i < length; i++){
// 快取length, 只進行一次DOM查詢
}
9.5.2 將頻繁操作改為一次性操作
const listNode = document.getElementById('list')
// 創建一個檔案片段,此時還沒有插入到DOM樹中
const frag = document.createDocumentFragment()
// 執行插入操作
for(let i = 0; i < 10; i++){
const li = document.createElement('li')
li.innerHTML = "Iist Item " + i
frag.appendChild(li)
}
// 都完成后,再插入DOM樹中
listNode.appendChild(frag)
10. BOM
- navigator
- screen
- location
- history
【BOM】JavaScript-定時器-執行機制-location-navigator-history
10.1 檢查瀏覽器型別
const ua = navigator.userAgent
const isChorme = ua.indexOf('Chrome')
console.log(isChorme)
10.2 拆解URL各個部分
location

11. 事件
【DOM】JavaScript-事件高級-注冊事件-事件流-事件物件-事件冒泡-委派-滑鼠鍵盤事件
11.1 事件系結、冒泡、代理
const btn = document.getElementById('btn1')
btn.addEventListener('click', event => {
console.log('clicked')
})
11.2 寫一個通用的事件系結函式
function bindEvent(elem, type, fn){
elem.addEventListener(type, fn)
}
const btn1 = document.getELementById('btn1')
bindEvent(btn1, 'click', event => {
console.log(event.target) // 獲取觸發的元素
event.preventDefault() // 阻止默認行為
alert('clicked')
})
升級版【支持代理】
function bindEvent(elem, type, selector, fn){
if(fn == null){
fn = selector
selector = null
}
elem.addEventListener(type, event => {
if(selector){
// 代理系結
if (target.matches(selector)){
fn.call(target, event)
}
}else {
// 普通系結
fn.call(target, event)
}
})
}
// 普通系結
const btn1 = document.getELementById('btn1')
bindEvent(btn1, 'click', function(event) {
console.log(event.target) // 獲取觸發的元素
event.preventDefault() // 阻止默認行為
alert(this.innerHTML)
})
// 代理系結 [在a父節點div上系結事件]
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function(event) {
event.preventDefault()
alert(this.innerHTML)
})
11.3 描述事件冒泡程序
- 基于DOM樹形結構
- 事件會順著觸發元素往上冒泡
- 應用場景:事件代理
11.4 無限下拉的圖片串列,如何監聽每個圖片的點擊
- 事件代理
- 用
event.target獲取觸發元素 - 用
matches來判斷是否是觸發元素
12. AJAX
【Ajax】HTTP相關問題-GET-POST-XHR使用-jQuery中的ajax-跨域-同源-jsonp-cors
【axios】使用json-server 搭建REST API - 使用axios - 自定義axios - 取消請求 - 攔截器
12.1 手寫一個簡單的ajax
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
resolve(
JSON.parse(xhr.responseText)
)
} else if (xhr.status === 404) {
reject(new Error('404 not found'))
}
}
}
xhr.send(null)
})
return p
}
// 使用
const url = '/data/test.json'
ajax(url)
.then(res => console.log(res))
.catch(err => console.log(err))
12.2 跨域解決方案
瀏覽器中加載圖片、css、js可以無視同源策略
<img />可用于統計打點,可使用第三方服務<link /><script>可以使用CDN<script>可以實作JSONP
所有跨域,都必須經過server端允許和配合
未經server允許就實作跨域,說明瀏覽器有漏洞
- JSONP
- CORS
- 代理
【Ajax】HTTP相關問題-GET-POST-XHR使用-jQuery中的ajax-跨域-同源-jsonp-cors
13. 瀏覽器存盤
【瀏覽器】瀏覽器存盤&快取 - Cookie - localStorage - sessionStorage - IndexDB - Cache Storage - Application Cache
cookie
- 本身用于瀏覽器和服務器通訊
- 存盤大小最大4KB
- http請求時需要發送到服務端,增加請求資料量
- 是能用
document.cookie='...'來修改,太過簡陋
localStorage 和 sessionStorage
- HTML5專門為存盤設計,最大可存5M
- API簡單易用
setItemgetItem - 不會隨著HTTP請求被發出去
14. 頁面加載
14.1 資源的形式
- HTML代碼
- 媒體檔案,如圖片、視頻
- JavaScript代碼 css代碼
14.2 從輸入url到渲染出頁面的整個程序
① 獲取資源
DNS域名決議:域名——> IP地址
TCP三次握手建立連接
瀏覽器根據IP地址向服務器發起HTTP請求
服務器處理HTTP請求,并資源回傳給瀏覽器
② 渲染頁面
瀏覽器根據HTML代碼生成DOM Tree
根據CSS代碼生成 CSSOM
將DOM Tree 和 CSSOM 整合成 渲染樹 Render Tree
根據Render Tree 渲染頁面
遇到 scrpit 標簽 則暫停渲染,優先加載并執行JS代碼,完成再繼續
直至把Render Tree 渲染完成
14.3 window.onload與DOMContentLoaded的區別
window.onload 資源全部加載完成才能執行,包括圖片
DOMContentLoaded DOM渲染完成即可,圖片可能尚未下載
const img1 = document.getElementById('img1')
img1.onload = function() {
console.log('img loaded') // 2
}
window.addEventListener('load', function() {
console.log('window loaded') // 3
})
document.addEventListener('DOMContentLoaded', function() {
console.log('dom content loaded') // 1
})
15. 性能優化
15.1 前端常見性能優化方案
- 思路
- 多使用記憶體、快取或其他方法
- 減少CPU計算量,減少網路加載耗時
- 空間換時間
- 加載優化
- 減少資源體積:壓縮代碼
- 減少訪問次數:合并代碼,SSR服務端渲染、快取
- 使用更快的網路:CDN
- 渲染優化
- CSS放在head,JS放在body最下面
- 盡早開始執行JS,用DOMContentLoaded觸發
- 懶加載
- 對DOM查詢進行快取
- 頻繁DOM操作,合并到一起插入DOM結構
- 節流與防抖
15.2 快取
靜態資源加hash后綴,根據檔案內容計算hash
檔案內容不變,則hash不變,則url不變
url和檔案不變,就會自動觸發HTTP快取機制,回傳304
網路方面的快取分為三塊:DNS快取、HTTP快取、CDN快取, HTTP 快取也稱為瀏覽器快取
(建議收藏)為什么第二次打開頁面快?五步吃透前端快取,讓頁面飛起
15.3 SSR服務渲染
將網頁和資料一起加載,一起渲染
非SSR(前后端分離):先加載網頁,再加載資料(ajax),再渲染資料
15.4 圖片懶加載
先給一個小的預覽圖,判斷到用戶訪問到當前位置,再加載高清
<img id="img1" src="preview.png" data-realsrc="real.png" />
<script>
let img1 = document.getElementById('img1')
img1.src = img1.getAttribute('data-realsrc')
<script>
15.5 節流與防抖
【JS】函式節流與函式防抖-自定義JS工具類
16. 前端安全
16.1 XSS 跨站請求攻擊
前端安全系列(一):如何防止XSS攻擊?
16.2 XSRF 跨站請求偽造
前端安全系列之二:如何防止CSRF攻擊?
17. 演算法刷題
【演算法】經典排序演算法總結-JavaScript描述-圖解-復雜度分析
【LeetCode】經典題分類(數學 - 陣列 - 字串)精選 - JavaScript - ES6 - 技巧總結
【LeetCode】經典題分類(鏈表 )精選 - JavaScript - ES6 - 技巧總結
【LeetCode】經典題分類(樹&圖 )精選 - JavaScript - ES6 - 技巧總結
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/293188.html
標籤:其他
