偶遇this之坑


前言

在寫一個懶加載插件時,遇到一個坑,就是this的指向問題,我想這種情況大部分人都會遇到,就寫下來,新手也有個參考。

事件

有些頁面圖片比較多,但用戶還不一定會全看,這樣的話,全部去加載這些圖片,就有點浪費資源了,於是想寫一個通用的插件來解決這個問題

想法

根據滾動條高度加屏幕高度來判斷是否加載這張圖片,需要懶加載的圖片這樣寫

<img data-src="image/bg.jpg">

凡是加上這個屬性的都會做懶加載處理。

this之坑

以下是最終的代碼

function SetImg(top){
	var imgs = Array.prototype.slice.apply(document.getElementsByTagName("img"));
	this.imgs = imgs.filter(function(item,index){
		return item.dataset.src;
	});
	this.top = top || 150;
}
SetImg.prototype = {
	init:function(){
		this.event();
	},
	setSrc:function(){
		if(this.imgs.length===0){
			window.removeEventListener("scroll",this.setSrc);
		};
		var _this = this;
		this.imgs.forEach(function(item,index){
			if(document.documentElement.clientHeight+document.body.scrollTop+_this.top>item.offsetTop||item.offsetTop<document.documentElement.clientHeight){
				item.src = item.dataset.src;
				_this.imgs.splice(index,1);
			}
		})
	},
	event:function(){
		this.setSrc = this.setSrc.bind(this);
		window.addEventListener("load",this.setSrc);
		window.addEventListener("scroll",this.setSrc);
	}
};
new SetImg().init();

這個代碼已經解決了this的指向問題,也就是下面這句

this.setSrc = this.setSrc.bind(this);

一開始是沒有這句話的,但問題是,我在setSrc里面使用了this,而恰恰那里面的this指向的是window,為什么指向window?因為這句話

window.addEventListener("load",this.setSrc);
window.addEventListener("scroll",this.setSrc);

我們說this始終指向一個對象,而現在給添加的事件,就是在window身上添加的,正如

element.addEventListener("click",fn);

這句不就是指向element嗎,那上面的那個指向window,也就不足為奇了。因此這也是為什么最后我添加這么一句

this.setSrc = this.setSrc.bind(this);

其實一開始我沒想要這么做,但這也是迫不得已的事,一開始我是這樣寫的:

window.addEventListener("load",this.setSrc.bind(this));
window.addEventListener("scroll",this.setSrc.bind(this));

但這樣的問題是,bind返回的是一個新對象,而不是原本的this.setSrc。一般情況下,這也不是什么大問題,但坑就坑在this.setSrc里面的這句

if(this.imgs.length===0){
    window.removeEventListener("scroll",this.setSrc);
};

看似一切都正常,但這是一個大坑,這里面的this.setSrc指向的是SetImg.setSrc,而

window.addEventListener("scroll",this.setSrc.bind(this));

this.setSrc.bind(this)這是一個新對象,因此你根本就無法remove掉這個新對象。所以最終才想出個迫不得已的方法就是讓this.setSrc變成新的那個對象。

可能有些朋友,不太懂為什么要寫這么一句:

if(this.imgs.length===0){
    window.removeEventListener("scroll",this.setSrc);
};

這句話的作用是,如果所有圖片都加載完畢了,這個滾動事件,就不需要了。當然如果你直接使用window.onscroll這種情勢來寫,或許這個問題可以很好的解決,但作為一個插件用addEventListener是迫不得已的,因為我不知道哪個頁面使用了onscroll事件,如果我直接那樣寫,就會把其他人寫的事件覆蓋掉。

結語

這種情況出現的概率還是蠻高的,導致這種問題的出現就是,事件里面的this和構造函數里面的this,指向的是不同的對象,所以啊這就是坑點。

說到正題,這個插件還不能用T_T,再改改吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM