1、淺拷貝、深拷貝的實作
淺拷貝
// 1. ...實作
let copy1 = {...{x:1}}
// 2. Object.assign實作
let copy2 = Object.assign({}, {x:1})
深拷貝
javascript深拷貝和淺拷貝以及實作方法(推薦)_紙飛機博客-CSDN博客_js淺拷貝和深拷貝的區別深拷貝和淺拷貝的區別?淺拷貝: 將原物件或原陣列的參考直接賦給新物件,新陣列,新物件/陣列只是原物件的一個參考,深拷貝: 創建一個新的物件和陣列,將原物件的各項屬性的“值”(陣列的所有元素)拷貝過來,是“值”而不是“參考”,為什么要用深拷貝?我們希望在改變新的陣列(物件)的時候,不改變原陣列(物件)一般是針對Array和Object型別資料的復制物件深拷貝方法1.JSON的方式實作function deepClone2(obj) { let _obj = JSON.sthttps://blog.csdn.net/qq_32442973/article/details/118584594
2、手寫防抖節流函式
javascript的防抖和節流深入理解_紙飛機博客-CSDN博客基本概念函式防抖(debounce):觸發高頻事件后n秒內函式只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間,函式節流(throttle):高頻事件觸發,但在n秒內只會執行一次,所以節流會稀釋函式的執行頻率,函式防抖(debounce)與 函式節流(throttle)都是為了限制函式的執行頻次,以優化函式觸發頻率過高導致的回應速度跟不上觸發頻率,出現延遲,假死或卡頓的現象,函式防抖(debounce)實作方式:每次觸發事件時設定一個延遲呼叫方法,并且取消之前的延時呼叫方法https://blog.csdn.net/qq_32442973/article/details/118739927
3、instanceof (考察對原型鏈的理解)
instanceof作用:判斷一個實體是否是其父類或者祖先型別的實體,
instanceof 在查找的程序中會遍歷左邊變數的原型鏈,直到找到右邊變數的 prototype查找失敗,回傳 false,
let myInstanceof = (target,origin) => {
while(target) {
if(target.__proto__===origin.prototype) {
return true
}
target = target.__proto__
}
return false
}
let a = [1,2,3]
console.log(myInstanceof(a,Array)); // true
console.log(myInstanceof(a,Object)); // true
4、實作陣列的map方法
Array.prototype.newMap = function(fn) {
var newArr = [];
for(var i = 0; i<this.length; i++){
newArr.push(fn(this[i],i,this))
}
return newArr;
}
5、實作 new 方法
function createNew() {
let obj = {} // 1.創建一個空物件
let constructor = [].shift.call(arguments)
// let [constructor,...args] = [...arguments]
obj.__proto__ = constructor.prototype // 2.鏈接到原型
let result = constructor.apply(obj, arguments) // 3.系結this值,為實體添加方法和屬性
// let result = constructor.apply(obj, args)
return typeof result === 'object' ? result : obj // 4.回傳新物件
}
function People(name,age) {
this.name = name
this.age = age
}
let peo = createNew(People,'Bob',22)
console.log(peo.name)
console.log(peo.age)
6、實作call&apply&bind
call
Function.prototype.myCall = function (context = window) {
// 函式的方法,所以寫在Fuction原型物件上
if (typeof this !== "function") {
// 這里if其實沒必要,會自動拋出錯誤
throw new Error("不是函式");
}
const obj = context || window; //這里可用ES6方法,為引數添加默認值,js嚴格模式全域作用域this為undefined
obj.fn = this; //this為呼叫的背景關系,this此處為函式,將這個函式作為obj的方法
const arg = [...arguments].slice(1); //第一個為obj所以洗掉,偽陣列轉為陣列
res = obj.fn(...arg);
delete obj.fn; // 不洗掉會導致context屬性越來越多
return res;
};
apply(arguments[this, [引數1,引數2.....] ])
Function.prototype.myApply = function (context) {
// 箭頭函式從不具有引數物件!!!!!這里不能寫成箭頭函式
let obj = context || window;
obj.fn = this;
const arg = arguments[1] || []; //若有引數,得到的是陣列
let res = obj.fn(...arg);
delete obj.fn;
return res;
};
function f(a, b) {
console.log(a, b);
console.log(this.name);
}
let obj = {
name: "張三",
};
f.myApply(obj, [1, 2]); //arguments[1]
bind
// 思路:類似call,但回傳的是函式
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let arg = [...arguments].slice(1)
return function F() {
// 處理函式使用new的情況
if (this instanceof F) {
return new _this(...arg, ...arguments)
} else {
return _this.apply(context, arg.concat(...arguments))
}
}
}
更多實作:bind方法的實作
7、手動實作promise
// Promise/A+ 規范規定的三種狀態
const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
// 建構式接收一個執行回呼
constructor(executor) {
this._status = STATUS.PENDING // Promise初始狀態
this._value = undefined // then回呼的值
this._resolveQueue = [] // resolve時觸發的成功佇列
this._rejectQueue = [] // reject時觸發的失敗佇列
// 使用箭頭函式固定this(resolve函式在executor中觸發,不然找不到this)
const resolve = value => {
const run = () => {
// Promise/A+ 規范規定的Promise狀態只能從pending觸發,變成fulfilled
if (this._status === STATUS.PENDING) {
this._status = STATUS.FULFILLED // 更改狀態
this._value = value // 儲存當前值,用于then回呼
// 執行resolve回呼
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift()
callback(value)
}
}
}
//把resolve執行回呼的操作封裝成一個函式,放進setTimeout里,以實作promise異步呼叫的特性(規范上是微任務,這里是宏任務)
setTimeout(run)
}
// 同 resolve
const reject = value => {
const run = () => {
if (this._status === STATUS.PENDING) {
this._status = STATUS.REJECTED
this._value = value
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift()
callback(value)
}
}
}
setTimeout(run)
}
// new Promise()時立即執行executor,并傳入resolve和reject
executor(resolve, reject)
}
// then方法,接收一個成功的回呼和一個失敗的回呼
function then(onFulfilled, onRejected) {
// 根據規范,如果then的引數不是function,則忽略它, 讓值繼續往下傳遞,鏈式呼叫繼續往下執行
typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
typeof onRejected !== 'function' ? onRejected = error => error : null
// then 回傳一個新的promise
return new MyPromise((resolve, reject) => {
const resolveFn = value => {
try {
const x = onFulfilled(value)
// 分類討論回傳值,如果是Promise,那么等待Promise狀態變更,否則直接resolve
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
}
}
const rejectFn = error => {
try {
const x = onRejected(error)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
switch (this._status) {
case STATUS.PENDING:
this._resolveQueue.push(resolveFn)
this._rejectQueue.push(rejectFn)
break;
case STATUS.FULFILLED:
resolveFn(this._value)
break;
case STATUS.REJECTED:
rejectFn(this._value)
break;
}
})
}
catch (rejectFn) {
return this.then(undefined, rejectFn)
}
// promise.finally方法
finally(callback) {
return this.then(value => MyPromise.resolve(callback()).then(() => value), error => {
MyPromise.resolve(callback()).then(() => error)
})
}
// 靜態resolve方法
static resolve(value) {
return value instanceof MyPromise ? value : new MyPromise(resolve => resolve(value))
}
// 靜態reject方法
static reject(error) {
return new MyPromise((resolve, reject) => reject(error))
}
// 靜態all方法
static all(promiseArr) {
let count = 0
let result = []
return new MyPromise((resolve, reject) => {
if (!promiseArr.length) {
return resolve(result)
}
promiseArr.forEach((p, i) => {
MyPromise.resolve(p).then(value => {
count++
result[i] = value
if (count === promiseArr.length) {
resolve(result)
}
}, error => {
reject(error)
})
})
})
}
// 靜態race方法
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach(p => {
MyPromise.resolve(p).then(value => {
resolve(value)
}, error => {
reject(error)
})
})
})
}
}
8、手寫原生AJAX
步驟
-
創建 XMLHttpRequest 實體
-
發出 HTTP 請求
-
服務器回傳 XML 格式的字串
-
JS 決議 XML,并更新區域頁面
不過隨著歷史行程的推進,XML 已經被淘汰,取而代之的是 JSON,
了解了屬性和方法之后,根據 AJAX 的步驟,手寫最簡單的 GET 請求,
myButton.addEventListener("click", function () {
ajax();
});
function ajax() {
let xhr = new XMLHttpRequest(); //實體化,以呼叫方法
xhr.open("get", "https://www.baidu.com"); //引數2,url,引數三:異步
xhr.onreadystatechange = () => {
//每當 readyState 屬性改變時,就會呼叫該函式,
if (xhr.readyState === 4) {
//XMLHttpRequest 代理當前所處狀態,
if (xhr.status >= 200 && xhr.status < 300) {
//200-300請求成功
let string = request.responseText;
//JSON.parse() 方法用來決議JSON字串,構造由字串描述的JavaScript值或物件
let object = JSON.parse(string);
}
}
};
request.send(); //用于實際發出 HTTP 請求,不帶引數為GET請求
}
基于promise實作:
function ajax(url) {
const p = new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("get", url);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject("請求出錯");
}
}
};
xhr.send(); //發送hppt請求
});
return p;
}
let url = "/data.json";
ajax(url)
.then((res) => console.log(res))
.catch((reason) => console.log(reason));
9、柯里化函式的實作
柯里化函式的定義:將多引數的函式轉換成單引數的形式,
柯里化函式實作的原理:利用閉包原理在執行可以形成一個不銷毀的作用域,然后把需要預先處理的內容都儲存在這個不銷毀的作用域中,并且回傳一個最少引數函式,
問法有很多,比較全面的可看該文:JS函式柯里化
10、實作一個雙向資料系結
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 資料劫持
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
console.log('獲取資料了')
},
set(newVal) {
console.log('資料更新了')
input.value = newVal
span.innerHTML = newVal
}
})
// 輸入監聽
input.addEventListener('keyup', function(e) {
obj.text = e.target.value
})
11、rem 基本設定
// 提前執行,初始化 resize 事件不會執行
setRem()
// 原始配置
function setRem () {
let doc = document.documentElement
let width = doc.getBoundingClientRect().width
let rem = width / 75
doc.style.fontSize = rem + 'px'
}
// 監聽視窗變化
addEventListener("resize", setRem)
12、手寫發布訂閱
發布訂閱模式的發布和訂閱都由一個調度中心來處理
發布訂閱模式是完全解耦的,因為調度中心中存的直接就是邏輯處理函式
要點:都要實作添加/洗掉/派發更新三個事件,
class Event {
// 首先定義一個事件容器,用來裝事件陣列(因為訂閱者可以是多個)
#handlers = {}
// 事件添加方法,引數有事件名和事件方法
addEventListener(type, handler) {
// 首先判斷handlers內有沒有type事件容器,沒有則創建一個新陣列容器
if (!(type in this.#handlers)) {
this.#handlers[type] = []
}
// 將事件存入
this.#handlers[type].push(handler)
}
// 觸發事件兩個引數(事件名,引數)
dispatchEvent(type, ...params) {
// 若沒有注冊該事件則拋出錯誤
if (!(type in this.#handlers)) {
return new Error('未注冊該事件')
}
// 便利觸發
this.#handlers[type].forEach(handler => {
handler(...params)
})
}
// 事件移除引數(事件名,洗掉的事件,若無第二個引數則洗掉該事件的訂閱和發布)
removeEventListener(type, handler) {
// 無效事件拋出
if (!(type in this.#handlers)) {
return new Error('無效事件')
}
if (!handler) {
// 直接移除事件
delete this.#handlers[type]
} else {
const idx = this.#handlers[type].findIndex(ele => ele === handler)
// 拋出例外事件
if (idx === -1) {
return new Error('無該系結事件')
}
// 移除事件
this.#handlers[type].splice(idx, 1)
if (this.#handlers[type].length === 0) {
delete this.#handlers[type]
}
}
}
}
13、陣列去重的實作
該題比較靈活,答題時可根據題目選取最便捷的寫法,
JS陣列去重的實作
14、實作陣列拍平
參考:
15、實作斐波那契數列
參考:js實作斐波那契數列的幾種方式
16、實作圖片懶加載
與普通的圖片懶加載不同,如下這個多做了 2 個精心處理:
- 圖片全部加載完成后移除事件監聽;
- 加載完的圖片,從 imgList 移除;
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length
const imgLazyLoad = function() {
let count = 0
return (function() {
let deleteIndexList = []
imgList.forEach((img, index) => {
let rect = img.getBoundingClientRect()
if (rect.top < window.innerHeight) {
img.src = img.dataset.src
deleteIndexList.push(index)
count++
if (count === length) {
document.removeEventListener('scroll', imgLazyLoad)
}
}
})
imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
})()
}
// 這里最好加上防抖處理
document.addEventListener('scroll', imgLazyLoad)
17、實作一個JSONP
JSONP 核心原理:script 標簽不受同源策略約束,所以可以用來進行跨域請求,優點是兼容性好,但是只能用于 GET 請求,
const jsonp = ({ url, params, callbackName }) => {
const generateUrl = () => {
let dataSrc = ''
for (let key in params) {
if (params.hasOwnProperty(key)) {
dataSrc += `${key}=${params[key]}&`
}
}
dataSrc += `callback=${callbackName}`
return `${url}?${dataSrc}`
}
return new Promise((resolve, reject) => {
const scriptEle = document.createElement('script')
scriptEle.src = generateUrl()
document.body.appendChild(scriptEle)
window[callbackName] = data => {
resolve(data)
document.removeChild(scriptEle)
}
})
}
推薦文章:
大前端基本知識點及面試重災區學習目錄_紙飛機博客-CSDN博客
2021前端面試js題目總結,不妨看看有沒有屬于你的那道題_紙飛機博客-CSDN博客
一起談一談js中的宏任務和微任務!_紙飛機博客-CSDN博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301324.html
標籤:其他
上一篇:深度學習筆記1:“安裝CUDA、Pytorch、除錯GPU環境“方法以及常見誤區
下一篇:蘋果這波是要偷家啊。。
