1. 程式人生 > 其它 >個人自學前端15-JS8

個人自學前端15-JS8

目錄

this的指向和非同步

一 非同步操作

js 的程式碼執行,分為兩個佇列。一個是同步佇列,另一個是非同步佇列。

同步操作:程式碼逐行執行,前面的操作完成了才可以進行後面的操作。(程式碼阻塞).

非同步操作:非同步程式碼執行不會阻塞程式碼,必定落後於同步操作

js 中有很多非同步操作,例如定時器。

// 定時器的回撥函式執行是非同步操作,必定落後於同步操作。
// 因此先列印200,再列印100
setTimeout(()=>{
	console.log(100)
},0)
console.log(200);

現實生活中:

睡覺是同步操作,因為沒睡醒你無法做其他任何事情。

等公車是非同步操作,因為在等公車的過程中,你還可以看手機。

js 中只要是需要等待一段時間的操作,基本都是非同步操作。

例如:定時器,ajax請求資料,FileReader讀取檔案等操作,都需要花一小段時間,他們都是非同步操作。

為何非同步操作要落後於同步操作?(為何非同步操作的同時還可以進行同步操作?)

如果非同步操作不落後於同步操作,則在這個等待的時間段內,頁面會出現假死狀態,影響使用者體驗。

ajax預設就是非同步請求資料,請求資料需要消耗一段時間,這個時間有可能長有可能短。

如果ajax請求是同步的,則會導致在等待的時間中頁面出現假死狀態,無法做任何操作。這明顯體驗很不好。

因此ajax在請求資料的過程中,其實還可以進行其他操作。

資料請求成功之後,會自動觸發onreadystatechange事件中的邏輯渲染頁面。

js 是單執行緒的,非同步操作有點類似於多執行緒.但是還是有本質的區別.

二 this的指向

1.1 this的作用

this的作用就是找到呼叫函式的物件。

通過這個this,還可以訪問該物件身上的其他屬性。減少了很多傳值的操作。

1.2 如何確定this的指向

記住一句話:誰呼叫它,它就指向誰。

這句話的完整版應該是:哪個物件呼叫this所在的函式,this就指向這個物件。

1.3 如何呼叫一個函式?

呼叫函式有好幾種形式。

// 形式1.
show();

// 形式2.
obj.show();

// 形式3.
show.call(obj);

// 形式4.
show.apply(obj)

// 形式5.
new show();

1.4 如何知道函式是被哪個物件呼叫的?

在面向物件的程式語言裡,函式都應該屬於某個物件,是某個物件的方法。

例如,alert就是window物件的一個方法。parseInt也是window的一個方法。

正常情況下,只有函式的所有者可以呼叫函式。

例如,只有window可以呼叫alert。Math物件調用不了alert方法。因為alert不屬於Math。

不同的呼叫形式,呼叫函式的物件是不同的。

// 形式1. 非嚴格模式,show會變成window的方法,這種呼叫事件上省略了window.因此show是被window呼叫的
// 如果是嚴格模式,因為沒有顯式的把show變成window的方法,因此show不屬於window,也就是沒有物件呼叫show
show();

// 形式2.show是通過obj來呼叫的。
obj.show();

// 形式3.show是通過obj呼叫的
show.call(obj);

// 形式4.show是通過obj呼叫的
show.apply(obj);

// 形式5.通過new 操作符來修飾。這裡show沒有物件在呼叫。
new show();

確定了哪個物件在呼叫show函式,則show函式內的this就指向這個物件。

1.5 正常情況下的this指向分析步驟:

1:先確定this寫在哪個函式宣告內。

2:確定這個函式呼叫在哪裡。

3:函式前面的物件是哪個,this就指向這個物件。如果沒有物件,this指向window或者undefined

1.6 call和apply

理論上,函式是哪個物件的方法,就只能由哪個物件來呼叫。方法內的this只能指向它的所有者。

但是,有些時候,一個物件沒有對應的方法,可以去跟另一個物件臨時 "借用"一次.

call和apply可以臨時呼叫不屬於自己的方法一次.


let oYm = {
	name:'冪冪'
	fn(){console.log(this.name)}
};
let oCy = {name:'超越'};

