個人自學前端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)
})()