一文幫你搞定 90- 的 JS 手寫題
還在害怕手寫題嗎,本文可以幫你擴展並鞏固自己的 JS 基礎,順便搞定 90% 的手寫題。在工作中還可以對常用的需求進行手寫實現,比如深拷貝、防抖節流等可以直接用於往後的項目中,提高項目開發效率。不說廢話了,下面就直接上代碼吧。
1.call 的實現
-
第一個參數爲 null 或者 undefined 時,this 指向全局對象 window,值爲原始值的指向該原始值的自動包裝對象,如 String、Number、Boolean
-
爲了避免函數名與上下文 (context) 的屬性發生衝突,使用 Symbol 類型作爲唯一值
-
將函數作爲傳入的上下文 (context) 屬性執行
-
函數執行完成後刪除該屬性
-
返回執行結果
Function.prototype.myCall = function(context,...args){
let cxt = context || window;
//將當前被調用的方法定義在cxt.func上.(爲了能以對象調用形式綁定this)
//新建一個唯一的Symbol變量避免重複
let func = Symbol()
cxt[func] = this;
args = args ? args : []
//以對象調用形式調用func,此時this指向cxt 也就是傳入的需要綁定的this指向
const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
//刪除該方法,不然會對傳入對象造成污染(添加該方法)
delete cxt[func];
return res;
}
2.apply 的實現
-
前部分與 call 一樣
-
第二個參數可以不傳,但類型必須爲數組或者類數組
Function.prototype.myApply = function(context,args = []){
let cxt = context || window;
//將當前被調用的方法定義在cxt.func上.(爲了能以對象調用形式綁定this)
//新建一個唯一的Symbol變量避免重複
let func = Symbol()
cxt[func] = this;
//以對象調用形式調用func,此時this指向cxt 也就是傳入的需要綁定的this指向
const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
delete cxt[func];
return res;
}
3.bind 的實現
需要考慮:
-
bind() 除了 this 外,還可傳入多個參數;
-
bind 創建的新函數可能傳入多個參數;
-
新函數可能被當做構造函數調用;
-
函數可能有返回值;
實現方法:
-
bind 方法不會立即執行,需要返回一個待執行的函數;(閉包)
-
實現作用域綁定(apply)
-
參數傳遞(apply 的數組傳參)
-
當作爲構造函數的時候,進行原型繼承
Function.prototype.myBind = function (context, ...args) {
//新建一個變量賦值爲this,表示當前函數
const fn = this
//判斷有沒有傳參進來,若爲空則賦值[]
args = args ? args : []
//返回一個newFn函數,在裏面調用fn
return function newFn(...newFnArgs) {
if (this instanceof newFn) {
return new fn(...args, ...newFnArgs)
}
return fn.apply(context, [...args,...newFnArgs])
}
}
- 測試
let name = '小王',age =17;
let obj = {
name:'小張',
age: this.age,
myFun: function(from,to){
console.log(this.name + ' 年齡 ' + this.age+'來自 '+from+'去往'+ to)
}
}
let db = {
name: '德瑪',
age: 99
}
//結果
obj.myFun.myCall(db,'成都','上海'); // 德瑪 年齡 99 來自 成都去往上海
obj.myFun.myApply(db,['成都','上海']); // 德瑪 年齡 99 來自 成都去往上海
obj.myFun.myBind(db,'成都','上海')(); // 德瑪 年齡 99 來自 成都去往上海
obj.myFun.myBind(db,['成都','上海'])(); // 德瑪 年齡 99 來自 成都, 上海去往 undefined
4. 寄生式組合繼承
function Person(obj) {
this.name = obj.name
this.age = obj.age
}
Person.prototype.add = function(value){
console.log(value)
}
var p1 = new Person({name:"番茄", age: 18})
function Person1(obj) {
Person.call(this, obj)
this.sex = obj.sex
}
// 這一步是繼承的關鍵
Person1.prototype = Object.create(Person.prototype);
Person1.prototype.constructor = Person1;
Person1.prototype.play = function(value){
console.log(value)
}
var p2 = new Person1({name:"雞蛋", age: 118, sex: "男"})
5.ES6 繼承
//class 相當於es5中構造函數
//class中定義方法時,前後不能加function,全部定義在class的protopyte屬性中
//class中定義的所有方法是不可枚舉的
//class中只能定義方法,不能定義對象,變量等
//class和方法內默認都是嚴格模式
//es5中constructor爲隱式屬性
class People{
constructor(name='wang',age='27'){
this.name = name;
this.age = age;
}
eat(){
console.log(`${this.name} ${this.age} eat food`)
}
}
//繼承父類
class Woman extends People{
constructor(name = 'ren',age = '27'){
//繼承父類屬性
super(name, age);
}
eat(){
//繼承父類方法
super.eat()
}
}
let wonmanObj=new Woman('xiaoxiami');
wonmanObj.eat();
//es5繼承先創建子類的實例對象,然後再將父類的方法添加到this上(Parent.apply(this))。
//es6繼承是使用關鍵字super先創建父類的實例對象this,最後在子類class中修改this。
6.new 的實現
-
一個繼承自 Foo.prototype 的新對象被創建。
-
使用指定的參數調用構造函數 Foo,並將 this 綁定到新創建的對象。new Foo 等同於 new Foo(),也就是沒有指定參數列表,Foo 不帶任何參數調用的情況。
-
由構造函數返回的對象就是 new 表達式的結果。如果構造函數沒有顯式返回一個對象,則使用步驟 1 創建的對象。
-
一般情況下,構造函數不返回值,但是用戶可以選擇主動返回對象,來覆蓋正常的對象創建步驟
function Ctor(){
....
}
function myNew(ctor,...args){
if(typeof ctor !== 'function'){
throw 'myNew function the first param must be a function';
}
var newObj = Object.create(ctor.prototype); //創建一個繼承自ctor.prototype的新對象
var ctorReturnResult = ctor.apply(newObj, args); //將構造函數ctor的this綁定到newObj中
var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
var isFunction = typeof ctorReturnResult === 'function';
if(isObject || isFunction){
return ctorReturnResult;
}
return newObj;
}
let c = myNew(Ctor);
7.instanceof 的實現
-
instanceof 是用來判斷 A 是否爲 B 的實例,表達式爲:A instanceof B,如果 A 是 B 的實例,則返回 true, 否則返回 false。
-
instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
-
不能檢測基本數據類型,在原型鏈上的結果未必準確,不能檢測 null,undefined
-
實現:遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果沒有找到,返回 false
function myInstanceOf(a,b){
let left = a.__proto__;
let right = b.prototype;
while(true){
if(left == null){
return false
}
if(left == right){
return true
}
left = left.__proto__
}
}
//instanceof 運算符用於判斷構造函數的 prototype 屬性是否出現在對象的原型鏈中的任何位置。
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left), // 獲取對象的原型
prototype = right.prototype; // 獲取構造函數的 prototype 對象
// 判斷構造函數的 prototype 對象是否在對象的原型鏈上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
8.Object.create() 的實現
-
MDN 文檔
-
Object.create() 會將參數對象作爲一個新創建的空對象的原型, 並返回這個空對象
//簡略版
function myCreate(obj){
// 新聲明一個函數
function C(){};
// 將函數的原型指向obj
C.prototype = obj;
// 返回這個函數的實力化對象
return new C()
}
//官方版Polyfill
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
}
9. 實現 Object.assign
Object.assign2 = function(target, ...source) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object')
}
let ret = Object(target)
source.forEach(function(obj) {
if (obj != null) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = obj[key]
}
}
}
})
return ret
}
10.Promise 的實現
實現 Promise 需要完全讀懂 Promise A+ 規範,不過從總體的實現上看,有如下幾個點需要考慮到:
-
Promise 本質是一個狀態機,且狀態只能爲以下三種:Pending(等待態)、Fulfilled(執行態)、Rejected(拒絕態),狀態的變更是單向的,只能從 Pending -> Fulfilled 或 Pending -> Rejected,狀態變更不可逆
-
then 需要支持鏈式調用
class Promise {
callbacks = [];
state = 'pending';//增加狀態
value = null;//保存結果
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
_handle(callback) {
if (this.state === 'pending') {
this.callbacks.push(callback);
return;
}
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {//如果then中沒有傳遞任何東西
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
let ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(ret);
}
_resolve(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';//改變狀態
this.value = value;//保存結果
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}
Promise.resolve
- Promsie.resolve(value) 可以將任何值轉成值爲 value 狀態是 fulfilled 的 Promise,但如果傳入的值本身是 Promise 則會原樣返回它。
Promise.resolve(value) {
if (value && value instanceof Promise) {
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => {
then(resolve);
});
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
}
Promise.reject
- 和 Promise.resolve() 類似,Promise.reject() 會實例化一個 rejected 狀態的 Promise。但與 Promise.resolve() 不同的是,如果給 Promise.reject() 傳遞一個 Promise 對象,則這個對象會成爲新 Promise 的值。
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason))
}
Promise.all
-
傳入的所有 Promsie 都是 fulfilled,則返回由他們的值組成的,狀態爲 fulfilled 的新 Promise;
-
只要有一個 Promise 是 rejected,則返回 rejected 狀態的新 Promsie,且它的值是第一個 rejected 的 Promise 的值;
-
只要有一個 Promise 是 pending,則返回一個 pending 狀態的新 Promise;
Promise.all = function(promiseArr) {
let index = 0, result = []
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(val => {
index++
result[i] = val
if (index === promiseArr.length) {
resolve(result)
}
}, err => {
reject(err)
})
})
})
}
Promise.race
- Promise.race 會返回一個由所有可迭代實例中第一個 fulfilled 或 rejected 的實例包裝後的新實例。
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(val => {
resolve(val)
}, err => {
rejecte(err)
})
})
})
}
11.Ajax 的實現
function ajax(url,method,body,headers){
return new Promise((resolve,reject)=>{
let req = new XMLHttpRequest();
req.open(methods,url);
for(let key in headers){
req.setRequestHeader(key,headers[key])
}
req.onreadystatechange(()=>{
if(req.readystate == 4){
if(req.status >= '200' && req.status <= 300){
resolve(req.responeText)
}else{
reject(req)
}
}
})
req.send(body)
})
}
12. 實現防抖函數(debounce)
- 連續觸發在最後一次執行方法,場景:輸入框匹配
let debounce = (fn,time = 1000) => {
let timeLock = null
return function (...args){
clearTimeout(timeLock)
timeLock = setTimeout(()=>{
fn(...args)
},time)
}
}
13. 實現節流函數(throttle)
- 在一定時間內只觸發一次,場景:長列表滾動節流
let throttle = (fn,time = 1000) => {
let flag = true;
return function (...args){
if(flag){
flag = false;
setTimeout(()=>{
flag = true;
fn(...args)
},time)
}
}
}
14. 深拷貝(deepclone)
-
判斷類型,正則和日期直接返回新對象
-
空或者非對象類型,直接返回原值
-
考慮循環引用,判斷如果 hash 中含有直接返回 hash 中的值
-
新建一個相應的 new obj.constructor 加入 hash
-
遍歷對象遞歸(普通 key 和 key 是 symbol 情況)
function deepClone(obj,hash = new WeakMap()){
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(obj === null || typeof obj !== 'object') return obj;
//循環引用的情況
if(hash.has(obj)){
return hash.get(obj)
}
//new 一個相應的對象
//obj爲Array,相當於new Array()
//obj爲Object,相當於new Object()
let constr = new obj.constructor();
hash.set(obj,constr);
for(let key in obj){
if(obj.hasOwnProperty(key)){
constr[key] = deepClone(obj[key],hash)
}
}
//考慮symbol的情況
let symbolObj = Object.getOwnPropertySymbols(obj)
for(let i=0;i<symbolObj.length;i++){
if(obj.hasOwnProperty(symbolObj[i])){
constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)
}
}
return constr
}
15. 數組扁平化的實現 (flat)
let arr = [1,2,[3,4,[5,[6]]]]
console.log(arr.flat(Infinity))//flat參數爲指定要提取嵌套數組的結構深度,默認值爲 1
//用reduce實現
function fn(arr){
return arr.reduce((prev,cur)=>{
return prev.concat(Array.isArray(cur)?fn(cur):cur)
},[])
}
16. 函數柯里化
function sumFn(a,b,c){return a+ b + c};
let sum = curry(sumFn);
sum(2)(3)(5)//10
sum(2,3)(5)//10
function curry(fn,...args){
let fnLen = fn.length,
argsLen = args.length;
//對比函數的參數和當前傳入參數
//若參數不夠就繼續遞歸返回curry
//若參數夠就調用函數返回相應的值
if(fnLen > argsLen){
return function(...arg2s){
return curry(fn,...args,...arg2s)
}
}else{
return fn(...args)
}
}
17. 使用閉包實現每隔一秒打印 1,2,3,4
for (var i=1; i<=5; i++) {
(function (i) {
setTimeout(() => console.log(i), 1000*i)
})(i)
}
18. 手寫一個 jsonp
const jsonp = function (url, data) {
return new Promise((resolve, reject) => {
// 初始化url
let dataString = url.indexOf('?') === -1 ? '?' : ''
let callbackName = `jsonpCB_${Date.now()}`
url += `${dataString}callback=${callbackName}`
if (data) {
// 有請求參數,依次添加到url
for (let k in data) {
url += `${k}=${data[k]}`
}
}
let jsNode = document.createElement('script')
jsNode.src = url
// 觸發callback,觸發後刪除js標籤和綁定在window上的callback
window[callbackName] = result => {
delete window[callbackName]
document.body.removeChild(jsNode)
if (result) {
resolve(result)
} else {
reject('沒有返回數據')
}
}
// js加載異常的情況
jsNode.addEventListener('error', () => {
delete window[callbackName]
document.body.removeChild(jsNode)
reject('JavaScript資源加載失敗')
}, false)
// 添加js節點到document上時,開始請求
document.body.appendChild(jsNode)
})
}
jsonp('http://192.168.0.103:8081/jsonp', {
a: 1,
b: 'heiheihei'
})
.then(result => {
console.log(result)
})
.catch(err => {
console.error(err)
})
19. 手寫一個觀察者模式
class Subject{
constructor(name){
this.name = name
this.observers = []
this.state = 'XXXX'
}
// 被觀察者要提供一個接受觀察者的方法
attach(observer){
this.observers.push(observer)
}
// 改變被觀察着的狀態
setState(newState){
this.state = newState
this.observers.forEach(o=>{
o.update(newState)
})
}
}
class Observer{
constructor(name){
this.name = name
}
update(newState){
console.log(`${this.name}say:${newState}`)
}
}
// 被觀察者 燈
let sub = new Subject('燈')
let mm = new Observer('小明')
let jj = new Observer('小健')
// 訂閱 觀察者
sub.attach(mm)
sub.attach(jj)
sub.setState('燈亮了來電了')
20.EventEmitter 實現
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
let callbacks = this.events[event] || [];
callbacks.push(callback);
this.events[event] = callbacks;
return this;
}
off(event, callback) {
let callbacks = this.events[event];
this.events[event] = callbacks && callbacks.filter(fn => fn !== callback);
return this;
}
emit(event, ...args) {
let callbacks = this.events[event];
callbacks.forEach(fn => {
fn(...args);
});
return this;
}
once(event, callback) {
let wrapFun = function (...args) {
callback(...args);
this.off(event, wrapFun);
};
this.on(event, wrapFun);
return this;
}
}
21. 生成隨機數的各種方法?
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
22. 如何實現數組的隨機排序?
let arr = [2,3,454,34,324,32]
arr.sort(randomSort)
function randomSort(a, b) {
return Math.random() > 0.5 ? -1 : 1;
}
23. 寫一個通用的事件偵聽器函數。
const EventUtils = {
// 視能力分別使用dom0||dom2||IE方式 來綁定事件
// 添加事件
addEvent: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
// 移除事件
removeEvent: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
// 獲取事件目標
getTarget: function(event) {
return event.target || event.srcElement;
},
// 獲取 event 對象的引用,取到事件的所有信息,確保隨時能使用 event
getEvent: function(event) {
return event || window.event;
},
// 阻止事件(主要是事件冒泡,因爲 IE 不支持事件捕獲)
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
},
// 取消事件的默認行爲
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
};
24. 使用迭代的方式實現 flatten 函數。
var arr = [1, 2, 3, [4, 5], [6, [7, [8]]]]
/** * 使用遞歸的方式處理 * wrap 內保
存結果 ret * 返回一個遞歸函數 **/
function wrap() {
var ret = [];
return function flat(a) {
for (var item of
a) {
if (item.constructor === Array) {
ret.concat(flat(item))
} else {
ret.push(item)
}
}
return ret
}
}
console.log(wrap()(arr));
25. 怎麼實現一個 sleep
- sleep 函數作用是讓線程休眠,等到指定時間在重新喚起。
function sleep(delay) {
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
function test() {
console.log('111');
sleep(2000);
console.log('222');
}
test()
26. 實現正則切分千分位(10000 => 10,000)
//無小數點
let num1 = '1321434322222'
num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,')
//有小數點
let num2 = '342243242322.3432423'
num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,')
27. 對象數組去重
輸入:
[{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
輸出:
[{a:1,b:2,c:3},{d:2,c:2}]
-
首先寫一個函數把對象中的 key 排序,然後再轉成字符串
-
遍歷數組利用 Set 將轉爲字符串後的對象去重
function objSort(obj){
let newObj = {}
//遍歷對象,並將key進行排序
Object.keys(obj).sort().map(key => {
newObj[key] = obj[key]
})
//將排序好的數組轉成字符串
return JSON.stringify(newObj)
}
function unique(arr){
let set = new Set();
for(let i=0;i<arr.length;i++){
let str = objSort(arr[i])
set.add(str)
}
//將數組中的字符串轉回對象
arr = [...set].map(item => {
return JSON.parse(item)
})
return arr
}
28. 解析 URL Params 爲對象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 結果
{ user: 'anonymous',
id: [ 123, 456 ], // 重複出現的 key 要組裝成數組,能被轉成數字的就轉成數字類型
city: '北京', // 中文需解碼
enabled: true, // 未指定值得 key 約定爲 true
}
*/
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 將 ? 後面的字符串取出來
const paramsArr = paramsStr.split('&'); // 將字符串以 & 分割後存到數組中
let paramsObj = {};
// 將 params 存到對象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 處理有 value 的參數
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解碼
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判斷是否轉爲數字
if (paramsObj.hasOwnProperty(key)) { // 如果對象有 key,則添加一個值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果對象沒有這個 key,創建 key 並設置值
paramsObj[key] = val;
}
} else { // 處理沒有 value 的參數
paramsObj[param] = true;
}
})
return paramsObj;
}
29. 模板引擎實現
let template = '我是{{name}},年齡{{age}},性別{{sex}}';
let data = {
name: '姓名',
age: 18
}
render(template, data); // 我是姓名,年齡18,性別undefined
function render(template, data) {
const reg = /\{\{(\w+)\}\}/; // 模板字符串正則
if (reg.test(template)) { // 判斷模板裏是否有模板字符串
const name = reg.exec(template)[1]; // 查找當前模板裏第一個模板字符串的字段
template = template.replace(reg, data[name]); // 將第一個模板字符串渲染
return render(template, data); // 遞歸的渲染並返回渲染後的結構
}
return template; // 如果模板沒有模板字符串直接返回
}
30. 轉化爲駝峯命名
var s1 = "get-element-by-id"
// 轉化爲 getElementById
var f = function(s) {
return s.replace(/-\w/g, function(x) {
return x.slice(1).toUpperCase();
})
}
31. 查找字符串中出現最多的字符和個數
- 例: abbcccddddd -> 字符最多的是 d,出現了 5 次
let str = "abcabcabcbbccccc";
let num = 0;
let char = '';
// 使其按照一定的次序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"
// 定義正則表達式
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => {
if(num < $0.length){
num = $0.length;
char = $1;
}
});
console.log(`字符最多的是${char},出現了${num}次`);
32. 圖片懶加載
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)
參考資料
-
高頻 JavaScript 手寫
-
初、中級前端應該要掌握的手寫代碼實現
-
22 道高頻 JavaScript 手寫
-
死磕 36 個 JS 手寫題(搞懂後,提升真的大)
作者:xpsilvester
https://juejin.cn/post/6963167124881670152
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/gpZmJ2ZljlW83Pb-TCnm3A