最近在復習面試中常見的JS手寫函數,順便進行代碼注釋和總結,方便自己回顧也加深記,內容也會陸陸續續進行補充和改善。
<script> const obj1 = { name: 'Leise', age: 23, address: { country: 'China', city: 'Guanzhou' } } const obj2 = deepClone(obj1) obj2.address.city = 'Hangzhou' console.log(obj1.address.city); console.log(obj2.address.city); function deepClone(obj) { //判斷是否為非對象或者是null if (typeof obj !== 'object' || typeof pbj == null) { return obj } // 初始化拷貝結果 let result // 判斷是數組還是對象 if (obj instanceof Array) { result = [] } else { result = {} } for (let key in obj) { //保證key不是原型的屬性 if (obj.hasOwnProperty(key)) { // 遞歸調用深拷貝 防止多重嵌套 result[key] = deepClone(obj[key]) } } // 最后返回結果 return result } </script>
二、手寫bind函數
<script> Function.prototype.mybind = function () { //原型添加方法mybind const args = Array.prototype.slice.call(arguments) //將獲得參數拆解成數組 const t = args.shift() //獲取this(數組第一項,同時剔除第一項,shift可以實現) const self = this //當前fn1.bind(...)中的fn1 //console.log(this); //function fn1(a, b, c) { //console.log("this", this); //定義一個變量保留this指向 //console.log(a, b, c); } return function () { //console.log(this);window return self.apply(t, args) //這個函數需要執行 } } function fn1(a, b, c) { console.log("this", this); console.log(a, b, c); } const fn2 = fn1.mybind({ x: 1, y: 2 }, 10, 20, ) //bind之后需要執行 const res = fn2() //保存運行后的結果並打印出來 console.log(res); </script>
三、手寫通用事件綁定函數
<script>
function bindEvent(elem, type, selcetor, fn) {
//判斷三個還是四個,四個則是事件代理
if (fn == null) {
fn = selcetor
selcetor = null
}
//開始綁定事件
elem.addEventListener(type, event => {
const target = event.target
//判斷是事件代理還是普通綁定
if (selector) {
//事件代理
if (target.matches(selcetor)) {
fn.call(target, event)
} else {
fn.call(target.event)
}
}
})
}
function bindEvent(elem, type, selector, fn) {
// 三個參數和四個參數的判斷處理
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, event => {
const target = event.target
if (selector) {
// 有selector就是代理
if (target.matches(selector)) {
fn.call(target, event)
}
} else {
fn.call(target, event)
}
})
}
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a',
function (event) {
event.preventDefault()
alert(this.innerHTML)
})
</script>
四、手寫閉包的簡單應用
<script>
function createCache() {
// 閉包隱藏數據,只提供 API
let obj = {}
return {
set: function (key, value) {
obj[key] = value
},
get: function (key) {
return obj[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
obj.d = 100 //undefined,必須使用get或者set
</script>
五、手寫promise
<script>
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.src = url
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error("圖片加載失敗"))
}
})
}
const url1 = 'http://img.mukewang.com/5e5c85e1000116c505400720-156-88.jpg'
const url2 = 'https://img.mukewang.com/szimg/5dba8cee0969880506000338.jpg'
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);
})
</script>
六、手寫Ajax
<script>
function ajax(url) {
return new Promise((resolve, reject) => {
//判斷谷歌等還是IE
let xhr
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest()
} else { // code for IE6, IE5
xhr = new ActiveXObject("Microsoft.XMLHTTP")
}
// xhr 具有一個 open 方法,這個方法的作用類似於初始化,並不會發起真正的請求
// open 方法具有 5 個參數,但是常用的是前 3 個
// method: 請求方式 —— get / post
// url:請求的地址
// async:是否異步請求,默認為 true(異步)
xhr.open("get", url, true)
// send 方法發送請求,並接受一個可選參數
// 當請求方式為 post 時,可以將請求體的參數傳入
// 當請求方式為 get 時,可以不傳或傳入 null
// 不管是 get 還是 post,參數都需要通過 encodeURIComponent 編碼后拼接
xhr.send(null)
//當readyStatus的狀態發生改變時,會觸發 xhr 的事件onreadystatechange
xhr.onreadystatechange = () => {
//readyStatus: 請求/響應過程的當前活動階段
if (xhr.readyState == 4) {
//HTTP 狀態在 200-300 之間表示請求成功
// HTTP 狀態為 304 表示請求內容未發生改變,可直接從緩存中讀取
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
let obj = JSON.parse(xhr.responseText)
// console.log(obj)
// callBack(obj)
resolve(obj)
} else {
reject(new Error("錯誤"))
}
}
}
})
}
const url1 = "http://jsonplaceholder.typicode.com/users"
const url2 = "/a.json"
const url3 = "/b.json"
ajax(url1).then(res => {
console.log(res);
return ajax(url2)
}).then(res => {
console.log(res);
return ajax(url3)
}).then(() => {
console.log("ok");
}).catch((res) => {
console.log(res) //設置一個統一出口 如果出現錯誤就終止
})
</script>
七、手寫防抖
<script> const input1 = document.getElementById('input1') function debounce(fn, delay) { //timer必須在閉包中 才不會被外面修改 let timer = null return function () { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay); } } input1.addEventListener('keyup', debounce(function () { console.log(input1.value); }, 500)) </script>
八、手寫節流
<script>
function throttle(fn, delay) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null++
}, delay);
}
}
const one = document.getElementById('one')
one.addEventListener('drag', throttle(function (e) {
console.log(e.offsetX, e.offsetY);
}, 1000))
</script>