// 理論上,oCy沒有fn方法,是不能呼叫fn的.但是通過call和apply可以臨時呼叫一次.
oYm.fn.call(oCy);
// 這時,fn是被oCy物件呼叫的。因此fn內的this在這裡指向oCy。

call和apply的區別:傳參的方式不一樣。apply需要傳遞陣列作為引數。

function show(x,y){
	console.log(x,y)
}
// 正常呼叫
show(100,200);
// call呼叫
show.call(null,100,200);
// apply呼叫
show.apply(null,[100,200])

1.7 箭頭函式內的this

箭頭函式內的this:指向箭頭函式所在作用域內的this。(為了面向物件方便獲取this)

let fn = null;
let oYm = {
	name:'冪冪',
	show(){
		console.log(this.name);
		fn = ()=>{
			console.log(this.name);
		}
	}
};
// show被oYm呼叫,show內的this指向oYm。
oYm.show(); // 冪冪
let oCy = {name:'超越',fn};
// fn被oCy呼叫。但是由於fn是一個箭頭函式,因此fn內的this指向show內的this,也就是oYm。
oCy.fn(); // 冪冪

1.8 bind內的this。

bind和call和apply類似,可以改變函式內的this指向。bind可以讓某個函式內的this永遠指向某個物件。

但是,bind不會馬上呼叫函式,call和apply會

另外:call和apply返回的值和源函式相同,bind返回的是一個和源函式一模一樣的新函式

function show(){
	return 100
}
// 利用call或者apply呼叫函式show。num得到的值都是100.
// 這種情況下,show都會被立即呼叫執行.
let num  = show.call(null);
let num  = show.apply(null);
// 這裡,show不會被立馬呼叫.num是一個函式,長得跟show一模一樣.
let num = show.bind(null);
console.log(num); // function(){return 100}
console.log(num == show); // false

call和apply呼叫函式後,函式內的this指向call和apply的第一個引數。

注意:bind返回的函式在呼叫時,無論以何種方式呼叫,這個函式內的this永遠指向bind的第一個引數。

1.9 總結

this指向,分普通情況和特殊情況

普通情況的分析步驟:

1:先確定this寫在哪個函式宣告內。

2:確定這個函式呼叫在哪裡。

3:函式前面的物件是哪個,this就指向這個物件。如果沒有物件,this指向window或者undefined

特殊情況的分析步驟:

1:有call和apply,this指向call和apply的第一個引數。

2:有箭頭函式,this指向箭頭函式所在作用域內的this。

3:有bind,bind返回的函式的this永遠指向bind的第一個引數。

4:有new 的情況。this指向例項。(後面學)

二 事件內的this

標籤事件內的this,預設都指向觸發事件的標籤本身。任何事件都適用。

oBtn.onclick = function(){console.log(this)}; // oBtn
oBtn.onmouseover = function(){console.log(this)}; // oBtn
oBtn.onmouseout = function(){console.log(this)}; // oBtn
oBtn.onchange = function(){console.log(this)}; // oBtn

這個匿名函式是如何被oBtn呼叫的?這個匿名函式是oBtn的方法嗎?

實際上事件發生時,系統會自動呼叫這個匿名函式。系統是通過以下方式呼叫這個匿名函式的。

// 通過分析下面的呼叫方式,可以確定,匿名函式內的this就是這些方法呼叫時前面的物件:oBtn
oBtn.click();
oBtn.mouseover();
oBtn.mouseout();
oBtn.change();

三 自執行函式

自執行函式:宣告和呼叫寫在一起的特殊函式。

自執行函式的作用:方便建立一個區域性作用域。(外掛中都使用自執行函式來建立一個區域性作用域)

語法:

// 自執行函式形式1
(function (){
	console.log(100)
})();

// 自執行函式形式2
(function (){
	console.log(100)
}());

// 自執行函式傳參
(function (x){
	console.log(x)
})(100);

自執行函式本質上是一個函式呼叫表示式.

// num的值就是200
let num = (function(){
	return 200;
})()

自執行函式內的this指向window或者undefined.

// 非嚴格模式下,this指向window。嚴格模式下,this指向undefined。
(function(){
	console.log(this)
})()